aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c10
-rw-r--r--drivers/usb/core/usb.h1
-rw-r--r--drivers/usb/host/ehci-hcd.c19
-rw-r--r--drivers/usb/host/ehci-hub.c13
-rw-r--r--drivers/usb/host/ehci-langwell-pci.c195
-rw-r--r--drivers/usb/host/ehci-pci.c15
-rw-r--r--drivers/usb/host/ehci.h6
7 files changed, 256 insertions, 3 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 27115b45edc..4e14c00c6f6 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1549,6 +1549,14 @@ static void hub_free_dev(struct usb_device *udev)
hcd->driver->free_dev(hcd, udev);
}
+static void otg_notify(struct usb_device *udev, unsigned action)
+{
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+ if (hcd->otg_notify)
+ hcd->otg_notify(udev, action);
+}
+
/**
* usb_disconnect - disconnect a device (usbcore-internal)
* @pdev: pointer to device being disconnected
@@ -1606,6 +1614,7 @@ void usb_disconnect(struct usb_device **pdev)
* notifier chain (used by usbfs and possibly others).
*/
device_del(&udev->dev);
+ otg_notify(udev, USB_DEVICE_REMOVE);
/* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
@@ -1829,6 +1838,7 @@ int usb_new_device(struct usb_device *udev)
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
}
+ otg_notify(udev, USB_DEVICE_ADD);
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
return err;
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index cd882203ad3..12276501064 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -147,4 +147,3 @@ extern void usb_notify_add_device(struct usb_device *udev);
extern void usb_notify_remove_device(struct usb_device *udev);
extern void usb_notify_add_bus(struct usb_bus *ubus);
extern void usb_notify_remove_bus(struct usb_bus *ubus);
-
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index e9062806d4a..c59361e6e91 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -43,6 +43,8 @@
#include <asm/irq.h>
#include <asm/system.h>
#include <asm/unaligned.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/langwell_otg.h>
/*-------------------------------------------------------------------------*/
@@ -1154,6 +1156,10 @@ MODULE_LICENSE ("GPL");
#ifdef CONFIG_PCI
#include "ehci-pci.c"
#define PCI_DRIVER ehci_pci_driver
+#ifdef CONFIG_USB_LANGWELL_OTG
+#include "ehci-langwell-pci.c"
+#define LNW_OTG_HOST_DRIVER ehci_otg_driver
+#endif
#endif
#ifdef CONFIG_USB_EHCI_FSL
@@ -1278,8 +1284,18 @@ static int __init ehci_hcd_init(void)
if (retval < 0)
goto clean4;
#endif
+
+#ifdef LNW_OTG_HOST_DRIVER
+ retval = langwell_register_host(&LNW_OTG_HOST_DRIVER);
+ if (retval < 0)
+ goto clean5;
+#endif
return retval;
+#ifdef LNW_OTG_HOST_DRIVER
+clean5:
+ langwell_unregister_host(&LNW_OTG_HOST_DRIVER);
+#endif
#ifdef XILINX_OF_PLATFORM_DRIVER
/* of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER); */
clean4:
@@ -1312,6 +1328,9 @@ module_init(ehci_hcd_init);
static void __exit ehci_hcd_cleanup(void)
{
+#ifdef LNW_OTG_HOST_DRIVER
+ langwell_unregister_host(&LNW_OTG_HOST_DRIVER);
+#endif
#ifdef XILINX_OF_PLATFORM_DRIVER
of_unregister_platform_driver(&XILINX_OF_PLATFORM_DRIVER);
#endif
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 796ea0c8900..86f081548fa 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -178,6 +178,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
int port;
int mask;
int changed;
+ int rc = 0;
ehci_dbg(ehci, "suspend root hub\n");
@@ -297,13 +298,17 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
ehci_readl(ehci, &ehci->regs->intr_enable);
ehci->next_statechange = jiffies + msecs_to_jiffies(10);
+
+ if (ehci->has_otg && ehci->otg_suspend)
+ rc = ehci->otg_suspend(hcd);
+
spin_unlock_irq (&ehci->lock);
/* ehci_work() may have re-enabled the watchdog timer, which we do not
* want, and so we must delete any pending watchdog timer events.
*/
del_timer_sync(&ehci->watchdog);
- return 0;
+ return rc;
}
@@ -315,6 +320,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
u32 power_okay;
int i;
u8 resume_needed = 0;
+ int rc = 0;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
@@ -428,9 +434,12 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* Now we can safely re-enable irqs */
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
+ if (ehci->has_otg && ehci->otg_resume)
+ rc = ehci->otg_resume(hcd);
+
spin_unlock_irq (&ehci->lock);
ehci_handover_companion_ports(ehci);
- return 0;
+ return rc;
}
#else
diff --git a/drivers/usb/host/ehci-langwell-pci.c b/drivers/usb/host/ehci-langwell-pci.c
new file mode 100644
index 00000000000..64ffd6020e3
--- /dev/null
+++ b/drivers/usb/host/ehci-langwell-pci.c
@@ -0,0 +1,195 @@
+/*
+ * Intel Moorestown Platform Langwell OTG EHCI Controller PCI Bus Glue.
+ *
+ * Copyright (c) 2008 - 2009, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License 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, write to the Free Software Foundation,
+ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+static int usb_otg_suspend(struct usb_hcd *hcd)
+{
+ struct otg_transceiver *otg;
+ struct langwell_otg *iotg;
+
+ otg = otg_get_transceiver();
+ if (otg == NULL) {
+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__);
+ return -EINVAL;
+ }
+ iotg = container_of(otg, struct langwell_otg, otg);
+ printk(KERN_INFO "%s OTG HNP update suspend\n", __func__);
+ if (iotg->otg.default_a)
+ iotg->hsm.a_suspend_req = 1;
+ else
+ iotg->hsm.b_bus_req = 0;
+ langwell_update_transceiver();
+ otg_put_transceiver(otg);
+ return 0;
+}
+
+static int usb_otg_resume(struct usb_hcd *hcd)
+{
+ struct otg_transceiver *otg;
+ struct langwell_otg *iotg;
+
+ otg = otg_get_transceiver();
+ if (otg == NULL) {
+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__);
+ return -EINVAL;
+ }
+ iotg = container_of(otg, struct langwell_otg, otg);
+ printk(KERN_INFO "%s OTG HNP update resume\n", __func__);
+ if (iotg->otg.default_a) {
+ iotg->hsm.b_bus_resume = 1;
+ langwell_update_transceiver();
+ }
+ otg_put_transceiver(otg);
+ return 0;
+}
+
+/* the root hub will call this callback when device added/removed */
+static void otg_notify(struct usb_device *udev, unsigned action)
+{
+ struct otg_transceiver *otg;
+ struct langwell_otg *iotg;
+
+ otg = otg_get_transceiver();
+ if (otg == NULL) {
+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__);
+ return;
+ }
+ iotg = container_of(otg, struct langwell_otg, otg);
+
+ switch (action) {
+ case USB_DEVICE_ADD:
+ pr_debug("Notify OTG HNP add device\n");
+ if (iotg->otg.default_a == 1)
+ iotg->hsm.b_conn = 1;
+ else
+ iotg->hsm.a_conn = 1;
+ break;
+ case USB_DEVICE_REMOVE:
+ pr_debug("Notify OTG HNP delete device\n");
+ if (iotg->otg.default_a == 1)
+ iotg->hsm.b_conn = 0;
+ else
+ iotg->hsm.a_conn = 0;
+ break;
+ default:
+ otg_put_transceiver(otg);
+ return ;
+ }
+ if (spin_trylock(&iotg->wq_lock)) {
+ langwell_update_transceiver();
+ spin_unlock(&iotg->wq_lock);
+ }
+ otg_put_transceiver(otg);
+ return;
+}
+
+static int ehci_langwell_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ struct hc_driver *driver;
+ struct langwell_otg *iotg;
+ struct otg_transceiver *otg;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ int irq;
+ int retval;
+
+ pr_debug("initializing Langwell USB OTG Host Controller\n");
+
+ /* we need not call pci_enable_dev since otg transceiver already take
+ * the control of this device and this probe actaully gets called by
+ * otg transceiver driver with HNP protocol.
+ */
+ irq = pdev->irq;
+
+ if (!id)
+ return -EINVAL;
+ driver = (struct hc_driver *)id->driver_data;
+ if (!driver)
+ return -EINVAL;
+
+ hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd) {
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ hcd->self.otg_port = 1;
+ ehci = hcd_to_ehci(hcd);
+ /* this will be called in ehci_bus_suspend and ehci_bus_resume */
+ ehci->otg_suspend = usb_otg_suspend;
+ ehci->otg_resume = usb_otg_resume;
+ /* this will be called by root hub code */
+ hcd->otg_notify = otg_notify;
+ otg = otg_get_transceiver();
+ if (otg == NULL) {
+ printk(KERN_ERR "%s Failed to get otg transceiver\n", __func__);
+ retval = -EINVAL;
+ goto err1;
+ }
+ iotg = container_of(otg, struct langwell_otg, otg);
+ hcd->regs = iotg->regs;
+ hcd->rsrc_start = pci_resource_start(pdev, 0);
+ hcd->rsrc_len = pci_resource_len(pdev, 0);
+
+ if (hcd->regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ retval = -EFAULT;
+ goto err2;
+ }
+ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (retval != 0)
+ goto err2;
+ retval = otg_set_host(otg, &hcd->self);
+ if (!otg->default_a)
+ hcd->self.is_b_host = 1;
+ otg_put_transceiver(otg);
+ return retval;
+
+err2:
+ usb_put_hcd(hcd);
+err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
+ return retval;
+}
+
+void ehci_langwell_remove(struct pci_dev *dev)
+{
+ struct usb_hcd *hcd = pci_get_drvdata(dev);
+
+ if (!hcd)
+ return;
+ usb_remove_hcd(hcd);
+ usb_put_hcd(hcd);
+}
+
+/* Langwell OTG EHCI driver */
+static struct pci_driver ehci_otg_driver = {
+ .name = "ehci-langwell",
+ .id_table = pci_ids,
+
+ .probe = ehci_langwell_probe,
+ .remove = ehci_langwell_remove,
+
+#ifdef CONFIG_PM_SLEEP
+ .driver = {
+ .pm = &usb_hcd_pci_pm_ops
+ },
+#endif
+ .shutdown = usb_hcd_pci_shutdown,
+};
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index 655f3c9f88b..c3637bb299c 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -50,6 +50,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
u8 rev;
u32 temp;
int retval;
+ int force_otg_hc_mode = 0;
switch (pdev->vendor) {
case PCI_VENDOR_ID_TOSHIBA_2:
@@ -63,6 +64,18 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
#endif
}
break;
+ case PCI_VENDOR_ID_INTEL:
+ if (pdev->device == 0x0811) {
+ ehci_info(ehci, "Detected Langwell OTG HC\n");
+ hcd->has_tt = 1;
+ ehci->has_hostpc = 1;
+ ehci->has_otg = 1;
+ force_otg_hc_mode = 1;
+ } else if (pdev->device == 0x0806) {
+ ehci_info(ehci, "Detected Langwell MPH\n");
+ hcd->has_tt = 1;
+ ehci->has_hostpc = 1;
+ }
}
ehci->caps = hcd->regs;
@@ -98,6 +111,8 @@ 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 (force_otg_hc_mode)
+ ehci_reset(ehci);
retval = ehci_halt(ehci);
if (retval)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index ba8eab366b8..d78a781a49b 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -145,6 +145,8 @@ struct ehci_hcd { /* one per controller */
unsigned has_hostpc:1;
unsigned has_lpm:1; /* support link power management */
unsigned has_ppcd:1; /* support per-port change bits */
+
+ unsigned has_otg:1; /* if it is otg host*/
u8 sbrn; /* packed release number */
/* irq statistics */
@@ -155,6 +157,10 @@ struct ehci_hcd { /* one per controller */
# define COUNT(x) do {} while (0)
#endif
+ /* otg host has additional bus_suspend and bus_resume */
+ int (*otg_suspend)(struct usb_hcd *hcd);
+ int (*otg_resume)(struct usb_hcd *hcd);
+
/* debug files */
#ifdef DEBUG
struct dentry *debug_dir;