diff options
-rw-r--r-- | drivers/usb/host/Kconfig | 15 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/ehci-pmcmsp.c | 383 |
3 files changed, 401 insertions, 2 deletions
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 0e6afa260ed8..923e5a079b59 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig | |||
@@ -91,17 +91,28 @@ config USB_EHCI_TT_NEWSCHED | |||
91 | 91 | ||
92 | If unsure, say Y. | 92 | If unsure, say Y. |
93 | 93 | ||
94 | config USB_EHCI_HCD_PMC_MSP | ||
95 | tristate "EHCI support for on-chip PMC MSP71xx USB controller" | ||
96 | depends on USB_EHCI_HCD && MSP_HAS_USB | ||
97 | default n | ||
98 | select USB_EHCI_BIG_ENDIAN_DESC | ||
99 | select USB_EHCI_BIG_ENDIAN_MMIO | ||
100 | ---help--- | ||
101 | Enables support for the onchip USB controller on the PMC_MSP7100 Family SoC's. | ||
102 | If unsure, say N. | ||
103 | |||
94 | config USB_EHCI_BIG_ENDIAN_MMIO | 104 | config USB_EHCI_BIG_ENDIAN_MMIO |
95 | bool | 105 | bool |
96 | depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ | 106 | depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ |
97 | ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ | 107 | ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ |
98 | PPC_MPC512x || CPU_CAVIUM_OCTEON) | 108 | PPC_MPC512x || CPU_CAVIUM_OCTEON || \ |
109 | PMC_MSP) | ||
99 | default y | 110 | default y |
100 | 111 | ||
101 | config USB_EHCI_BIG_ENDIAN_DESC | 112 | config USB_EHCI_BIG_ENDIAN_DESC |
102 | bool | 113 | bool |
103 | depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ | 114 | depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ |
104 | PPC_MPC512x) | 115 | PPC_MPC512x || PMC_MSP) |
105 | default y | 116 | default y |
106 | 117 | ||
107 | config XPS_USB_HCD_XILINX | 118 | config XPS_USB_HCD_XILINX |
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 4c77a818fd80..f69305d3a9b8 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c | |||
@@ -1259,6 +1259,11 @@ MODULE_LICENSE ("GPL"); | |||
1259 | #define PLATFORM_DRIVER ehci_msm_driver | 1259 | #define PLATFORM_DRIVER ehci_msm_driver |
1260 | #endif | 1260 | #endif |
1261 | 1261 | ||
1262 | #ifdef CONFIG_USB_EHCI_HCD_PMC_MSP | ||
1263 | #include "ehci-pmcmsp.c" | ||
1264 | #define PLATFORM_DRIVER ehci_hcd_msp_driver | ||
1265 | #endif | ||
1266 | |||
1262 | #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ | 1267 | #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \ |
1263 | !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ | 1268 | !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \ |
1264 | !defined(XILINX_OF_PLATFORM_DRIVER) | 1269 | !defined(XILINX_OF_PLATFORM_DRIVER) |
diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c new file mode 100644 index 000000000000..a2168642175b --- /dev/null +++ b/drivers/usb/host/ehci-pmcmsp.c | |||
@@ -0,0 +1,383 @@ | |||
1 | /* | ||
2 | * PMC MSP EHCI (Host Controller Driver) for USB. | ||
3 | * | ||
4 | * (C) Copyright 2006-2010 PMC-Sierra Inc | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file "COPYING" in the main directory of this archive | ||
8 | * for more details. | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | /* includes */ | ||
13 | #include <linux/platform_device.h> | ||
14 | #include <linux/gpio.h> | ||
15 | #include <linux/usb.h> | ||
16 | #include <msp_usb.h> | ||
17 | |||
18 | /* stream disable*/ | ||
19 | #define USB_CTRL_MODE_STREAM_DISABLE 0x10 | ||
20 | |||
21 | /* threshold */ | ||
22 | #define USB_CTRL_FIFO_THRESH 0x00300000 | ||
23 | |||
24 | /* register offset for usb_mode */ | ||
25 | #define USB_EHCI_REG_USB_MODE 0x68 | ||
26 | |||
27 | /* register offset for usb fifo */ | ||
28 | #define USB_EHCI_REG_USB_FIFO 0x24 | ||
29 | |||
30 | /* register offset for usb status */ | ||
31 | #define USB_EHCI_REG_USB_STATUS 0x44 | ||
32 | |||
33 | /* serial/parallel transceiver */ | ||
34 | #define USB_EHCI_REG_BIT_STAT_STS (1<<29) | ||
35 | |||
36 | /* TWI USB0 host device pin */ | ||
37 | #define MSP_PIN_USB0_HOST_DEV 49 | ||
38 | |||
39 | /* TWI USB1 host device pin */ | ||
40 | #define MSP_PIN_USB1_HOST_DEV 50 | ||
41 | |||
42 | |||
43 | static void usb_hcd_tdi_set_mode(struct ehci_hcd *ehci) | ||
44 | { | ||
45 | u8 *base; | ||
46 | u8 *statreg; | ||
47 | u8 *fiforeg; | ||
48 | u32 val; | ||
49 | struct ehci_regs *reg_base = ehci->regs; | ||
50 | |||
51 | /* get register base */ | ||
52 | base = (u8 *)reg_base + USB_EHCI_REG_USB_MODE; | ||
53 | statreg = (u8 *)reg_base + USB_EHCI_REG_USB_STATUS; | ||
54 | fiforeg = (u8 *)reg_base + USB_EHCI_REG_USB_FIFO; | ||
55 | |||
56 | /* Disable controller mode stream */ | ||
57 | val = ehci_readl(ehci, (u32 *)base); | ||
58 | ehci_writel(ehci, (val | USB_CTRL_MODE_STREAM_DISABLE), | ||
59 | (u32 *)base); | ||
60 | |||
61 | /* clear STS to select parallel transceiver interface */ | ||
62 | val = ehci_readl(ehci, (u32 *)statreg); | ||
63 | val = val & ~USB_EHCI_REG_BIT_STAT_STS; | ||
64 | ehci_writel(ehci, val, (u32 *)statreg); | ||
65 | |||
66 | /* write to set the proper fifo threshold */ | ||
67 | ehci_writel(ehci, USB_CTRL_FIFO_THRESH, (u32 *)fiforeg); | ||
68 | |||
69 | /* set TWI GPIO USB_HOST_DEV pin high */ | ||
70 | gpio_direction_output(MSP_PIN_USB0_HOST_DEV, 1); | ||
71 | #ifdef CONFIG_MSP_HAS_DUAL_USB | ||
72 | gpio_direction_output(MSP_PIN_USB1_HOST_DEV, 1); | ||
73 | #endif | ||
74 | } | ||
75 | |||
76 | /* called during probe() after chip reset completes */ | ||
77 | static int ehci_msp_setup(struct usb_hcd *hcd) | ||
78 | { | ||
79 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
80 | int retval; | ||
81 | ehci->big_endian_mmio = 1; | ||
82 | ehci->big_endian_desc = 1; | ||
83 | |||
84 | ehci->caps = hcd->regs; | ||
85 | ehci->regs = hcd->regs + | ||
86 | HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); | ||
87 | dbg_hcs_params(ehci, "reset"); | ||
88 | dbg_hcc_params(ehci, "reset"); | ||
89 | |||
90 | /* cache this readonly data; minimize chip reads */ | ||
91 | ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); | ||
92 | hcd->has_tt = 1; | ||
93 | |||
94 | retval = ehci_halt(ehci); | ||
95 | if (retval) | ||
96 | return retval; | ||
97 | |||
98 | ehci_reset(ehci); | ||
99 | |||
100 | /* data structure init */ | ||
101 | retval = ehci_init(hcd); | ||
102 | if (retval) | ||
103 | return retval; | ||
104 | |||
105 | usb_hcd_tdi_set_mode(ehci); | ||
106 | ehci_port_power(ehci, 0); | ||
107 | |||
108 | return retval; | ||
109 | } | ||
110 | |||
111 | |||
112 | /* configure so an HC device and id are always provided | ||
113 | * always called with process context; sleeping is OK | ||
114 | */ | ||
115 | |||
116 | static int usb_hcd_msp_map_regs(struct mspusb_device *dev) | ||
117 | { | ||
118 | struct resource *res; | ||
119 | struct platform_device *pdev = &dev->dev; | ||
120 | u32 res_len; | ||
121 | int retval; | ||
122 | |||
123 | /* MAB register space */ | ||
124 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
125 | if (res == NULL) | ||
126 | return -ENOMEM; | ||
127 | res_len = res->end - res->start + 1; | ||
128 | if (!request_mem_region(res->start, res_len, "mab regs")) | ||
129 | return -EBUSY; | ||
130 | |||
131 | dev->mab_regs = ioremap_nocache(res->start, res_len); | ||
132 | if (dev->mab_regs == NULL) { | ||
133 | retval = -ENOMEM; | ||
134 | goto err1; | ||
135 | } | ||
136 | |||
137 | /* MSP USB register space */ | ||
138 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
139 | if (res == NULL) { | ||
140 | retval = -ENOMEM; | ||
141 | goto err2; | ||
142 | } | ||
143 | res_len = res->end - res->start + 1; | ||
144 | if (!request_mem_region(res->start, res_len, "usbid regs")) { | ||
145 | retval = -EBUSY; | ||
146 | goto err2; | ||
147 | } | ||
148 | dev->usbid_regs = ioremap_nocache(res->start, res_len); | ||
149 | if (dev->usbid_regs == NULL) { | ||
150 | retval = -ENOMEM; | ||
151 | goto err3; | ||
152 | } | ||
153 | |||
154 | return 0; | ||
155 | err3: | ||
156 | res = platform_get_resource(pdev, IORESOURCE_MEM, 2); | ||
157 | res_len = res->end - res->start + 1; | ||
158 | release_mem_region(res->start, res_len); | ||
159 | err2: | ||
160 | iounmap(dev->mab_regs); | ||
161 | err1: | ||
162 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
163 | res_len = res->end - res->start + 1; | ||
164 | release_mem_region(res->start, res_len); | ||
165 | dev_err(&pdev->dev, "Failed to map non-EHCI regs.\n"); | ||
166 | return retval; | ||
167 | } | ||
168 | |||
169 | /** | ||
170 | * usb_hcd_msp_probe - initialize PMC MSP-based HCDs | ||
171 | * Context: !in_interrupt() | ||
172 | * | ||
173 | * Allocates basic resources for this USB host controller, and | ||
174 | * then invokes the start() method for the HCD associated with it | ||
175 | * through the hotplug entry's driver_data. | ||
176 | * | ||
177 | */ | ||
178 | int usb_hcd_msp_probe(const struct hc_driver *driver, | ||
179 | struct platform_device *dev) | ||
180 | { | ||
181 | int retval; | ||
182 | struct usb_hcd *hcd; | ||
183 | struct resource *res; | ||
184 | struct ehci_hcd *ehci ; | ||
185 | |||
186 | hcd = usb_create_hcd(driver, &dev->dev, "pmcmsp"); | ||
187 | if (!hcd) | ||
188 | return -ENOMEM; | ||
189 | |||
190 | res = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
191 | if (res == NULL) { | ||
192 | pr_debug("No IOMEM resource info for %s.\n", dev->name); | ||
193 | retval = -ENOMEM; | ||
194 | goto err1; | ||
195 | } | ||
196 | hcd->rsrc_start = res->start; | ||
197 | hcd->rsrc_len = res->end - res->start + 1; | ||
198 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, dev->name)) { | ||
199 | retval = -EBUSY; | ||
200 | goto err1; | ||
201 | } | ||
202 | hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len); | ||
203 | if (!hcd->regs) { | ||
204 | pr_debug("ioremap failed"); | ||
205 | retval = -ENOMEM; | ||
206 | goto err2; | ||
207 | } | ||
208 | |||
209 | res = platform_get_resource(dev, IORESOURCE_IRQ, 0); | ||
210 | if (res == NULL) { | ||
211 | dev_err(&dev->dev, "No IRQ resource info for %s.\n", dev->name); | ||
212 | retval = -ENOMEM; | ||
213 | goto err3; | ||
214 | } | ||
215 | |||
216 | /* Map non-EHCI register spaces */ | ||
217 | retval = usb_hcd_msp_map_regs(to_mspusb_device(dev)); | ||
218 | if (retval != 0) | ||
219 | goto err3; | ||
220 | |||
221 | ehci = hcd_to_ehci(hcd); | ||
222 | ehci->big_endian_mmio = 1; | ||
223 | ehci->big_endian_desc = 1; | ||
224 | |||
225 | |||
226 | retval = usb_add_hcd(hcd, res->start, IRQF_SHARED); | ||
227 | if (retval == 0) | ||
228 | return 0; | ||
229 | |||
230 | usb_remove_hcd(hcd); | ||
231 | err3: | ||
232 | iounmap(hcd->regs); | ||
233 | err2: | ||
234 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
235 | err1: | ||
236 | usb_put_hcd(hcd); | ||
237 | |||
238 | return retval; | ||
239 | } | ||
240 | |||
241 | |||
242 | |||
243 | /** | ||
244 | * usb_hcd_msp_remove - shutdown processing for PMC MSP-based HCDs | ||
245 | * @dev: USB Host Controller being removed | ||
246 | * Context: !in_interrupt() | ||
247 | * | ||
248 | * Reverses the effect of usb_hcd_msp_probe(), first invoking | ||
249 | * the HCD's stop() method. It is always called from a thread | ||
250 | * context, normally "rmmod", "apmd", or something similar. | ||
251 | * | ||
252 | * may be called without controller electrically present | ||
253 | * may be called with controller, bus, and devices active | ||
254 | */ | ||
255 | void usb_hcd_msp_remove(struct usb_hcd *hcd, struct platform_device *dev) | ||
256 | { | ||
257 | usb_remove_hcd(hcd); | ||
258 | iounmap(hcd->regs); | ||
259 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
260 | usb_put_hcd(hcd); | ||
261 | } | ||
262 | |||
263 | #ifdef CONFIG_MSP_HAS_DUAL_USB | ||
264 | /* | ||
265 | * Wrapper around the main ehci_irq. Since both USB host controllers are | ||
266 | * sharing the same IRQ, need to first determine whether we're the intended | ||
267 | * recipient of this interrupt. | ||
268 | */ | ||
269 | static irqreturn_t ehci_msp_irq(struct usb_hcd *hcd) | ||
270 | { | ||
271 | u32 int_src; | ||
272 | struct device *dev = hcd->self.controller; | ||
273 | struct platform_device *pdev; | ||
274 | struct mspusb_device *mdev; | ||
275 | struct ehci_hcd *ehci = hcd_to_ehci(hcd); | ||
276 | /* need to reverse-map a couple of containers to get our device */ | ||
277 | pdev = to_platform_device(dev); | ||
278 | mdev = to_mspusb_device(pdev); | ||
279 | |||
280 | /* Check to see if this interrupt is for this host controller */ | ||
281 | int_src = ehci_readl(ehci, &mdev->mab_regs->int_stat); | ||
282 | if (int_src & (1 << pdev->id)) | ||
283 | return ehci_irq(hcd); | ||
284 | |||
285 | /* Not for this device */ | ||
286 | return IRQ_NONE; | ||
287 | } | ||
288 | #endif /* DUAL_USB */ | ||
289 | |||
290 | static const struct hc_driver ehci_msp_hc_driver = { | ||
291 | .description = hcd_name, | ||
292 | .product_desc = "PMC MSP EHCI", | ||
293 | .hcd_priv_size = sizeof(struct ehci_hcd), | ||
294 | |||
295 | /* | ||
296 | * generic hardware linkage | ||
297 | */ | ||
298 | #ifdef CONFIG_MSP_HAS_DUAL_USB | ||
299 | .irq = ehci_msp_irq, | ||
300 | #else | ||
301 | .irq = ehci_irq, | ||
302 | #endif | ||
303 | .flags = HCD_MEMORY | HCD_USB2, | ||
304 | |||
305 | /* | ||
306 | * basic lifecycle operations | ||
307 | */ | ||
308 | .reset = ehci_msp_setup, | ||
309 | .start = ehci_run, | ||
310 | .shutdown = ehci_shutdown, | ||
311 | .start = ehci_run, | ||
312 | .stop = ehci_stop, | ||
313 | |||
314 | /* | ||
315 | * managing i/o requests and associated device resources | ||
316 | */ | ||
317 | .urb_enqueue = ehci_urb_enqueue, | ||
318 | .urb_dequeue = ehci_urb_dequeue, | ||
319 | .endpoint_disable = ehci_endpoint_disable, | ||
320 | .endpoint_reset = ehci_endpoint_reset, | ||
321 | |||
322 | /* | ||
323 | * scheduling support | ||
324 | */ | ||
325 | .get_frame_number = ehci_get_frame, | ||
326 | |||
327 | /* | ||
328 | * root hub support | ||
329 | */ | ||
330 | .hub_status_data = ehci_hub_status_data, | ||
331 | .hub_control = ehci_hub_control, | ||
332 | .bus_suspend = ehci_bus_suspend, | ||
333 | .bus_resume = ehci_bus_resume, | ||
334 | .relinquish_port = ehci_relinquish_port, | ||
335 | .port_handed_over = ehci_port_handed_over, | ||
336 | |||
337 | .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, | ||
338 | }; | ||
339 | |||
340 | static int ehci_hcd_msp_drv_probe(struct platform_device *pdev) | ||
341 | { | ||
342 | int ret; | ||
343 | |||
344 | pr_debug("In ehci_hcd_msp_drv_probe"); | ||
345 | |||
346 | if (usb_disabled()) | ||
347 | return -ENODEV; | ||
348 | |||
349 | gpio_request(MSP_PIN_USB0_HOST_DEV, "USB0_HOST_DEV_GPIO"); | ||
350 | #ifdef CONFIG_MSP_HAS_DUAL_USB | ||
351 | gpio_request(MSP_PIN_USB1_HOST_DEV, "USB1_HOST_DEV_GPIO"); | ||
352 | #endif | ||
353 | |||
354 | ret = usb_hcd_msp_probe(&ehci_msp_hc_driver, pdev); | ||
355 | |||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | static int ehci_hcd_msp_drv_remove(struct platform_device *pdev) | ||
360 | { | ||
361 | struct usb_hcd *hcd = platform_get_drvdata(pdev); | ||
362 | |||
363 | usb_hcd_msp_remove(hcd, pdev); | ||
364 | |||
365 | /* free TWI GPIO USB_HOST_DEV pin */ | ||
366 | gpio_free(MSP_PIN_USB0_HOST_DEV); | ||
367 | #ifdef CONFIG_MSP_HAS_DUAL_USB | ||
368 | gpio_free(MSP_PIN_USB1_HOST_DEV); | ||
369 | #endif | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | |||
374 | MODULE_ALIAS("pmcmsp-ehci"); | ||
375 | |||
376 | static struct platform_driver ehci_hcd_msp_driver = { | ||
377 | .probe = ehci_hcd_msp_drv_probe, | ||
378 | .remove = ehci_hcd_msp_drv_remove, | ||
379 | .driver = { | ||
380 | .name = "pmcmsp-ehci", | ||
381 | .owner = THIS_MODULE, | ||
382 | }, | ||
383 | }; | ||