diff options
author | Bhuvana Kakunoori <bhuvana.kakunoori@linaro.org> | 2011-08-05 10:56:35 +0530 |
---|---|---|
committer | Angus Ainslie <angus.ainslie@linaro.org> | 2011-08-18 17:15:37 -0600 |
commit | 2059b112e6dca256a8aa55bbef97facd169d10ac (patch) | |
tree | 994668a8680cc9f778fda9824dec27582e0a744b | |
parent | 03fe1bf5d1edb64166d77252a891d79698f8e2fc (diff) |
SAMSUNG: USB: Add support for S5P OHCI driver
This patch adds support for OHCI driver for samsung S5P series.
Signed-off-by: Bhuvana Kakunoori <bhuvana.kakunoori@linaro.org>
Signed-off-by: Sachin Kamat <sachin.kamat@linaro.org>
-rw-r--r-- | drivers/usb/Kconfig | 1 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ohci-s5p.c | 245 |
4 files changed, 257 insertions, 0 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 48f1781352f1..12c7ada927f8 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -42,6 +42,7 @@ config USB_ARCH_HAS_OHCI default y if ARCH_DAVINCI_DA8XX default y if ARCH_CNS3XXX default y if PLAT_SPEAR + default y if PLAT_S5P # PPC: default y if STB03xxx default y if PPC_MPC52xx diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index ab085f12d570..6d6ce0031ac9 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -578,3 +578,9 @@ config USB_OCTEON_OHCI config USB_OCTEON2_COMMON bool default y if USB_OCTEON_EHCI || USB_OCTEON_OHCI + +config USB_OHCI_S5P + boolean "S5P OHCI support" + depends on USB_OHCI_HCD && PLAT_S5P + help + Enable support for the S5P SOC's on-chip OHCI controller. diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index f9cf3f04b742..f5a45459ff97 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1114,6 +1114,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_ath79_driver #endif +#ifdef CONFIG_ARCH_EXYNOS4 +#include "ohci-s5p.c" +#define PLATFORM_DRIVER ohci_hcd_s5pv210_driver +#endif + #if !defined(PCI_DRIVER) && \ !defined(PLATFORM_DRIVER) && \ !defined(OMAP1_PLATFORM_DRIVER) && \ diff --git a/drivers/usb/host/ohci-s5p.c b/drivers/usb/host/ohci-s5p.c new file mode 100644 index 000000000000..57c00d70de4a --- /dev/null +++ b/drivers/usb/host/ohci-s5p.c @@ -0,0 +1,245 @@ +/* ohci-s5pv210.c - Driver for USB HOST on Samsung S5PV210 processor + * + * Bus Glue for SAMSUNG S5PV210 USB HOST OHCI Controller + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> + * (C) Copyright 2002 Hewlett-Packard Company + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * Author: Jingoo Han <jg1.han@samsung.com> + * + * Based on "ohci-au1xxx.c" by Matt Porter <mporter@kernel.crashing.org> + * Modified for SAMSUNG s5pv210 OHCI by Jingoo Han <jg1.han@samsung.com> + * + * 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. +*/ + +#include <linux/platform_device.h> +#include <plat/ohci.h> +#include <plat/usb-phy.h> + + + +static int ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev); +static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev); + +#ifdef CONFIG_PM +static int ohci_hcd_s5pv210_drv_suspend( + struct platform_device *pdev, + pm_message_t message +){ + struct s5p_ohci_platdata *pdata; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + unsigned long flags; + int rc = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + /* Root hub was already suspended. Disable irq emission and + * mark HW unaccessible, bail out if RH has been resumed. Use + * the spinlock to properly synchronize with possible pending + * RH suspend or resume activity. + * + * This is still racy as hcd->state is manipulated outside of + * any locks =P But that will be a different fix. + */ + spin_lock_irqsave(&ohci->lock, flags); + if (hcd->state != HC_STATE_SUSPENDED) { + rc = -EINVAL; + goto bail; + } + + /* make sure snapshot being resumed re-enumerates everything */ + if (message.event == PM_EVENT_PRETHAW) + ohci_usb_reset(ohci); + + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); +bail: + spin_unlock_irqrestore(&ohci->lock, flags); + + return rc; +} +static int ohci_hcd_s5pv210_drv_resume(struct platform_device *pdev) +{ + struct s5p_ohci_platdata *pdata; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + int rc = 0; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + if (pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + /* Mark hardware accessible again as we are out of D3 state by now */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + ohci_finish_controller_resume(hcd); + + return rc; +} +#else +#define ohci_hcd_s5pv210_drv_suspend NULL +#define ohci_hcd_s5pv210_drv_resume NULL +#endif + +static int __devinit ohci_s5pv210_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + ohci_dbg(ohci, "ohci_s5pv210_start, ohci:%p", ohci); + + ret = ohci_init(ohci); + if (ret < 0) + return ret; + + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); + return ret; + } + + return 0; +} + +static const struct hc_driver ohci_s5pv210_hc_driver = { + .description = hcd_name, + .product_desc = "s5pv210 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + .irq = ohci_irq, + .flags = HCD_MEMORY|HCD_USB11, + + .start = ohci_s5pv210_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + .get_frame_number = ohci_get_frame, + + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +static int ohci_hcd_s5pv210_drv_probe(struct platform_device *pdev) +{ + struct s5p_ohci_platdata *pdata; + struct usb_hcd *hcd = NULL; + int retval = 0; + + if (usb_disabled()) + return -ENODEV; + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + if (pdev->resource[1].flags != IORESOURCE_IRQ) { + dev_err(&pdev->dev, "resource[1] is not IORESOURCE_IRQ.\n"); + return -ENODEV; + } + + hcd = usb_create_hcd(&ohci_s5pv210_hc_driver, &pdev->dev, "s5pv210"); + if (!hcd) { + dev_err(&pdev->dev, "usb_create_hcd failed!\n"); + return -ENODEV; + } + + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = resource_size(&pdev->resource[0]); + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_err(&pdev->dev, "request_mem_region failed!\n"); + retval = -EBUSY; + goto err1; + } + + if (pdata->phy_init) + pdata->phy_init(pdev, S5P_USB_PHY_HOST); + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "ioremap failed!\n"); + retval = -ENOMEM; + goto err2; + } + + ohci_hcd_init(hcd_to_ohci(hcd)); + + retval = usb_add_hcd(hcd, pdev->resource[1].start, + IRQF_DISABLED | IRQF_SHARED); + + if (retval == 0) { + platform_set_drvdata(pdev, hcd); + return retval; + } + + if (pdata && pdata->phy_exit) + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); + return retval; +} + +static int ohci_hcd_s5pv210_drv_remove(struct platform_device *pdev) +{ + struct s5p_ohci_platdata *pdata; + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(&pdev->dev, "No platform data defined\n"); + return -EINVAL; + } + + usb_remove_hcd(hcd); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + pdata->phy_exit(pdev, S5P_USB_PHY_HOST); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver ohci_hcd_s5pv210_driver = { + .probe = ohci_hcd_s5pv210_drv_probe, + .remove = ohci_hcd_s5pv210_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .suspend = ohci_hcd_s5pv210_drv_suspend, + .resume = ohci_hcd_s5pv210_drv_resume, + .driver = { + .name = "s5p-ohci", + .owner = THIS_MODULE, + } +}; + +MODULE_ALIAS("platform:s5p-ohci"); |