diff options
author | Sebastian Andrzej Siewior <bigeasy@linutronix.de> | 2012-03-13 10:57:41 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2012-03-13 13:30:59 -0400 |
commit | 3429e91a661e1f383aecc86c6bbcf65afb15c892 (patch) | |
tree | ff3aa3ac0a8cb3ce8190511874a03ab2b13fc8ff /drivers/usb/host/xhci-plat.c | |
parent | fdaf8b3183d126d70f19e13c690c762c65b28a5d (diff) |
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>
Diffstat (limited to 'drivers/usb/host/xhci-plat.c')
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c new file mode 100644 index 000000000000..689bc18b051d --- /dev/null +++ b/drivers/usb/host/xhci-plat.c | |||
@@ -0,0 +1,205 @@ | |||
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 | } | ||