aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBhuvana Kakunoori <bhuvana.kakunoori@linaro.org>2011-08-05 10:56:35 +0530
committerAngus Ainslie <angus.ainslie@linaro.org>2011-08-18 17:15:37 -0600
commit2059b112e6dca256a8aa55bbef97facd169d10ac (patch)
tree994668a8680cc9f778fda9824dec27582e0a744b
parent03fe1bf5d1edb64166d77252a891d79698f8e2fc (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/Kconfig1
-rw-r--r--drivers/usb/host/Kconfig6
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/ohci-s5p.c245
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");