aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-langwell-pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ehci-langwell-pci.c')
-rw-r--r--drivers/usb/host/ehci-langwell-pci.c195
1 files changed, 195 insertions, 0 deletions
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,
+};