diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/host/Kconfig | 4 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 4 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 205 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 9 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 11 |
5 files changed, 233 insertions, 0 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index dd04517..b179958 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -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 diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 7ca290f..0982bcc 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -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 index 0000000..689bc18 --- /dev/null +++ b/drivers/usb/host/xhci-plat.c @@ -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); +} diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index f9f161f..e1963d4 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -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); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 57cd632..91074fd 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -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); |