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/host/ehci-hcd.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers/usb/host/ehci-hcd.c') 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 -- 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/ehci-hcd.c') 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/ehci-hcd.c') 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 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/ehci-hcd.c') 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 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/ehci-hcd.c') 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/ehci-hcd.c') 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