Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: host: xhci: add platform driver support

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>

authored by

Sebastian Andrzej Siewior and committed by
Sarah Sharp
3429e91a fdaf8b31

+233
+4
drivers/usb/host/Kconfig
··· 27 27 To compile this driver as a module, choose M here: the 28 28 module will be called xhci-hcd. 29 29 30 + config USB_XHCI_PLATFORM 31 + tristate 32 + depends on USB_XHCI_HCD 33 + 30 34 config USB_XHCI_HCD_DEBUGGING 31 35 bool "Debugging for the xHCI host controller" 32 36 depends on USB_XHCI_HCD
+4
drivers/usb/host/Makefile
··· 15 15 xhci-hcd-y += xhci-ring.o xhci-hub.o xhci-dbg.o 16 16 xhci-hcd-$(CONFIG_PCI) += xhci-pci.o 17 17 18 + ifneq ($(CONFIG_USB_XHCI_PLATFORM), ) 19 + xhci-hcd-y += xhci-plat.o 20 + endif 21 + 18 22 obj-$(CONFIG_USB_WHCI_HCD) += whci/ 19 23 20 24 obj-$(CONFIG_PCI) += pci-quirks.o
+205
drivers/usb/host/xhci-plat.c
··· 1 + /* 2 + * xhci-plat.c - xHCI host controller driver platform Bus Glue. 3 + * 4 + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com 5 + * Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> 6 + * 7 + * A lot of code borrowed from the Linux xHCI driver. 8 + * 9 + * This program is free software; you can redistribute it and/or 10 + * modify it under the terms of the GNU General Public License 11 + * version 2 as published by the Free Software Foundation. 12 + */ 13 + 14 + #include <linux/platform_device.h> 15 + #include <linux/module.h> 16 + #include <linux/slab.h> 17 + 18 + #include "xhci.h" 19 + 20 + static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) 21 + { 22 + /* 23 + * As of now platform drivers don't provide MSI support so we ensure 24 + * here that the generic code does not try to make a pci_dev from our 25 + * dev struct in order to setup MSI 26 + */ 27 + xhci->quirks |= XHCI_BROKEN_MSI; 28 + } 29 + 30 + /* called during probe() after chip reset completes */ 31 + static int xhci_plat_setup(struct usb_hcd *hcd) 32 + { 33 + return xhci_gen_setup(hcd, xhci_plat_quirks); 34 + } 35 + 36 + static const struct hc_driver xhci_plat_xhci_driver = { 37 + .description = "xhci-hcd", 38 + .product_desc = "xHCI Host Controller", 39 + .hcd_priv_size = sizeof(struct xhci_hcd *), 40 + 41 + /* 42 + * generic hardware linkage 43 + */ 44 + .irq = xhci_irq, 45 + .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, 46 + 47 + /* 48 + * basic lifecycle operations 49 + */ 50 + .reset = xhci_plat_setup, 51 + .start = xhci_run, 52 + .stop = xhci_stop, 53 + .shutdown = xhci_shutdown, 54 + 55 + /* 56 + * managing i/o requests and associated device resources 57 + */ 58 + .urb_enqueue = xhci_urb_enqueue, 59 + .urb_dequeue = xhci_urb_dequeue, 60 + .alloc_dev = xhci_alloc_dev, 61 + .free_dev = xhci_free_dev, 62 + .alloc_streams = xhci_alloc_streams, 63 + .free_streams = xhci_free_streams, 64 + .add_endpoint = xhci_add_endpoint, 65 + .drop_endpoint = xhci_drop_endpoint, 66 + .endpoint_reset = xhci_endpoint_reset, 67 + .check_bandwidth = xhci_check_bandwidth, 68 + .reset_bandwidth = xhci_reset_bandwidth, 69 + .address_device = xhci_address_device, 70 + .update_hub_device = xhci_update_hub_device, 71 + .reset_device = xhci_discover_or_reset_device, 72 + 73 + /* 74 + * scheduling support 75 + */ 76 + .get_frame_number = xhci_get_frame, 77 + 78 + /* Root hub support */ 79 + .hub_control = xhci_hub_control, 80 + .hub_status_data = xhci_hub_status_data, 81 + .bus_suspend = xhci_bus_suspend, 82 + .bus_resume = xhci_bus_resume, 83 + }; 84 + 85 + static int xhci_plat_probe(struct platform_device *pdev) 86 + { 87 + const struct hc_driver *driver; 88 + struct xhci_hcd *xhci; 89 + struct resource *res; 90 + struct usb_hcd *hcd; 91 + int ret; 92 + int irq; 93 + 94 + if (usb_disabled()) 95 + return -ENODEV; 96 + 97 + driver = &xhci_plat_xhci_driver; 98 + 99 + irq = platform_get_irq(pdev, 0); 100 + if (irq < 0) 101 + return -ENODEV; 102 + 103 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 104 + if (!res) 105 + return -ENODEV; 106 + 107 + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); 108 + if (!hcd) 109 + return -ENOMEM; 110 + 111 + hcd->rsrc_start = res->start; 112 + hcd->rsrc_len = resource_size(res); 113 + 114 + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, 115 + driver->description)) { 116 + dev_dbg(&pdev->dev, "controller already in use\n"); 117 + ret = -EBUSY; 118 + goto put_hcd; 119 + } 120 + 121 + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 122 + if (!hcd->regs) { 123 + dev_dbg(&pdev->dev, "error mapping memory\n"); 124 + ret = -EFAULT; 125 + goto release_mem_region; 126 + } 127 + 128 + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); 129 + if (ret) 130 + goto unmap_registers; 131 + 132 + /* USB 2.0 roothub is stored in the platform_device now. */ 133 + hcd = dev_get_drvdata(&pdev->dev); 134 + xhci = hcd_to_xhci(hcd); 135 + xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev, 136 + dev_name(&pdev->dev), hcd); 137 + if (!xhci->shared_hcd) { 138 + ret = -ENOMEM; 139 + goto dealloc_usb2_hcd; 140 + } 141 + 142 + /* 143 + * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) 144 + * is called by usb_add_hcd(). 145 + */ 146 + *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; 147 + 148 + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); 149 + if (ret) 150 + goto put_usb3_hcd; 151 + 152 + return 0; 153 + 154 + put_usb3_hcd: 155 + usb_put_hcd(xhci->shared_hcd); 156 + 157 + dealloc_usb2_hcd: 158 + usb_remove_hcd(hcd); 159 + 160 + unmap_registers: 161 + iounmap(hcd->regs); 162 + 163 + release_mem_region: 164 + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 165 + 166 + put_hcd: 167 + usb_put_hcd(hcd); 168 + 169 + return ret; 170 + } 171 + 172 + static int xhci_plat_remove(struct platform_device *dev) 173 + { 174 + struct usb_hcd *hcd = platform_get_drvdata(dev); 175 + struct xhci_hcd *xhci = hcd_to_xhci(hcd); 176 + 177 + usb_remove_hcd(xhci->shared_hcd); 178 + usb_put_hcd(xhci->shared_hcd); 179 + 180 + usb_remove_hcd(hcd); 181 + iounmap(hcd->regs); 182 + usb_put_hcd(hcd); 183 + kfree(xhci); 184 + 185 + return 0; 186 + } 187 + 188 + static struct platform_driver usb_xhci_driver = { 189 + .probe = xhci_plat_probe, 190 + .remove = xhci_plat_remove, 191 + .driver = { 192 + .name = "xhci-hcd", 193 + }, 194 + }; 195 + MODULE_ALIAS("platform:xhci-hcd"); 196 + 197 + int xhci_register_plat(void) 198 + { 199 + return platform_driver_register(&usb_xhci_driver); 200 + } 201 + 202 + void xhci_unregister_plat(void) 203 + { 204 + platform_driver_unregister(&usb_xhci_driver); 205 + }
+9
drivers/usb/host/xhci.c
··· 4064 4064 printk(KERN_DEBUG "Problem registering PCI driver."); 4065 4065 return retval; 4066 4066 } 4067 + retval = xhci_register_plat(); 4068 + if (retval < 0) { 4069 + printk(KERN_DEBUG "Problem registering platform driver."); 4070 + goto unreg_pci; 4071 + } 4067 4072 /* 4068 4073 * Check the compiler generated sizes of structures that must be laid 4069 4074 * out in specific ways for hardware access. ··· 4088 4083 BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); 4089 4084 BUILD_BUG_ON(sizeof(struct xhci_doorbell_array) != 256*32/8); 4090 4085 return 0; 4086 + unreg_pci: 4087 + xhci_unregister_pci(); 4088 + return retval; 4091 4089 } 4092 4090 module_init(xhci_hcd_init); 4093 4091 4094 4092 static void __exit xhci_hcd_cleanup(void) 4095 4093 { 4096 4094 xhci_unregister_pci(); 4095 + xhci_unregister_plat(); 4097 4096 } 4098 4097 module_exit(xhci_hcd_cleanup);
+11
drivers/usb/host/xhci.h
··· 1663 1663 static inline void xhci_unregister_pci(void) {} 1664 1664 #endif 1665 1665 1666 + #if defined(CONFIG_USB_XHCI_PLATFORM) \ 1667 + || defined(CONFIG_USB_XHCI_PLATFORM_MODULE) 1668 + int xhci_register_plat(void); 1669 + void xhci_unregister_plat(void); 1670 + #else 1671 + static inline int xhci_register_plat(void) 1672 + { return 0; } 1673 + static inline void xhci_unregister_plat(void) 1674 + { } 1675 + #endif 1676 + 1666 1677 /* xHCI host controller glue */ 1667 1678 typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *); 1668 1679 void xhci_quiesce(struct xhci_hcd *xhci);