usb: host: xhci: add platform driver support
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Tue, 13 Mar 2012 14:57:41 +0000 (16:57 +0200)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Tue, 13 Mar 2012 17:30:59 +0000 (10:30 -0700)
This adds a fairly simple xhci-platform driver support. Currently it is
used by the dwc3 driver for supporting host mode.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/xhci-plat.c [new file with mode: 0644]
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h

index dd045173ab79670a00986b58f8cf0741377e7ce0..b1799582951452578f5371d682717c4f1cd6d7d6 100644 (file)
@@ -27,6 +27,10 @@ config USB_XHCI_HCD
          To compile this driver as a module, choose M here: the
          module will be called xhci-hcd.
 
+config USB_XHCI_PLATFORM
+       tristate
+       depends on USB_XHCI_HCD
+
 config USB_XHCI_HCD_DEBUGGING
        bool "Debugging for the xHCI host controller"
        depends on USB_XHCI_HCD
index 7ca290fcb070d07ff214c3e7d47ffbf83275a7a3..0982bcc140bd6b84dd46faa6bceca7fc6095238b 100644 (file)
@@ -15,6 +15,10 @@ xhci-hcd-y := xhci.o xhci-mem.o
 xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o
 xhci-hcd-$(CONFIG_PCI) += xhci-pci.o
 
+ifneq ($(CONFIG_USB_XHCI_PLATFORM), )
+       xhci-hcd-y              += xhci-plat.o
+endif
+
 obj-$(CONFIG_USB_WHCI_HCD)     += whci/
 
 obj-$(CONFIG_PCI)              += pci-quirks.o
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
new file mode 100644 (file)
index 0000000..689bc18
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * xhci-plat.c - xHCI host controller driver platform Bus Glue.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ *
+ * A lot of code borrowed from the Linux xHCI driver.
+ *
+ * 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 <linux/module.h>
+#include <linux/slab.h>
+
+#include "xhci.h"
+
+static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+       /*
+        * As of now platform drivers don't provide MSI support so we ensure
+        * here that the generic code does not try to make a pci_dev from our
+        * dev struct in order to setup MSI
+        */
+       xhci->quirks |= XHCI_BROKEN_MSI;
+}
+
+/* called during probe() after chip reset completes */
+static int xhci_plat_setup(struct usb_hcd *hcd)
+{
+       return xhci_gen_setup(hcd, xhci_plat_quirks);
+}
+
+static const struct hc_driver xhci_plat_xhci_driver = {
+       .description =          "xhci-hcd",
+       .product_desc =         "xHCI Host Controller",
+       .hcd_priv_size =        sizeof(struct xhci_hcd *),
+
+       /*
+        * generic hardware linkage
+        */
+       .irq =                  xhci_irq,
+       .flags =                HCD_MEMORY | HCD_USB3 | HCD_SHARED,
+
+       /*
+        * basic lifecycle operations
+        */
+       .reset =                xhci_plat_setup,
+       .start =                xhci_run,
+       .stop =                 xhci_stop,
+       .shutdown =             xhci_shutdown,
+
+       /*
+        * managing i/o requests and associated device resources
+        */
+       .urb_enqueue =          xhci_urb_enqueue,
+       .urb_dequeue =          xhci_urb_dequeue,
+       .alloc_dev =            xhci_alloc_dev,
+       .free_dev =             xhci_free_dev,
+       .alloc_streams =        xhci_alloc_streams,
+       .free_streams =         xhci_free_streams,
+       .add_endpoint =         xhci_add_endpoint,
+       .drop_endpoint =        xhci_drop_endpoint,
+       .endpoint_reset =       xhci_endpoint_reset,
+       .check_bandwidth =      xhci_check_bandwidth,
+       .reset_bandwidth =      xhci_reset_bandwidth,
+       .address_device =       xhci_address_device,
+       .update_hub_device =    xhci_update_hub_device,
+       .reset_device =         xhci_discover_or_reset_device,
+
+       /*
+        * scheduling support
+        */
+       .get_frame_number =     xhci_get_frame,
+
+       /* Root hub support */
+       .hub_control =          xhci_hub_control,
+       .hub_status_data =      xhci_hub_status_data,
+       .bus_suspend =          xhci_bus_suspend,
+       .bus_resume =           xhci_bus_resume,
+};
+
+static int xhci_plat_probe(struct platform_device *pdev)
+{
+       const struct hc_driver  *driver;
+       struct xhci_hcd         *xhci;
+       struct resource         *res;
+       struct usb_hcd          *hcd;
+       int                     ret;
+       int                     irq;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       driver = &xhci_plat_xhci_driver;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return -ENODEV;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+
+       hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       hcd->rsrc_start = res->start;
+       hcd->rsrc_len = resource_size(res);
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+                               driver->description)) {
+               dev_dbg(&pdev->dev, "controller already in use\n");
+               ret = -EBUSY;
+               goto put_hcd;
+       }
+
+       hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+       if (!hcd->regs) {
+               dev_dbg(&pdev->dev, "error mapping memory\n");
+               ret = -EFAULT;
+               goto release_mem_region;
+       }
+
+       ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
+       if (ret)
+               goto unmap_registers;
+
+       /* USB 2.0 roothub is stored in the platform_device now. */
+       hcd = dev_get_drvdata(&pdev->dev);
+       xhci = hcd_to_xhci(hcd);
+       xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev,
+                       dev_name(&pdev->dev), hcd);
+       if (!xhci->shared_hcd) {
+               ret = -ENOMEM;
+               goto dealloc_usb2_hcd;
+       }
+
+       /*
+        * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset)
+        * is called by usb_add_hcd().
+        */
+       *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
+
+       ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+       if (ret)
+               goto put_usb3_hcd;
+
+       return 0;
+
+put_usb3_hcd:
+       usb_put_hcd(xhci->shared_hcd);
+
+dealloc_usb2_hcd:
+       usb_remove_hcd(hcd);
+
+unmap_registers:
+       iounmap(hcd->regs);
+
+release_mem_region:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+put_hcd:
+       usb_put_hcd(hcd);
+
+       return ret;
+}
+
+static int xhci_plat_remove(struct platform_device *dev)
+{
+       struct usb_hcd  *hcd = platform_get_drvdata(dev);
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       usb_remove_hcd(xhci->shared_hcd);
+       usb_put_hcd(xhci->shared_hcd);
+
+       usb_remove_hcd(hcd);
+       iounmap(hcd->regs);
+       usb_put_hcd(hcd);
+       kfree(xhci);
+
+       return 0;
+}
+
+static struct platform_driver usb_xhci_driver = {
+       .probe  = xhci_plat_probe,
+       .remove = xhci_plat_remove,
+       .driver = {
+               .name = "xhci-hcd",
+       },
+};
+MODULE_ALIAS("platform:xhci-hcd");
+
+int xhci_register_plat(void)
+{
+       return platform_driver_register(&usb_xhci_driver);
+}
+
+void xhci_unregister_plat(void)
+{
+       platform_driver_unregister(&usb_xhci_driver);
+}
index f9f161fa66ad1f861aa0e77691860582360ddfb1..e1963d4a430f2952c8383b5a25fff8debc6c0014 100644 (file)
@@ -4064,6 +4064,11 @@ static int __init xhci_hcd_init(void)
                printk(KERN_DEBUG "Problem registering PCI driver.");
                return retval;
        }
+       retval = xhci_register_plat();
+       if (retval < 0) {
+               printk(KERN_DEBUG "Problem registering platform driver.");
+               goto unreg_pci;
+       }
        /*
         * Check the compiler generated sizes of structures that must be laid
         * out in specific ways for hardware access.
@@ -4083,11 +4088,15 @@ static int __init xhci_hcd_init(void)
        BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8);
        BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8);
        return 0;
+unreg_pci:
+       xhci_unregister_pci();
+       return retval;
 }
 module_init(xhci_hcd_init);
 
 static void __exit xhci_hcd_cleanup(void)
 {
        xhci_unregister_pci();
+       xhci_unregister_plat();
 }
 module_exit(xhci_hcd_cleanup);
index 57cd632064b4088bd17fd6c75fc177611b8ff6df..91074fdab3eb6cbcfd772b6c7066083155beed91 100644 (file)
@@ -1663,6 +1663,17 @@ static inline int xhci_register_pci(void) { return 0; }
 static inline void xhci_unregister_pci(void) {}
 #endif
 
+#if defined(CONFIG_USB_XHCI_PLATFORM) \
+       || defined(CONFIG_USB_XHCI_PLATFORM_MODULE)
+int xhci_register_plat(void);
+void xhci_unregister_plat(void);
+#else
+static inline int xhci_register_plat(void)
+{ return 0; }
+static inline void xhci_unregister_plat(void)
+{  }
+#endif
+
 /* xHCI host controller glue */
 typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
 void xhci_quiesce(struct xhci_hcd *xhci);