From 9cc563968066b55b067bcff132e4d566b020687d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 1 Nov 2010 17:01:27 -0400 Subject: usb: ohci-sh: Set IRQ as shared. The SH USB interface has both OHCI and EHCI modes that share the same interrupt. Flag the OHCI IRQ as shared in preparation for EHCI support. Signed-off-by: Paul Mundt --- drivers/usb/host/ohci-sh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-sh.c b/drivers/usb/host/ohci-sh.c index 0b35d22cc70e..f47867ff78c7 100644 --- a/drivers/usb/host/ohci-sh.c +++ b/drivers/usb/host/ohci-sh.c @@ -109,7 +109,7 @@ static int ohci_hcd_sh_probe(struct platform_device *pdev) hcd->regs = (void __iomem *)res->start; hcd->rsrc_start = res->start; hcd->rsrc_len = resource_size(res); - ret = usb_add_hcd(hcd, irq, IRQF_DISABLED); + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (ret != 0) { err("Failed to add hcd"); usb_put_hcd(hcd); -- cgit v1.2.3 From 63c845522263b2da08f70deba760ed0ab51e841d Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Mon, 1 Nov 2010 17:03:27 -0400 Subject: usb: ehci-hcd: Add support for SuperH EHCI. This adds a trivial stub for supporting EHCI mode of the on-chip SH USB host controllers. Signed-off-by: Paul Mundt --- drivers/usb/Kconfig | 1 + drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-sh.c | 232 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 238 insertions(+) create mode 100644 drivers/usb/host/ehci-sh.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 67eb3770868f..22a917302e35 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -66,6 +66,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_AT91SAM9G45 default y if ARCH_MXC default y if ARCH_OMAP3 + default y if CPU_SUBTYPE_SH7786 default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 502a7e6fef42..02ffbea0c8fa 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1166,6 +1166,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_mxc_driver #endif +#ifdef CONFIG_CPU_SUBTYPE_SH7786 +#include "ehci-sh.c" +#define PLATFORM_DRIVER ehci_hcd_sh_driver +#endif + #ifdef CONFIG_SOC_AU1200 #include "ehci-au1xxx.c" #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c new file mode 100644 index 000000000000..430b72e637fd --- /dev/null +++ b/drivers/usb/host/ehci-sh.c @@ -0,0 +1,232 @@ +/* + * SuperH EHCI host controller driver + * + * Copyright (C) 2010 Paul Mundt + * + * Based on ohci-sh.c and ehci-atmel.c. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include + +struct ehci_sh_priv { + struct clk *iclk, *fclk; + struct usb_hcd *hcd; +}; + +static int ehci_sh_reset(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int ret; + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + &ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + ret = ehci_halt(ehci); + if (unlikely(ret)) + return ret; + + ret = ehci_init(hcd); + if (unlikely(ret)) + return ret; + + ehci->sbrn = 0x20; + + ehci_reset(ehci); + ehci_port_power(ehci, 0); + + return ret; +} + +static const struct hc_driver ehci_sh_hc_driver = { + .description = hcd_name, + .product_desc = "SuperH EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ehci_sh_reset, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, +}; + +static int ehci_hcd_sh_probe(struct platform_device *pdev) +{ + const struct hc_driver *driver = &ehci_sh_hc_driver; + struct resource *res; + struct ehci_sh_priv *priv; + struct usb_hcd *hcd; + int irq, ret; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, + "Found HC with no register addr. Check %s setup!\n", + dev_name(&pdev->dev)); + ret = -ENODEV; + goto fail_create_hcd; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + dev_err(&pdev->dev, + "Found HC with no IRQ. Check %s setup!\n", + dev_name(&pdev->dev)); + ret = -ENODEV; + goto fail_create_hcd; + } + + /* initialize hcd */ + hcd = usb_create_hcd(&ehci_sh_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); + if (!hcd) { + ret = -ENOMEM; + goto fail_create_hcd; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + dev_dbg(&pdev->dev, "controller already in use\n"); + ret = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + ret = -ENXIO; + goto fail_ioremap; + } + + priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL); + if (!priv) { + dev_dbg(&pdev->dev, "error allocating priv data\n"); + ret = -ENOMEM; + goto fail_alloc; + } + + /* These are optional, we don't care if they fail */ + priv->fclk = clk_get(&pdev->dev, "usb_fck"); + if (IS_ERR(priv->fclk)) + priv->fclk = NULL; + + priv->iclk = clk_get(&pdev->dev, "usb_ick"); + if (IS_ERR(priv->iclk)) + priv->iclk = NULL; + + clk_enable(priv->fclk); + clk_enable(priv->iclk); + + ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); + if (ret != 0) { + dev_err(&pdev->dev, "Failed to add hcd"); + goto fail_add_hcd; + } + + priv->hcd = hcd; + platform_set_drvdata(pdev, priv); + + return ret; + +fail_add_hcd: + clk_disable(priv->iclk); + clk_disable(priv->fclk); + + clk_put(priv->iclk); + clk_put(priv->fclk); + + kfree(priv); +fail_alloc: + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), ret); + + return ret; +} + +static int __exit ehci_hcd_sh_remove(struct platform_device *pdev) +{ + struct ehci_sh_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + clk_disable(priv->fclk); + clk_disable(priv->iclk); + + clk_put(priv->fclk); + clk_put(priv->iclk); + + kfree(priv); + + return 0; +} + +static struct platform_driver ehci_hcd_sh_driver = { + .probe = ehci_hcd_sh_probe, + .remove = __exit_p(ehci_hcd_sh_remove), + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "sh_ehci", + .owner = THIS_MODULE, + }, +}; + +MODULE_ALIAS("platform:sh_ehci"); -- cgit v1.2.3 From ad78acafeed26f62c9e644f96eecb7c19bd78bb4 Mon Sep 17 00:00:00 2001 From: Alexey Charkov Date: Sun, 7 Nov 2010 19:28:55 +0300 Subject: usb: Add support for VIA VT8500 and compatibles in EHCI HCD VIA and WonderMedia Systems-on-Chip feature a standard EHCI host controller. This adds necessary glue to use the standard driver with these systems. Signed-off-by: Alexey Charkov Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/host/ehci-hcd.c | 5 ++ drivers/usb/host/ehci-vt8500.c | 172 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+) create mode 100644 drivers/usb/host/ehci-vt8500.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 67eb3770868f..7d13506cc383 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -66,6 +66,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_AT91SAM9G45 default y if ARCH_MXC default y if ARCH_OMAP3 + default y if ARCH_VT8500 default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 502a7e6fef42..10f985def70f 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1216,6 +1216,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_octeon_driver #endif +#ifdef CONFIG_ARCH_VT8500 +#include "ehci-vt8500.c" +#define PLATFORM_DRIVER vt8500_ehci_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c new file mode 100644 index 000000000000..20168062035a --- /dev/null +++ b/drivers/usb/host/ehci-vt8500.c @@ -0,0 +1,172 @@ +/* + * drivers/usb/host/ehci-vt8500.c + * + * Copyright (C) 2010 Alexey Charkov + * + * Based on ehci-au1xxx.c + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include + +static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int rc = 0; + + if (!udev->parent) /* udev is root hub itself, impossible */ + rc = -1; + /* we only support lpm device connected to root hub yet */ + if (ehci->has_lpm && !udev->parent->parent) { + rc = ehci_lpm_set_da(ehci, udev->devnum, udev->portnum); + if (!rc) + rc = ehci_lpm_check(ehci, udev->portnum); + } + return rc; +} + +static const struct hc_driver vt8500_ehci_hc_driver = { + .description = hcd_name, + .product_desc = "VT8500 EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_init, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + /* + * call back when device connected and addressed + */ + .update_device = ehci_update_device, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int vt8500_ehci_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct ehci_hcd *ehci; + struct resource *res; + int ret; + + if (usb_disabled()) + return -ENODEV; + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + pr_debug("resource[1] is not IORESOURCE_IRQ"); + return -ENOMEM; + } + hcd = usb_create_hcd(&vt8500_ehci_hc_driver, &pdev->dev, "VT8500"); + if (!hcd) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + pr_debug("request_mem_region failed"); + ret = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + pr_debug("ioremap failed"); + ret = -ENOMEM; + goto err2; + } + + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = readl(&ehci->caps->hcs_params); + + ehci_port_power(ehci, 1); + + ret = usb_add_hcd(hcd, pdev->resource[1].start, + IRQF_DISABLED | IRQF_SHARED); + if (ret == 0) { + platform_set_drvdata(pdev, hcd); + return ret; + } + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return ret; +} + +static int vt8500_ehci_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver vt8500_ehci_driver = { + .probe = vt8500_ehci_drv_probe, + .remove = vt8500_ehci_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "vt8500-ehci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:vt8500-ehci"); -- cgit v1.2.3 From c8c38de9d8002578599222296b90696745ac0fe3 Mon Sep 17 00:00:00 2001 From: Deepak Sikri Date: Wed, 10 Nov 2010 14:33:18 +0530 Subject: USB host: Adding USB ehci & ohci support for spear platform This patch adds support for ehci and ohci controller in the SPEAr platform. Changes since V2: added clear_tt_buffer_complete in ehci_spear_hc_driver Signed-off-by: Deepak Sikri Signed-off-by: Viresh Kumar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 2 + drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-spear.c | 212 +++++++++++++++++++++++++++++++++++++ drivers/usb/host/ohci-hcd.c | 5 + drivers/usb/host/ohci-spear.c | 240 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 drivers/usb/host/ehci-spear.c create mode 100644 drivers/usb/host/ohci-spear.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 7d13506cc383..0f81b606cb13 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -41,6 +41,7 @@ config USB_ARCH_HAS_OHCI default y if MFD_TC6393XB default y if ARCH_W90X900 default y if ARCH_DAVINCI_DA8XX + default y if PLAT_SPEAR # PPC: default y if STB03xxx default y if PPC_MPC52xx @@ -67,6 +68,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_MXC default y if ARCH_OMAP3 default y if ARCH_VT8500 + default y if PLAT_SPEAR default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 10f985def70f..87157db155f6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1221,6 +1221,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER vt8500_ehci_driver #endif +#ifdef CONFIG_PLAT_SPEAR +#include "ehci-spear.c" +#define PLATFORM_DRIVER spear_ehci_hcd_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c new file mode 100644 index 000000000000..75c00873443d --- /dev/null +++ b/drivers/usb/host/ehci-spear.c @@ -0,0 +1,212 @@ +/* +* Driver for EHCI HCD on SPEAR SOC +* +* Copyright (C) 2010 ST Micro Electronics, +* Deepak Sikri +* +* Based on various ehci-*.c drivers +* +* This file is subject to the terms and conditions of the GNU General Public +* License. See the file COPYING in the main directory of this archive for +* more details. +*/ + +#include +#include + +struct spear_ehci { + struct ehci_hcd ehci; + struct clk *clk; +}; + +#define to_spear_ehci(hcd) (struct spear_ehci *)hcd_to_ehci(hcd) + +static void spear_start_ehci(struct spear_ehci *ehci) +{ + clk_enable(ehci->clk); +} + +static void spear_stop_ehci(struct spear_ehci *ehci) +{ + clk_disable(ehci->clk); +} + +static int ehci_spear_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval = 0; + + /* registers start at offset 0x0 */ + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + &ehci->caps->hc_capbase)); + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci_reset(ehci); + ehci_port_power(ehci, 0); + + return retval; +} + +static const struct hc_driver ehci_spear_hc_driver = { + .description = hcd_name, + .product_desc = "SPEAr EHCI", + .hcd_priv_size = sizeof(struct spear_ehci), + + /* generic hardware linkage */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* basic lifecycle operations */ + .reset = ehci_spear_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* managing i/o requests and associated device resources */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* scheduling support */ + .get_frame_number = ehci_get_frame, + + /* root hub support */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + +static int spear_ehci_hcd_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd ; + struct spear_ehci *ehci; + struct resource *res; + struct clk *usbh_clk; + const struct hc_driver *driver = &ehci_spear_hc_driver; + int *pdata = pdev->dev.platform_data; + int irq, retval; + char clk_name[20] = "usbh_clk"; + + if (pdata == NULL) + return -EFAULT; + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail_irq_get; + } + + if (*pdata >= 0) + sprintf(clk_name, "usbh.%01d_clk", *pdata); + + usbh_clk = clk_get(NULL, clk_name); + if (IS_ERR(usbh_clk)) { + dev_err(&pdev->dev, "Error getting interface clock\n"); + retval = PTR_ERR(usbh_clk); + goto fail_get_usbh_clk; + } + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, + driver->description)) { + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&pdev->dev, "error mapping memory\n"); + retval = -ENOMEM; + goto fail_ioremap; + } + + ehci = (struct spear_ehci *)hcd_to_ehci(hcd); + ehci->clk = usbh_clk; + + spear_start_ehci(ehci); + retval = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + if (retval) + goto fail_add_hcd; + + return retval; + +fail_add_hcd: + spear_stop_ehci(ehci); + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + clk_put(usbh_clk); +fail_get_usbh_clk: +fail_irq_get: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval ; +} + +static int spear_ehci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct spear_ehci *ehci_p = to_spear_ehci(hcd); + + if (!hcd) + return 0; + if (in_interrupt()) + BUG(); + usb_remove_hcd(hcd); + + if (ehci_p->clk) + spear_stop_ehci(ehci_p); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + if (ehci_p->clk) + clk_put(ehci_p->clk); + + return 0; +} + +static struct platform_driver spear_ehci_hcd_driver = { + .probe = spear_ehci_hcd_drv_probe, + .remove = spear_ehci_hcd_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "spear-ehci", + .bus = &platform_bus_type + } +}; + +MODULE_ALIAS("platform:spear-ehci"); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5179acb7aa2f..4a4a4f025385 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1081,6 +1081,11 @@ MODULE_LICENSE ("GPL"); #define OF_PLATFORM_DRIVER ohci_hcd_ppc_of_driver #endif +#ifdef CONFIG_PLAT_SPEAR +#include "ohci-spear.c" +#define PLATFORM_DRIVER spear_ohci_hcd_driver +#endif + #ifdef CONFIG_PPC_PS3 #include "ohci-ps3.c" #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c new file mode 100644 index 000000000000..4fd4bea9ac7a --- /dev/null +++ b/drivers/usb/host/ohci-spear.c @@ -0,0 +1,240 @@ +/* +* OHCI HCD (Host Controller Driver) for USB. +* +* Copyright (C) 2010 ST Microelectronics. +* Deepak Sikri +* +* Based on various ohci-*.c drivers +* +* This file is licensed under the terms of the GNU General Public +* License version 2. This program is licensed "as is" without any +* warranty of any kind, whether express or implied. +*/ + +#include +#include +#include + +struct spear_ohci { + struct ohci_hcd ohci; + struct clk *clk; +}; + +#define to_spear_ohci(hcd) (struct spear_ohci *)hcd_to_ohci(hcd) + +static void spear_start_ohci(struct spear_ohci *ohci) +{ + clk_enable(ohci->clk); +} + +static void spear_stop_ohci(struct spear_ohci *ohci) +{ + clk_disable(ohci->clk); +} + +static int __devinit ohci_spear_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + ohci->regs = hcd->regs; + + ret = ohci_run(ohci); + if (ret < 0) { + dev_err(hcd->self.controller, "can't start\n"); + ohci_stop(hcd); + return ret; + } + + create_debug_files(ohci); + +#ifdef DEBUG + ohci_dump(ohci, 1); +#endif + return 0; +} + +static const struct hc_driver ohci_spear_hc_driver = { + .description = hcd_name, + .product_desc = "SPEAr OHCI", + .hcd_priv_size = sizeof(struct spear_ohci), + + /* generic hardware linkage */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* basic lifecycle operations */ + .start = ohci_spear_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + + /* managing i/o requests and associated device resources */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* scheduling support */ + .get_frame_number = ohci_get_frame, + + /* root hub support */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, + + .start_port_reset = ohci_start_port_reset, +}; + +static int spear_ohci_hcd_drv_probe(struct platform_device *pdev) +{ + const struct hc_driver *driver = &ohci_spear_hc_driver; + struct usb_hcd *hcd = NULL; + struct clk *usbh_clk; + struct spear_ohci *ohci_p; + struct resource *res; + int retval, irq; + int *pdata = pdev->dev.platform_data; + char clk_name[20] = "usbh_clk"; + + if (pdata == NULL) + return -EFAULT; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail_irq_get; + } + + if (*pdata >= 0) + sprintf(clk_name, "usbh.%01d_clk", *pdata); + + usbh_clk = clk_get(NULL, clk_name); + if (IS_ERR(usbh_clk)) { + dev_err(&pdev->dev, "Error getting interface clock\n"); + retval = PTR_ERR(usbh_clk); + goto fail_get_usbh_clk; + } + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail_create_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENODEV; + goto fail_request_resource; + } + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = resource_size(res); + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto fail_request_resource; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + retval = -ENOMEM; + goto fail_ioremap; + } + + ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd); + ohci_p->clk = usbh_clk; + spear_start_ohci(ohci_p); + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), IRQF_DISABLED); + if (retval == 0) + return retval; + + spear_stop_ohci(ohci_p); + iounmap(hcd->regs); +fail_ioremap: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +fail_request_resource: + usb_put_hcd(hcd); +fail_create_hcd: + clk_put(usbh_clk); +fail_get_usbh_clk: +fail_irq_get: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval; +} + +static int spear_ohci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + usb_remove_hcd(hcd); + if (ohci_p->clk) + spear_stop_ohci(ohci_p); + + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + + if (ohci_p->clk) + clk_put(ohci_p->clk); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#if defined(CONFIG_PM) +static int spear_ohci_hcd_drv_suspend(struct platform_device *dev, + pm_message_t message) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + spear_stop_ohci(ohci_p); + ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; + return 0; +} + +static int spear_ohci_hcd_drv_resume(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct spear_ohci *ohci_p = to_spear_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + spear_start_ohci(ohci_p); + ohci_finish_controller_resume(hcd); + return 0; +} +#endif + +/* Driver definition to register with the platform bus */ +static struct platform_driver spear_ohci_hcd_driver = { + .probe = spear_ohci_hcd_drv_probe, + .remove = spear_ohci_hcd_drv_remove, +#ifdef CONFIG_PM + .suspend = spear_ohci_hcd_drv_suspend, + .resume = spear_ohci_hcd_drv_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "spear-ohci", + }, +}; + +MODULE_ALIAS("platform:spear-ohci"); -- cgit v1.2.3 From ce7eb32fc24a7380f55924360fa0c96297aa237e Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Mon, 15 Nov 2010 12:14:01 -0800 Subject: drivers/usb/host/uhci-hcd.c: Remove unnecessary casts of pci_get_drvdata Signed-off-by: Joe Perches Acked-by: Alan Stern Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index f52d04db28f4..cee867829ec9 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -569,7 +569,7 @@ static int uhci_init(struct usb_hcd *hcd) */ static void uhci_shutdown(struct pci_dev *pdev) { - struct usb_hcd *hcd = (struct usb_hcd *) pci_get_drvdata(pdev); + struct usb_hcd *hcd = pci_get_drvdata(pdev); uhci_hc_died(hcd_to_uhci(hcd)); } -- cgit v1.2.3 From 4f6838436915fdc281173bfd5bef6d8ab5cb1a7f Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Wed, 17 Nov 2010 07:43:09 -0800 Subject: USB: ce4100: Add support for CE4100 EHCI IP block to EHCI driver This patch adds support for the EHCI IP block present on the Intel CE4100. Signed-off-by: Dirk Brandewie CC: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-pci.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index a1e8d273103f..56c78e93440f 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -22,6 +22,9 @@ #error "This file is PCI bus glue. CONFIG_PCI must be defined." #endif +/* defined here to avoid adding to pci_ids.h for single instance use */ +#define PCI_DEVICE_ID_INTEL_CE4100_USB 0x2e70 + /*-------------------------------------------------------------------------*/ /* called after powerup, by probe or system-pm "wakeup" */ @@ -124,6 +127,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci_info(ehci, "disable lpm for langwell/penwell\n"); ehci->has_lpm = 0; } + if (pdev->device == PCI_DEVICE_ID_INTEL_CE4100_USB) { + hcd->has_tt = 1; + tdi_reset(ehci); + } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { -- cgit v1.2.3 From c1e0774d74481fdc3082d2096a99a3aa411a71f1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 26 Nov 2010 16:43:38 +0900 Subject: usb: ehci-sh: Fix up fault in shutdown path. We can't use the generic usb_hcd_platform_shutdown helper on account of the fact we don't stash the hcd pointer in the driver data, so we provide our own shutdown handler. Signed-off-by: Paul Mundt --- drivers/usb/host/ehci-sh.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 430b72e637fd..9d3a29f8ced5 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -219,10 +219,19 @@ static int __exit ehci_hcd_sh_remove(struct platform_device *pdev) return 0; } +static void ehci_hcd_sh_shutdown(struct platform_device *pdev) +{ + struct ehci_sh_priv *priv = platform_get_drvdata(pdev); + struct usb_hcd *hcd = priv->hcd; + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + static struct platform_driver ehci_hcd_sh_driver = { .probe = ehci_hcd_sh_probe, .remove = __exit_p(ehci_hcd_sh_remove), - .shutdown = usb_hcd_platform_shutdown, + .shutdown = ehci_hcd_sh_shutdown, .driver = { .name = "sh_ehci", .owner = THIS_MODULE, -- cgit v1.2.3 From 6e9d4476063a820764ec063bf683379c38ea1e18 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Fri, 26 Nov 2010 16:48:31 +0900 Subject: usb: ehci-sh: Add missing ehci helpers. The ehci-sh driver was missing tie-ins for endpoint_reset and clear_tt_buffer_complete, add them in. Reported-by: Alan Stern Signed-off-by: Paul Mundt --- drivers/usb/host/ehci-sh.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 9d3a29f8ced5..595f70f42b52 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -72,6 +72,7 @@ static const struct hc_driver ehci_sh_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support @@ -91,6 +92,7 @@ static const struct hc_driver ehci_sh_hc_driver = { .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_hcd_sh_probe(struct platform_device *pdev) -- cgit v1.2.3 From 6dba39e278b81665a838f37a75fe37b89f3ce610 Mon Sep 17 00:00:00 2001 From: Keshava Munegowda Date: Sun, 21 Nov 2010 23:23:40 +0530 Subject: usb: ehci-omap: update clock names to be more generic Rename usbhost2_120m_fck to usbhost_hs_fck and usbhost1_48m_fck to usbhost_fs_fck, to better reflect the clocks' functionalities. In OMAP4, the frequencies for the corresponding clocks are not necessarily the same as with OMAP3, however the functionalities are. Signed-off-by: Keshava Munegowda Signed-off-by: Anand Gadiyar --- drivers/usb/host/ehci-omap.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 116ae280053a..d042bdefa2bd 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -156,8 +156,8 @@ struct ehci_hcd_omap { struct device *dev; struct clk *usbhost_ick; - struct clk *usbhost2_120m_fck; - struct clk *usbhost1_48m_fck; + struct clk *usbhost_hs_fck; + struct clk *usbhost_fs_fck; struct clk *usbtll_fck; struct clk *usbtll_ick; @@ -286,19 +286,19 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) } clk_enable(omap->usbhost_ick); - omap->usbhost2_120m_fck = clk_get(omap->dev, "usbhost_120m_fck"); - if (IS_ERR(omap->usbhost2_120m_fck)) { - ret = PTR_ERR(omap->usbhost2_120m_fck); + omap->usbhost_hs_fck = clk_get(omap->dev, "usbhost_120m_fck"); + if (IS_ERR(omap->usbhost_hs_fck)) { + ret = PTR_ERR(omap->usbhost_hs_fck); goto err_host_120m_fck; } - clk_enable(omap->usbhost2_120m_fck); + clk_enable(omap->usbhost_hs_fck); - omap->usbhost1_48m_fck = clk_get(omap->dev, "usbhost_48m_fck"); - if (IS_ERR(omap->usbhost1_48m_fck)) { - ret = PTR_ERR(omap->usbhost1_48m_fck); + omap->usbhost_fs_fck = clk_get(omap->dev, "usbhost_48m_fck"); + if (IS_ERR(omap->usbhost_fs_fck)) { + ret = PTR_ERR(omap->usbhost_fs_fck); goto err_host_48m_fck; } - clk_enable(omap->usbhost1_48m_fck); + clk_enable(omap->usbhost_fs_fck); if (omap->phy_reset) { /* Refer: ISSUE1 */ @@ -472,8 +472,8 @@ err_tll_ick: clk_put(omap->usbtll_fck); err_tll_fck: - clk_disable(omap->usbhost1_48m_fck); - clk_put(omap->usbhost1_48m_fck); + clk_disable(omap->usbhost_fs_fck); + clk_put(omap->usbhost_fs_fck); if (omap->phy_reset) { if (gpio_is_valid(omap->reset_gpio_port[0])) @@ -484,8 +484,8 @@ err_tll_fck: } err_host_48m_fck: - clk_disable(omap->usbhost2_120m_fck); - clk_put(omap->usbhost2_120m_fck); + clk_disable(omap->usbhost_hs_fck); + clk_put(omap->usbhost_hs_fck); err_host_120m_fck: clk_disable(omap->usbhost_ick); @@ -550,16 +550,16 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) omap->usbhost_ick = NULL; } - if (omap->usbhost1_48m_fck != NULL) { - clk_disable(omap->usbhost1_48m_fck); - clk_put(omap->usbhost1_48m_fck); - omap->usbhost1_48m_fck = NULL; + if (omap->usbhost_fs_fck != NULL) { + clk_disable(omap->usbhost_fs_fck); + clk_put(omap->usbhost_fs_fck); + omap->usbhost_fs_fck = NULL; } - if (omap->usbhost2_120m_fck != NULL) { - clk_disable(omap->usbhost2_120m_fck); - clk_put(omap->usbhost2_120m_fck); - omap->usbhost2_120m_fck = NULL; + if (omap->usbhost_hs_fck != NULL) { + clk_disable(omap->usbhost_hs_fck); + clk_put(omap->usbhost_hs_fck); + omap->usbhost_hs_fck = NULL; } if (omap->usbtll_ick != NULL) { -- cgit v1.2.3 From c072604115ab50d023eb5c33d4f3229400e441f4 Mon Sep 17 00:00:00 2001 From: Keshava Munegowda Date: Sun, 21 Nov 2010 23:23:40 +0530 Subject: usb: ehci-omap: don't hard-code TLL channel count Make the TLL channel count a parameter instead of a hardcoded value. This allows us to be flexible with future OMAP revisions which could have a different number of channels. Signed-off-by: Keshava Munegowda Signed-off-by: Anand Gadiyar --- drivers/usb/host/ehci-omap.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index d042bdefa2bd..d60efdc9b142 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -191,13 +191,14 @@ struct ehci_hcd_omap { /*-------------------------------------------------------------------------*/ -static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) +static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask, + u8 tll_channel_count) { unsigned reg; int i; /* Program the 3 TLL channels upfront */ - for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + for (i = 0; i < tll_channel_count; i++) { reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); /* Disable AutoIdle, BitStuffing and use SDR Mode */ @@ -217,7 +218,7 @@ static void omap_usb_utmi_init(struct ehci_hcd_omap *omap, u8 tll_channel_mask) ehci_omap_writel(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); /* Enable channels now */ - for (i = 0; i < OMAP_TLL_CHANNEL_COUNT; i++) { + for (i = 0; i < tll_channel_count; i++) { reg = ehci_omap_readl(omap->tll_base, OMAP_TLL_CHANNEL_CONF(i)); /* Enable only the reg that is needed */ @@ -438,7 +439,7 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) tll_ch_mask |= OMAP_TLL_CHANNEL_3_EN_MASK; /* Enable UTMI mode for required TLL channels */ - omap_usb_utmi_init(omap, tll_ch_mask); + omap_usb_utmi_init(omap, tll_ch_mask, OMAP_TLL_CHANNEL_COUNT); } if (omap->phy_reset) { -- cgit v1.2.3 From 7f124f4b353672bc150af959910cfc2a9778260a Mon Sep 17 00:00:00 2001 From: Keshava Munegowda Date: Sun, 21 Nov 2010 23:23:41 +0530 Subject: usb: ehci: introduce CONFIG_USB_EHCI_HCD_OMAP Introduce the CONFIG_USB_EHCI_HCD_OMAP option to select EHCI support on OMAP3 and later chips. This scales better than having a long line of dependencies for each new OMAP with EHCI support. Signed-off-by: Keshava Munegowda Signed-off-by: Anand Gadiyar --- drivers/usb/host/Kconfig | 8 ++++++++ drivers/usb/host/ehci-hcd.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 2391c396ca32..6a7c688b4781 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -133,6 +133,14 @@ config USB_EHCI_MXC ---help--- Variation of ARC USB block used in some Freescale chips. +config USB_EHCI_HCD_OMAP + bool "EHCI support for OMAP3 and later chips" + depends on USB_EHCI_HCD && ARCH_OMAP + default y + --- help --- + Enables support for the on-chip EHCI controller on + OMAP3 and later chips. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 87157db155f6..2f06ba471953 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1171,7 +1171,7 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ehci_hcd_au1xxx_driver #endif -#ifdef CONFIG_ARCH_OMAP3 +#ifdef CONFIG_USB_EHCI_HCD_OMAP #include "ehci-omap.c" #define PLATFORM_DRIVER ehci_hcd_omap_driver #endif -- cgit v1.2.3 From c5dff5545c97ab33bdb2a529a2375966ceb0700c Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Sun, 21 Nov 2010 23:23:41 +0530 Subject: usb: ehci-omap: use clkdev aliases for functional clocks Use the recently updated aliases to get functional clocks needed by the driver. This allows the driver to acquire OMAP4-specific clocks without having to use different clock names for OMAP3 and OMAP4. Signed-off-by: Anand Gadiyar --- drivers/usb/host/ehci-omap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index d60efdc9b142..7a4682c4ff67 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -287,14 +287,14 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) } clk_enable(omap->usbhost_ick); - omap->usbhost_hs_fck = clk_get(omap->dev, "usbhost_120m_fck"); + omap->usbhost_hs_fck = clk_get(omap->dev, "hs_fck"); if (IS_ERR(omap->usbhost_hs_fck)) { ret = PTR_ERR(omap->usbhost_hs_fck); goto err_host_120m_fck; } clk_enable(omap->usbhost_hs_fck); - omap->usbhost_fs_fck = clk_get(omap->dev, "usbhost_48m_fck"); + omap->usbhost_fs_fck = clk_get(omap->dev, "fs_fck"); if (IS_ERR(omap->usbhost_fs_fck)) { ret = PTR_ERR(omap->usbhost_fs_fck); goto err_host_48m_fck; -- cgit v1.2.3 From a42ccdc14de388a35ad0e8057543369351395eb9 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Sun, 21 Nov 2010 23:23:41 +0530 Subject: usb: ehci-omap: add helpers for checking port mode Introduce helper functions to test port mode. These checks are performed in several places in the driver, and these helpers improve readability. Signed-off-by: Anand Gadiyar --- drivers/usb/host/ehci-omap.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 7a4682c4ff67..dd9d5c1d9a8f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -127,6 +127,9 @@ #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 +#define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY) +#define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL) + /*-------------------------------------------------------------------------*/ static inline void ehci_omap_writel(void __iomem *base, u32 reg, u32 val) @@ -387,27 +390,27 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) /* Bypass the TLL module for PHY mode operation */ if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); - if ((omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) || - (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) || - (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY)) + if (is_ehci_phy_mode(omap->port_mode[0]) || + is_ehci_phy_mode(omap->port_mode[1]) || + is_ehci_phy_mode(omap->port_mode[2])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; else reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; } else { dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); - if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_PHY) + if (is_ehci_phy_mode(omap->port_mode[0])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - else if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_TLL) + else if (is_ehci_tll_mode(omap->port_mode[0])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_PHY) + if (is_ehci_phy_mode(omap->port_mode[1])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - else if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_TLL) + else if (is_ehci_tll_mode(omap->port_mode[1])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_PHY) + if (is_ehci_phy_mode(omap->port_mode[2])) reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - else if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_TLL) + else if (is_ehci_tll_mode(omap->port_mode[2])) reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; } -- cgit v1.2.3 From 4792a15bf0f388838c3e16636f961c99bc2f3572 Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Sun, 21 Nov 2010 23:23:42 +0530 Subject: usb: ehci-omap: Add OMAP4 support Update the ehci-omap glue layer to support the controller in the OMAP4. Major differences from OMAP3 is that the OMAP4 has per-port clocking, and supports ULPI output clocking mode. The old input clocking mode is not supported. Also, there are only 2 externally available ports as against 3 in the OMAP3. The third port is internally tied off and should not be used. Signed-off-by: Keshava Munegowda Signed-off-by: Anand Gadiyar --- drivers/usb/host/ehci-omap.c | 254 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 212 insertions(+), 42 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index dd9d5c1d9a8f..0374eb47f09b 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -1,11 +1,12 @@ /* - * ehci-omap.c - driver for USBHOST on OMAP 34xx processor + * ehci-omap.c - driver for USBHOST on OMAP3/4 processors * - * Bus Glue for OMAP34xx USBHOST 3 port EHCI controller - * Tested on OMAP3430 ES2.0 SDP + * Bus Glue for the EHCI controllers in OMAP3/4 + * Tested on several OMAP3 boards, and OMAP4 Pandaboard * - * Copyright (C) 2007-2008 Texas Instruments, Inc. + * Copyright (C) 2007-2010 Texas Instruments, Inc. * Author: Vikram Pandita + * Author: Anand Gadiyar * * Copyright (C) 2009 Nokia Corporation * Contact: Felipe Balbi @@ -26,11 +27,14 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * TODO (last updated Feb 12, 2010): + * TODO (last updated Nov 21, 2010): * - add kernel-doc * - enable AUTOIDLE * - add suspend/resume * - move workarounds to board-files + * - factor out code common to OHCI + * - add HSIC and TLL support + * - convert to use hwmod and runtime PM */ #include @@ -114,6 +118,23 @@ #define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) #define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) +/* OMAP4-specific defines */ +#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2) +#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2) + +#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4) +#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4) +#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0) + +#define OMAP4_P1_MODE_CLEAR (3 << 16) +#define OMAP4_P1_MODE_TLL (1 << 16) +#define OMAP4_P1_MODE_HSIC (3 << 16) +#define OMAP4_P2_MODE_CLEAR (3 << 18) +#define OMAP4_P2_MODE_TLL (1 << 18) +#define OMAP4_P2_MODE_HSIC (3 << 18) + +#define OMAP_REV2_TLL_CHANNEL_COUNT 2 + #define OMAP_UHH_DEBUG_CSR (0x44) /* EHCI Register Set */ @@ -127,8 +148,16 @@ #define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8 #define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0 +/* Values of UHH_REVISION - Note: these are not given in the TRM */ +#define OMAP_EHCI_REV1 0x00000010 /* OMAP3 */ +#define OMAP_EHCI_REV2 0x50700100 /* OMAP4 */ + +#define is_omap_ehci_rev1(x) (x->omap_ehci_rev == OMAP_EHCI_REV1) +#define is_omap_ehci_rev2(x) (x->omap_ehci_rev == OMAP_EHCI_REV2) + #define is_ehci_phy_mode(x) (x == EHCI_HCD_OMAP_MODE_PHY) #define is_ehci_tll_mode(x) (x == EHCI_HCD_OMAP_MODE_TLL) +#define is_ehci_hsic_mode(x) (x == EHCI_HCD_OMAP_MODE_HSIC) /*-------------------------------------------------------------------------*/ @@ -163,6 +192,10 @@ struct ehci_hcd_omap { struct clk *usbhost_fs_fck; struct clk *usbtll_fck; struct clk *usbtll_ick; + struct clk *xclk60mhsp1_ck; + struct clk *xclk60mhsp2_ck; + struct clk *utmi_p1_fck; + struct clk *utmi_p2_fck; /* FIXME the following two workarounds are * board specific not silicon-specific so these @@ -179,6 +212,9 @@ struct ehci_hcd_omap { /* phy reset workaround */ int phy_reset; + /* IP revision */ + u32 omap_ehci_rev; + /* desired phy_mode: TLL, PHY */ enum ehci_hcd_omap_mode port_mode[OMAP3_HS_USB_PORTS]; @@ -337,6 +373,80 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) } clk_enable(omap->usbtll_ick); + omap->omap_ehci_rev = ehci_omap_readl(omap->uhh_base, + OMAP_UHH_REVISION); + dev_dbg(omap->dev, "OMAP UHH_REVISION 0x%x\n", + omap->omap_ehci_rev); + + /* + * Enable per-port clocks as needed (newer controllers only). + * - External ULPI clock for PHY mode + * - Internal clocks for TLL and HSIC modes (TODO) + */ + if (is_omap_ehci_rev2(omap)) { + switch (omap->port_mode[0]) { + case EHCI_HCD_OMAP_MODE_PHY: + omap->xclk60mhsp1_ck = clk_get(omap->dev, + "xclk60mhsp1_ck"); + if (IS_ERR(omap->xclk60mhsp1_ck)) { + ret = PTR_ERR(omap->xclk60mhsp1_ck); + dev_err(omap->dev, + "Unable to get Port1 ULPI clock\n"); + } + + omap->utmi_p1_fck = clk_get(omap->dev, + "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_fck)) { + ret = PTR_ERR(omap->utmi_p1_fck); + dev_err(omap->dev, + "Unable to get utmi_p1_fck\n"); + } + + ret = clk_set_parent(omap->utmi_p1_fck, + omap->xclk60mhsp1_ck); + if (ret != 0) { + dev_err(omap->dev, + "Unable to set P1 f-clock\n"); + } + break; + case EHCI_HCD_OMAP_MODE_TLL: + /* TODO */ + default: + break; + } + switch (omap->port_mode[1]) { + case EHCI_HCD_OMAP_MODE_PHY: + omap->xclk60mhsp2_ck = clk_get(omap->dev, + "xclk60mhsp2_ck"); + if (IS_ERR(omap->xclk60mhsp2_ck)) { + ret = PTR_ERR(omap->xclk60mhsp2_ck); + dev_err(omap->dev, + "Unable to get Port2 ULPI clock\n"); + } + + omap->utmi_p2_fck = clk_get(omap->dev, + "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_fck)) { + ret = PTR_ERR(omap->utmi_p2_fck); + dev_err(omap->dev, + "Unable to get utmi_p2_fck\n"); + } + + ret = clk_set_parent(omap->utmi_p2_fck, + omap->xclk60mhsp2_ck); + if (ret != 0) { + dev_err(omap->dev, + "Unable to set P2 f-clock\n"); + } + break; + case EHCI_HCD_OMAP_MODE_TLL: + /* TODO */ + default: + break; + } + } + + /* perform TLL soft reset, and wait until reset is complete */ ehci_omap_writel(omap->tll_base, OMAP_USBTLL_SYSCONFIG, OMAP_USBTLL_SYSCONFIG_SOFTRESET); @@ -364,12 +474,20 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) /* Put UHH in NoIdle/NoStandby mode */ reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSCONFIG); - reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP - | OMAP_UHH_SYSCONFIG_SIDLEMODE - | OMAP_UHH_SYSCONFIG_CACTIVITY - | OMAP_UHH_SYSCONFIG_MIDLEMODE); - reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + if (is_omap_ehci_rev1(omap)) { + reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP + | OMAP_UHH_SYSCONFIG_SIDLEMODE + | OMAP_UHH_SYSCONFIG_CACTIVITY + | OMAP_UHH_SYSCONFIG_MIDLEMODE); + reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + + } else if (is_omap_ehci_rev2(omap)) { + reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; + reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; + } ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); reg = ehci_omap_readl(omap->uhh_base, OMAP_UHH_HOSTCONFIG); @@ -380,40 +498,56 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; - if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; - if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; - if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) - reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; - - /* Bypass the TLL module for PHY mode operation */ - if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { - dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); - if (is_ehci_phy_mode(omap->port_mode[0]) || - is_ehci_phy_mode(omap->port_mode[1]) || - is_ehci_phy_mode(omap->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - else - reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; - } else { - dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); - if (is_ehci_phy_mode(omap->port_mode[0])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - else if (is_ehci_tll_mode(omap->port_mode[0])) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; - - if (is_ehci_phy_mode(omap->port_mode[1])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - else if (is_ehci_tll_mode(omap->port_mode[1])) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; - - if (is_ehci_phy_mode(omap->port_mode[2])) - reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; - else if (is_ehci_tll_mode(omap->port_mode[2])) - reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + if (is_omap_ehci_rev1(omap)) { + if (omap->port_mode[0] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (omap->port_mode[1] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (omap->port_mode[2] == EHCI_HCD_OMAP_MODE_UNKNOWN) + reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { + dev_dbg(omap->dev, "OMAP3 ES version <= ES2.1\n"); + if (is_ehci_phy_mode(omap->port_mode[0]) || + is_ehci_phy_mode(omap->port_mode[1]) || + is_ehci_phy_mode(omap->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + } else { + dev_dbg(omap->dev, "OMAP3 ES version > ES2.1\n"); + if (is_ehci_phy_mode(omap->port_mode[0])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[0])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + + if (is_ehci_phy_mode(omap->port_mode[1])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[1])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + + if (is_ehci_phy_mode(omap->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + else if (is_ehci_tll_mode(omap->port_mode[2])) + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + } + } else if (is_omap_ehci_rev2(omap)) { + /* Clear port mode fields for PHY mode*/ + reg &= ~OMAP4_P1_MODE_CLEAR; + reg &= ~OMAP4_P2_MODE_CLEAR; + + if (is_ehci_tll_mode(omap->port_mode[0])) + reg |= OMAP4_P1_MODE_TLL; + else if (is_ehci_hsic_mode(omap->port_mode[0])) + reg |= OMAP4_P1_MODE_HSIC; + if (is_ehci_tll_mode(omap->port_mode[1])) + reg |= OMAP4_P2_MODE_TLL; + else if (is_ehci_hsic_mode(omap->port_mode[1])) + reg |= OMAP4_P2_MODE_HSIC; } + ehci_omap_writel(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); dev_dbg(omap->dev, "UHH setup done, uhh_hostconfig=%x\n", reg); @@ -468,6 +602,14 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) return 0; err_sys_status: + clk_disable(omap->utmi_p2_fck); + clk_put(omap->utmi_p2_fck); + clk_disable(omap->xclk60mhsp2_ck); + clk_put(omap->xclk60mhsp2_ck); + clk_disable(omap->utmi_p1_fck); + clk_put(omap->utmi_p1_fck); + clk_disable(omap->xclk60mhsp1_ck); + clk_put(omap->xclk60mhsp1_ck); clk_disable(omap->usbtll_ick); clk_put(omap->usbtll_ick); @@ -507,6 +649,8 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) /* Reset OMAP modules for insmod/rmmod to work */ ehci_omap_writel(omap->uhh_base, OMAP_UHH_SYSCONFIG, + is_omap_ehci_rev2(omap) ? + OMAP4_UHH_SYSCONFIG_SOFTRESET : OMAP_UHH_SYSCONFIG_SOFTRESET); while (!(ehci_omap_readl(omap->uhh_base, OMAP_UHH_SYSSTATUS) & (1 << 0))) { @@ -572,6 +716,32 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd) omap->usbtll_ick = NULL; } + if (is_omap_ehci_rev2(omap)) { + if (omap->xclk60mhsp1_ck != NULL) { + clk_disable(omap->xclk60mhsp1_ck); + clk_put(omap->xclk60mhsp1_ck); + omap->xclk60mhsp1_ck = NULL; + } + + if (omap->utmi_p1_fck != NULL) { + clk_disable(omap->utmi_p1_fck); + clk_put(omap->utmi_p1_fck); + omap->utmi_p1_fck = NULL; + } + + if (omap->xclk60mhsp2_ck != NULL) { + clk_disable(omap->xclk60mhsp2_ck); + clk_put(omap->xclk60mhsp2_ck); + omap->xclk60mhsp2_ck = NULL; + } + + if (omap->utmi_p2_fck != NULL) { + clk_disable(omap->utmi_p2_fck); + clk_put(omap->utmi_p2_fck); + omap->utmi_p2_fck = NULL; + } + } + if (omap->phy_reset) { if (gpio_is_valid(omap->reset_gpio_port[0])) gpio_free(omap->reset_gpio_port[0]); -- cgit v1.2.3 From 73f35c60d5c4a98061fc0f94505bf26fd4bb1a1c Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Tue, 9 Nov 2010 00:10:52 +0100 Subject: USB: Remove unnecessary casts of void ptr returning alloc function return values Hi, The [vk][cmz]alloc(_node) family of functions return void pointers which it's completely unnecessary/pointless to cast to other pointer types since that happens implicitly. This patch removes such casts from drivers/usb/ Signed-off-by: Jesper Juhl --- drivers/usb/host/ehci-dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 86afdc73322f..a6bab7264c63 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -879,7 +879,7 @@ static int fill_buffer(struct debug_buffer *buf) int ret = 0; if (!buf->output_buf) - buf->output_buf = (char *)vmalloc(buf->alloc_size); + buf->output_buf = vmalloc(buf->alloc_size); if (!buf->output_buf) { ret = -ENOMEM; -- cgit v1.2.3 From b48d7f50e6f16478304170eaf5c2d1a540ba5e31 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 30 Nov 2010 17:57:02 +0900 Subject: usb: Add in missing EHCI helpers. Several of the EHCI glue drivers either predate or were merged in the same timeframe as API changes at the USB core level, resulting in some missing endpoint_reset and clear_tt_buffer_complete callbacks. This fixes up all of ehci-atmel, mxc, w90x900, and xilinx-of to tie in the new helpers, which brings them in line with everyone else. Reported-by: Alan Stern Signed-off-by: Paul Mundt Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-atmel.c | 3 +++ drivers/usb/host/ehci-mxc.c | 3 +++ drivers/usb/host/ehci-w90x900.c | 3 +++ drivers/usb/host/ehci-xilinx-of.c | 1 + 4 files changed, 10 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 51bd0edf544f..d6a69d514a84 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -99,6 +99,7 @@ static const struct hc_driver ehci_atmel_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* scheduling support */ .get_frame_number = ehci_get_frame, @@ -110,6 +111,8 @@ static const struct hc_driver ehci_atmel_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __init ehci_atmel_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index ac9c4d7c44af..802fcf6f8839 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -92,6 +92,7 @@ static const struct hc_driver ehci_mxc_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support @@ -107,6 +108,8 @@ static const struct hc_driver ehci_mxc_hc_driver = { .bus_resume = ehci_bus_resume, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int ehci_mxc_drv_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index cfa21ea20f82..6bc35809a5c6 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -130,6 +130,7 @@ static const struct hc_driver ehci_w90x900_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support @@ -147,6 +148,8 @@ static const struct hc_driver ehci_w90x900_hc_driver = { #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __devinit ehci_w90x900_probe(struct platform_device *pdev) diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index 6c8076ad821d..e8f4f36fdf0b 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -117,6 +117,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = { .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, /* * scheduling support -- cgit v1.2.3 From 8170344cb8aaa726bf1afae83288946b7cfcb556 Mon Sep 17 00:00:00 2001 From: Namhyung Kim Date: Tue, 7 Dec 2010 01:03:32 +0900 Subject: USB: whci-hcd: fix compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Annotate whci_hcd_id_table as '__used' to fix following warning: CC drivers/usb/host/whci/hcd.o drivers/usb/host/whci/hcd.c:359: warning: ‘whci_hcd_id_table’ defined but not used Signed-off-by: Namhyung Kim Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/whci/hcd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c index 72b6892fda67..9546f6cd01f0 100644 --- a/drivers/usb/host/whci/hcd.c +++ b/drivers/usb/host/whci/hcd.c @@ -356,7 +356,7 @@ static void __exit whci_hc_driver_exit(void) module_exit(whci_hc_driver_exit); /* PCI device ID's that we handle (so it gets loaded) */ -static struct pci_device_id whci_hcd_id_table[] = { +static struct pci_device_id __used whci_hcd_id_table[] = { { PCI_DEVICE_CLASS(PCI_CLASS_WIRELESS_WHCI, ~0) }, { /* empty last entry */ } }; -- cgit v1.2.3 From 16325f18eaa6066c4b913a5661fc8190ce564f7c Mon Sep 17 00:00:00 2001 From: Tobias Ollmann Date: Thu, 9 Dec 2010 14:24:27 +0100 Subject: USB: host: uhci-q: Fixed minor coding style issues Fixed coding style issues (delete trailing whitespaces, break long line) Signed-off-by: Tobias Ollmann Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/uhci-q.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 2090b45eb606..af77abb5c68b 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -29,7 +29,7 @@ static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); - uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); + uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); } static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) @@ -195,7 +195,9 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, } else { struct uhci_td *ntd; - ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); + ntd = list_entry(td->fl_list.next, + struct uhci_td, + fl_list); uhci->frame[td->frame] = LINK_TO_TD(ntd); uhci->frame_cpu[td->frame] = ntd; } @@ -728,7 +730,7 @@ static inline struct urb_priv *uhci_alloc_urb_priv(struct uhci_hcd *uhci, urbp->urb = urb; urb->hcpriv = urbp; - + INIT_LIST_HEAD(&urbp->node); INIT_LIST_HEAD(&urbp->td_list); @@ -846,7 +848,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; - + uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, destination | uhci_explen(pktsze), data); @@ -857,7 +859,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, } /* - * Build the final TD for control status + * Build the final TD for control status */ td = uhci_alloc_td(uhci); if (!td) -- cgit v1.2.3 From 05570297ecbe834b1756b522412b68eaffb9ab11 Mon Sep 17 00:00:00 2001 From: Alex He Date: Tue, 7 Dec 2010 10:10:08 +0800 Subject: USB: EHCI: ASPM quirk of ISOC on AMD SB800 When ASPM PM Feature is enabled on UMI link, devices that use ISOC stream of data transfer may be exposed to longer latency causing less than optimal per- formance of the device. The longer latencies are normal and are due to link wake time coming out of low power state which happens frequently to save power when the link is not active. The following code will make exception for certain features of ASPM to be by passed and keep the logic normal state only when the ISOC device is connected and active. This change will allow the device to run at optimal performance yet minimize the impact on overall power savings. Signed-off-by: Alex He Acked-by: David Brownell Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 8 +++++ drivers/usb/host/ehci-pci.c | 32 ++++++++++++++++++ drivers/usb/host/ehci-sched.c | 79 +++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/ehci.h | 1 + 4 files changed, 120 insertions(+) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index abe8336ebffa..6778fbcf2416 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -114,6 +114,9 @@ MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us\n"); #define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) +/* for ASPM quirk of ISOC on AMD SB800 */ +static struct pci_dev *amd_nb_dev; + /*-------------------------------------------------------------------------*/ #include "ehci.h" @@ -529,6 +532,11 @@ static void ehci_stop (struct usb_hcd *hcd) spin_unlock_irq (&ehci->lock); ehci_mem_cleanup (ehci); + if (amd_nb_dev) { + pci_dev_put(amd_nb_dev); + amd_nb_dev = NULL; + } + #ifdef EHCI_STATS ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld (lost %ld)\n", ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim, diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 56c78e93440f..35a533e4c01d 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -44,6 +44,35 @@ static int ehci_pci_reinit(struct ehci_hcd *ehci, struct pci_dev *pdev) return 0; } +static int ehci_quirk_amd_SB800(struct ehci_hcd *ehci) +{ + struct pci_dev *amd_smbus_dev; + u8 rev = 0; + + amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL); + if (!amd_smbus_dev) + return 0; + + pci_read_config_byte(amd_smbus_dev, PCI_REVISION_ID, &rev); + if (rev < 0x40) { + pci_dev_put(amd_smbus_dev); + amd_smbus_dev = NULL; + return 0; + } + + if (!amd_nb_dev) + amd_nb_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1510, NULL); + if (!amd_nb_dev) + ehci_err(ehci, "QUIRK: unable to get AMD NB device\n"); + + ehci_info(ehci, "QUIRK: Enable AMD SB800 L1 fix\n"); + + pci_dev_put(amd_smbus_dev); + amd_smbus_dev = NULL; + + return 1; +} + /* called during probe() after chip reset completes */ static int ehci_pci_setup(struct usb_hcd *hcd) { @@ -102,6 +131,9 @@ static int ehci_pci_setup(struct usb_hcd *hcd) /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + if (ehci_quirk_amd_SB800(ehci)) + ehci->amd_l1_fix = 1; + retval = ehci_halt(ehci); if (retval) return retval; diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index a92526d6e5ae..724ba7133c4f 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -1583,6 +1583,63 @@ itd_link (struct ehci_hcd *ehci, unsigned frame, struct ehci_itd *itd) *hw_p = cpu_to_hc32(ehci, itd->itd_dma | Q_TYPE_ITD); } +#define AB_REG_BAR_LOW 0xe0 +#define AB_REG_BAR_HIGH 0xe1 +#define AB_INDX(addr) ((addr) + 0x00) +#define AB_DATA(addr) ((addr) + 0x04) +#define NB_PCIE_INDX_ADDR 0xe0 +#define NB_PCIE_INDX_DATA 0xe4 +#define NB_PIF0_PWRDOWN_0 0x01100012 +#define NB_PIF0_PWRDOWN_1 0x01100013 + +static void ehci_quirk_amd_L1(struct ehci_hcd *ehci, int disable) +{ + u32 addr, addr_low, addr_high, val; + + outb_p(AB_REG_BAR_LOW, 0xcd6); + addr_low = inb_p(0xcd7); + outb_p(AB_REG_BAR_HIGH, 0xcd6); + addr_high = inb_p(0xcd7); + addr = addr_high << 8 | addr_low; + outl_p(0x30, AB_INDX(addr)); + outl_p(0x40, AB_DATA(addr)); + outl_p(0x34, AB_INDX(addr)); + val = inl_p(AB_DATA(addr)); + + if (disable) { + val &= ~0x8; + val |= (1 << 4) | (1 << 9); + } else { + val |= 0x8; + val &= ~((1 << 4) | (1 << 9)); + } + outl_p(val, AB_DATA(addr)); + + if (amd_nb_dev) { + addr = NB_PIF0_PWRDOWN_0; + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr); + pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val); + if (disable) + val &= ~(0x3f << 7); + else + val |= 0x3f << 7; + + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val); + + addr = NB_PIF0_PWRDOWN_1; + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_ADDR, addr); + pci_read_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, &val); + if (disable) + val &= ~(0x3f << 7); + else + val |= 0x3f << 7; + + pci_write_config_dword(amd_nb_dev, NB_PCIE_INDX_DATA, val); + } + + return; +} + /* fit urb's itds into the selected schedule slot; activate as needed */ static int itd_link_urb ( @@ -1609,6 +1666,12 @@ itd_link_urb ( urb->interval, next_uframe >> 3, next_uframe & 0x7); } + + if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { + if (ehci->amd_l1_fix == 1) + ehci_quirk_amd_L1(ehci, 1); + } + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill iTDs uframe by uframe */ @@ -1733,6 +1796,11 @@ itd_complete ( (void) disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { + if (ehci->amd_l1_fix == 1) + ehci_quirk_amd_L1(ehci, 0); + } + if (unlikely(list_is_singular(&stream->td_list))) { ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; @@ -2018,6 +2086,12 @@ sitd_link_urb ( (next_uframe >> 3) & (ehci->periodic_size - 1), stream->interval, hc32_to_cpu(ehci, stream->splits)); } + + if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { + if (ehci->amd_l1_fix == 1) + ehci_quirk_amd_L1(ehci, 1); + } + ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++; /* fill sITDs frame by frame */ @@ -2118,6 +2192,11 @@ sitd_complete ( (void) disable_periodic(ehci); ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs--; + if (ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs == 0) { + if (ehci->amd_l1_fix == 1) + ehci_quirk_amd_L1(ehci, 0); + } + if (list_is_singular(&stream->td_list)) { ehci_to_hcd(ehci)->self.bandwidth_allocated -= stream->bandwidth; diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index bde823f704e9..fd1c53da89e4 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -130,6 +130,7 @@ struct ehci_hcd { /* one per controller */ unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; unsigned broken_periodic:1; + unsigned amd_l1_fix:1; unsigned fs_i_thresh:1; /* Intel iso scheduling */ /* required for usb32 quirk */ -- cgit v1.2.3 From b0848aea10da186372582f33152efdda43944f26 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Tue, 7 Dec 2010 17:53:56 +0530 Subject: USB: EHCI: Add MSM Host Controller driver This patch adds support for EHCI compliant HSUSB Host controller found on MSM chips. The root hub has a single port and TT is built into it. This driver depends on OTG driver for PHY initialization, clock management and powering up VBUS. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Kconfig | 1 + drivers/usb/host/Kconfig | 11 ++ drivers/usb/host/ehci-hcd.c | 5 + drivers/usb/host/ehci-msm.c | 290 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 307 insertions(+) create mode 100644 drivers/usb/host/ehci-msm.c (limited to 'drivers/usb/host') diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 6585f0b67801..b8e70a982fd4 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -64,6 +64,7 @@ config USB_ARCH_HAS_EHCI default y if ARCH_OMAP3 default y if ARCH_VT8500 default y if PLAT_SPEAR + default y if ARCH_MSM default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6a7c688b4781..d8665ec0565b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -141,6 +141,17 @@ config USB_EHCI_HCD_OMAP Enables support for the on-chip EHCI controller on OMAP3 and later chips. +config USB_EHCI_MSM + bool "Support for MSM on-chip EHCI USB controller" + depends on USB_EHCI_HCD && ARCH_MSM + select USB_EHCI_ROOT_HUB_TT + select USB_MSM_OTG_72K + ---help--- + Enables support for the USB Host controller present on the + Qualcomm chipsets. Root Hub has inbuilt TT. + This driver depends on OTG driver for PHY initialization, + clock management, powering up VBUS. + config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" depends on USB_EHCI_HCD && PPC_OF diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6778fbcf2416..48021d26024c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1239,6 +1239,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER spear_ehci_hcd_driver #endif +#ifdef CONFIG_USB_EHCI_MSM +#include "ehci-msm.c" +#define PLATFORM_DRIVER ehci_msm_driver +#endif + #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ !defined(XILINX_OF_PLATFORM_DRIVER) diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c new file mode 100644 index 000000000000..9ed855986599 --- /dev/null +++ b/drivers/usb/host/ehci-msm.c @@ -0,0 +1,290 @@ +/* ehci-msm.c - HSUSB Host Controller Driver Implementation + * + * Copyright (c) 2008-2010, Code Aurora Forum. All rights reserved. + * + * Partly derived from ehci-fsl.c and ehci-hcd.c + * Copyright (c) 2000-2004 by David Brownell + * Copyright (c) 2005 MontaVista Software + * + * All source code in this file is licensed under the following license except + * where indicated. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * See the GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, you can find it at http://www.fsf.org + */ + +#include +#include +#include + +#include +#include + +#define MSM_USB_BASE (hcd->regs) + +static struct otg_transceiver *otg; + +/* + * ehci_run defined in drivers/usb/host/ehci-hcd.c reset the controller and + * the configuration settings in ehci_msm_reset vanish after controller is + * reset. Resetting the controler in ehci_run seems to be un-necessary + * provided HCD reset the controller before calling ehci_run. Most of the HCD + * do but some are not. So this function is same as ehci_run but we don't + * reset the controller here. + */ +static int ehci_msm_run(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 temp; + u32 hcc_params; + + hcd->uses_new_polling = 1; + + ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); + ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next); + + /* + * hcc_params controls whether ehci->regs->segment must (!!!) + * be used; it constrains QH/ITD/SITD and QTD locations. + * pci_pool consistent memory always uses segment zero. + * streaming mappings for I/O buffers, like pci_map_single(), + * can return segments above 4GB, if the device allows. + * + * NOTE: the dma mask is visible through dma_supported(), so + * drivers can pass this info along ... like NETIF_F_HIGHDMA, + * Scsi_Host.highmem_io, and so forth. It's readonly to all + * host side drivers though. + */ + hcc_params = ehci_readl(ehci, &ehci->caps->hcc_params); + if (HCC_64BIT_ADDR(hcc_params)) + ehci_writel(ehci, 0, &ehci->regs->segment); + + /* + * Philips, Intel, and maybe others need CMD_RUN before the + * root hub will detect new devices (why?); NEC doesn't + */ + ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + dbg_cmd(ehci, "init", ehci->command); + + /* + * Start, enabling full USB 2.0 functionality ... usb 1.1 devices + * are explicitly handed to companion controller(s), so no TT is + * involved with the root hub. (Except where one is integrated, + * and there's no companion controller unless maybe for USB OTG.) + * + * Turning on the CF flag will transfer ownership of all ports + * from the companions to the EHCI controller. If any of the + * companions are in the middle of a port reset at the time, it + * could cause trouble. Write-locking ehci_cf_port_reset_rwsem + * guarantees that no resets are in progress. After we set CF, + * a short delay lets the hardware catch up; new resets shouldn't + * be started before the port switching actions could complete. + */ + down_write(&ehci_cf_port_reset_rwsem); + hcd->state = HC_STATE_RUNNING; + ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag); + ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ + usleep_range(5000, 5500); + up_write(&ehci_cf_port_reset_rwsem); + ehci->last_periodic_enable = ktime_get_real(); + + temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); + ehci_info(ehci, + "USB %x.%x started, EHCI %x.%02x%s\n", + ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), + temp >> 8, temp & 0xff, + ignore_oc ? ", overcurrent ignored" : ""); + + ehci_writel(ehci, INTR_MASK, + &ehci->regs->intr_enable); /* Turn On Interrupts */ + + /* GRR this is run-once init(), being done every time the HC starts. + * So long as they're part of class devices, we can't do it init() + * since the class device isn't created that early. + */ + create_debug_files(ehci); + create_companion_file(ehci); + + return 0; +} + +static int ehci_msm_reset(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + ehci->caps = USB_CAPLENGTH; + ehci->regs = USB_CAPLENGTH + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache the data to minimize the chip reads*/ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + hcd->has_tt = 1; + ehci->sbrn = HCD_USB2; + + /* data structure init */ + retval = ehci_init(hcd); + if (retval) + return retval; + + retval = ehci_reset(ehci); + if (retval) + return retval; + + /* bursts of unspecified length. */ + writel(0, USB_AHBBURST); + /* Use the AHB transactor */ + writel(0, USB_AHBMODE); + /* Disable streaming mode and select host mode */ + writel(0x13, USB_USBMODE); + + ehci_port_power(ehci, 1); + return 0; +} + +static struct hc_driver msm_hc_driver = { + .description = hcd_name, + .product_desc = "Qualcomm On-Chip EHCI Host Controller", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_USB2 | HCD_MEMORY, + + .reset = ehci_msm_reset, + .start = ehci_msm_run, + + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + /* + * PM support + */ + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +}; + +static int ehci_msm_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + int ret; + + dev_dbg(&pdev->dev, "ehci_msm proble\n"); + + hcd = usb_create_hcd(&msm_hc_driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + dev_err(&pdev->dev, "Unable to create HCD\n"); + return -ENOMEM; + } + + hcd->irq = platform_get_irq(pdev, 0); + if (hcd->irq < 0) { + dev_err(&pdev->dev, "Unable to get IRQ resource\n"); + ret = hcd->irq; + goto put_hcd; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get memory resource\n"); + ret = -ENODEV; + goto put_hcd; + } + + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENOMEM; + goto put_hcd; + } + + /* + * OTG driver takes care of PHY initialization, clock management, + * powering up VBUS and mapping of registers address space. + */ + otg = otg_get_transceiver(); + if (!otg) { + dev_err(&pdev->dev, "unable to find transceiver\n"); + ret = -ENODEV; + goto unmap; + } + + ret = otg_set_host(otg, &hcd->self); + if (ret < 0) { + dev_err(&pdev->dev, "unable to register with transceiver\n"); + goto put_transceiver; + } + + device_init_wakeup(&pdev->dev, 1); + return 0; + +put_transceiver: + otg_put_transceiver(otg); +unmap: + iounmap(hcd->regs); +put_hcd: + usb_put_hcd(hcd); + + return ret; +} + +static int __devexit ehci_msm_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + + otg_set_host(otg, NULL); + otg_put_transceiver(otg); + + usb_put_hcd(hcd); + + return 0; +} + +static struct platform_driver ehci_msm_driver = { + .probe = ehci_msm_probe, + .remove = __devexit_p(ehci_msm_remove), + .driver = { + .name = "msm_hsusb_host", + }, +}; -- cgit v1.2.3 From 8bb6a164b906bb7ca319202f85b30e3ef096cd65 Mon Sep 17 00:00:00 2001 From: Pavankumar Kondeti Date: Tue, 7 Dec 2010 17:53:57 +0530 Subject: USB: EHCI: msm: Add support for power management Enable runtime PM and mark no_callbacks flag. OTG device, parent of HCD takes care of putting hardware into low power mode. Adjust port power wakeup flags during system suspend and resume. Signed-off-by: Pavankumar Kondeti Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/Kconfig | 2 +- drivers/usb/host/ehci-msm.c | 57 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d8665ec0565b..b9cc31172fbd 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -150,7 +150,7 @@ config USB_EHCI_MSM Enables support for the USB Host controller present on the Qualcomm chipsets. Root Hub has inbuilt TT. This driver depends on OTG driver for PHY initialization, - clock management, powering up VBUS. + clock management, powering up VBUS, and power management. config USB_EHCI_HCD_PPC_OF bool "EHCI support for PPC USB controller on OF platform bus" diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9ed855986599..413f4deca532 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -239,7 +240,8 @@ static int ehci_msm_probe(struct platform_device *pdev) /* * OTG driver takes care of PHY initialization, clock management, - * powering up VBUS and mapping of registers address space. + * powering up VBUS, mapping of registers address space and power + * management. */ otg = otg_get_transceiver(); if (!otg) { @@ -255,6 +257,13 @@ static int ehci_msm_probe(struct platform_device *pdev) } device_init_wakeup(&pdev->dev, 1); + /* + * OTG device parent of HCD takes care of putting + * hardware into low power mode. + */ + pm_runtime_no_callbacks(&pdev->dev); + pm_runtime_enable(&pdev->dev); + return 0; put_transceiver: @@ -272,6 +281,8 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) struct usb_hcd *hcd = platform_get_drvdata(pdev); device_init_wakeup(&pdev->dev, 0); + pm_runtime_disable(&pdev->dev); + pm_runtime_set_suspended(&pdev->dev); otg_set_host(otg, NULL); otg_put_transceiver(otg); @@ -281,10 +292,54 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM +static int ehci_msm_pm_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + bool wakeup = device_may_wakeup(dev); + + dev_dbg(dev, "ehci-msm PM suspend\n"); + + /* + * EHCI helper function has also the same check before manipulating + * port wakeup flags. We do check here the same condition before + * calling the same helper function to avoid bringing hardware + * from Low power mode when there is no need for adjusting port + * wakeup flags. + */ + if (hcd->self.root_hub->do_remote_wakeup && !wakeup) { + pm_runtime_resume(dev); + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), + wakeup); + } + + return 0; +} + +static int ehci_msm_pm_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + dev_dbg(dev, "ehci-msm PM resume\n"); + ehci_prepare_ports_for_controller_resume(hcd_to_ehci(hcd)); + + return 0; +} +#else +#define ehci_msm_pm_suspend NULL +#define ehci_msm_pm_resume NULL +#endif + +static const struct dev_pm_ops ehci_msm_dev_pm_ops = { + .suspend = ehci_msm_pm_suspend, + .resume = ehci_msm_pm_resume, +}; + static struct platform_driver ehci_msm_driver = { .probe = ehci_msm_probe, .remove = __devexit_p(ehci_msm_remove), .driver = { .name = "msm_hsusb_host", + .pm = &ehci_msm_dev_pm_ops, }, }; -- cgit v1.2.3 From a0c9e95dfc6898dbc178d7b962916f3823434e6e Mon Sep 17 00:00:00 2001 From: Keshava Munegowda Date: Mon, 13 Dec 2010 22:00:51 +0530 Subject: usb: ohci-omap3: fix trivial typo This is the ohci-omap3 driver, not ehci-omap. Correct this obvious typo. Signed-off-by: Keshava Munegowda Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ohci-omap3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ohci-omap3.c b/drivers/usb/host/ohci-omap3.c index 2cc8a504b18c..a37d5993e4e3 100644 --- a/drivers/usb/host/ohci-omap3.c +++ b/drivers/usb/host/ohci-omap3.c @@ -648,7 +648,7 @@ static int __devinit ohci_hcd_omap3_probe(struct platform_device *pdev) ret = omap3_start_ohci(omap, hcd); if (ret) { - dev_dbg(&pdev->dev, "failed to start ehci\n"); + dev_dbg(&pdev->dev, "failed to start ohci\n"); goto err_start; } -- cgit v1.2.3 From 6ef9fc657bd69c708ada2ee9fd3b2e13f7600a3d Mon Sep 17 00:00:00 2001 From: Anand Gadiyar Date: Mon, 13 Dec 2010 22:00:34 +0530 Subject: usb: ehci-omap: fix tll channel enable mask The TLL channel enable code searches for the wrong mask, and could end up enabling the wrong port. Fix this. Signed-off-by: Anand Gadiyar Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-omap.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 0374eb47f09b..680f2ef4e59f 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -90,9 +90,9 @@ #define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) #define OMAP_TLL_CHANNEL_COUNT 3 -#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 1) -#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 2) -#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 4) +#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 0) +#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 1) +#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 2) /* UHH Register Set */ #define OMAP_UHH_REVISION (0x00) -- cgit v1.2.3 From 0247a7bcd4273fa10c4aba9b3f567c659bab2d2b Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 15 Dec 2010 22:31:28 -0200 Subject: USB: ehci-mxc: Setup portsc register prior to accessing OTG viewport In order to read/write to the i.MX OTG viewport register it is necessary to setup the PORTSCx register first. By default i.MX OTG port is configured for USB serial PHY. In order to use a ULPI PHY the PORTSCx register needs to be configured properly. commit 724c852 (USB: ehci/mxc: compile fix) placed the PORTSC setup after the OTG viewport is accessed and this causes ULPI read/write to fail. Revert the PORTSC setup order. Tested on a MX31PDK board with a ISP1504 transceiver: ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver mxc-ehci mxc-ehci.0: initializing i.MX USB Controller ULPI transceiver vendor/product ID 0x04cc/0x1504 Found NXP ISP1504 ULPI transceiver. ULPI integrity check: passed. Signed-off-by: Fabio Estevam Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-mxc.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'drivers/usb/host') diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index f6e5d44c06b6..535dcae7949f 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -36,14 +36,8 @@ struct ehci_mxc_priv { static int ehci_mxc_setup(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); - struct device *dev = hcd->self.controller; - struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev); int retval; - /* EHCI registers start at offset 0x100 */ - ehci->caps = hcd->regs + 0x100; - ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); @@ -65,12 +59,6 @@ static int ehci_mxc_setup(struct usb_hcd *hcd) ehci_reset(ehci); - /* set up the PORTSCx register */ - ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); - - /* is this really needed? */ - msleep(10); - ehci_port_power(ehci, 0); return 0; } @@ -128,6 +116,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) int irq, ret; struct ehci_mxc_priv *priv; struct device *dev = &pdev->dev; + struct ehci_hcd *ehci; dev_info(&pdev->dev, "initializing i.MX USB Controller\n"); @@ -204,6 +193,19 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) if (ret < 0) goto err_init; + ehci = hcd_to_ehci(hcd); + + /* EHCI registers start at offset 0x100 */ + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* set up the PORTSCx register */ + ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); + + /* is this really needed? */ + msleep(10); + /* Initialize the transceiver */ if (pdata->otg) { pdata->otg->io_priv = hcd->regs + ULPI_VIEWPORT_OFFSET; -- cgit v1.2.3