This adds a fairly simple xhci-platform driver support. Currently it is used by the dwc3 driver. Signed-off-by: Sebastian Andrzej Siewior <bigeasy@xxxxxxxxxxxxx> --- I haven't tested this because I don't have the toys for it atm. Felipe can you add a tested by once you are sure that it works? If someone wants a config option and not have the platform code always compiled in, it can be done :) drivers/usb/host/Makefile | 1 + drivers/usb/host/xhci-plat.c | 227 ++++++++++++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.c | 9 ++ drivers/usb/host/xhci.h | 3 + 4 files changed, 240 insertions(+), 0 deletions(-) create mode 100644 drivers/usb/host/xhci-plat.c diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index ed48a5d..f4876d1 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -14,6 +14,7 @@ fhci-$(CONFIG_FHCI_DEBUG) += fhci-dbg.o 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 +xhci-hcd-y += xhci-plat.o obj-$(CONFIG_USB_WHCI_HCD) += whci/ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c new file mode 100644 index 0000000..61e9198 --- /dev/null +++ b/drivers/usb/host/xhci-plat.c @@ -0,0 +1,227 @@ +/* + * xHCI host controller driver platform Bus Glue. + * + * Copyright (C) 2008 Intel Corp. + * Author: Sebastian Andrzej Siewior < bigeasy @ linutronix . de > + * + * A lot of code borrowed from the Linux xHCI driver. + */ + +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include "xhci.h" + +static const char hcd_name[] = "xhci_hcd"; + +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 = hcd_name, + .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 usb_hcd_plat_probe(struct platform_device *dev, + const struct hc_driver *driver) +{ + struct usb_hcd *hcd; + int retval; + int irq; + struct resource *res; + + if (usb_disabled()) + return -ENODEV; + + if (!driver) + return -EINVAL; + + irq = platform_get_irq(dev, 0); + if (irq < 0) + return -ENODEV; + + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + + hcd = usb_create_hcd(driver, &dev->dev, dev_name(&dev->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(&dev->dev, "controller already in use\n"); + retval = -EBUSY; + goto put_hcd; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (hcd->regs == NULL) { + dev_dbg(&dev->dev, "error mapping memory\n"); + retval = -EFAULT; + goto release_mem_region; + } + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval != 0) + goto unmap_registers; + return retval; + +unmap_registers: + iounmap(hcd->regs); +release_mem_region: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +put_hcd: + usb_put_hcd(hcd); + return retval; +} + +/* + * We need to register our own PCI probe function (instead of the USB core's + * function) in order to create a second roothub under xHCI. + */ +static int xhci_plat_probe(struct platform_device *dev) +{ + int retval; + struct xhci_hcd *xhci; + const struct hc_driver *driver; + struct usb_hcd *hcd; + int irq; + + driver = &xhci_plat_xhci_driver; + /* + * Register the USB 2.0 roothub. + * FIXME: USB core must know to register the USB 2.0 roothub first. + * This is sort of silly, because we could just set the HCD driver flags + * to say USB 2.0, but I'm not sure what the implications would be in + * the other parts of the HCD code. + */ + retval = usb_hcd_plat_probe(dev, driver); + if (retval) + return retval; + + irq = platform_get_irq(dev, 0); + + /* USB 2.0 roothub is stored in the PCI device now. */ + hcd = dev_get_drvdata(&dev->dev); + xhci = hcd_to_xhci(hcd); + xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev, + dev_name(&dev->dev), hcd); + if (!xhci->shared_hcd) { + retval = -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; + + retval = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + if (retval) + goto put_usb3_hcd; + /* Roothub already marked as USB 3.0 speed */ + return 0; + +put_usb3_hcd: + usb_put_hcd(xhci->shared_hcd); +dealloc_usb2_hcd: + return retval; +} + +static int xhci_plat_remove(struct platform_device *dev) +{ + struct xhci_hcd *xhci; + + xhci = hcd_to_xhci(platform_get_drvdata(dev)); + if (xhci->shared_hcd) { + usb_remove_hcd(xhci->shared_hcd); + usb_put_hcd(xhci->shared_hcd); + } + + usb_remove_hcd(xhci->main_hcd); + iounmap(xhci->main_hcd->regs); + usb_put_hcd(xhci->main_hcd); + kfree(xhci); + return 0; +} + +static struct platform_driver usb_xhci_driver = { + .probe = xhci_plat_probe, + .remove = xhci_plat_remove, + .driver = { + .name = "usb-xhci", + }, +}; +MODULE_ALIAS("platform:usb-xhci"); + +int xhci_register_plat(void) +{ + return platform_driver_register(&usb_xhci_driver); +} + +void xhci_unregister_plat(void) +{ + platform_driver_unregister(&usb_xhci_driver); +} diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 1ff95a0..d673837 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -4046,6 +4046,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. @@ -4065,11 +4070,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); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 3c8fbd2..086fa76 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1649,6 +1649,9 @@ static inline int xhci_register_pci(void) { return 0; } static inline void xhci_unregister_pci(void) {} #endif +int xhci_register_plat(void); +void xhci_unregister_plat(void); + /* xHCI host controller glue */ typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); void xhci_quiesce(struct xhci_hcd *xhci); -- 1.7.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html