diff options
Diffstat (limited to 'drivers/usb/host')
-rw-r--r-- | drivers/usb/host/ohci-da8xx.c | 456 | ||||
-rw-r--r-- | drivers/usb/host/ohci-hcd.c | 5 |
2 files changed, 461 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c new file mode 100644 index 000000000000..4aa08d36d077 --- /dev/null +++ b/drivers/usb/host/ohci-da8xx.c | |||
@@ -0,0 +1,456 @@ | |||
1 | /* | ||
2 | * OHCI HCD (Host Controller Driver) for USB. | ||
3 | * | ||
4 | * TI DA8xx (OMAP-L1x) Bus Glue | ||
5 | * | ||
6 | * Derived from: ohci-omap.c and ohci-s3c2410.c | ||
7 | * Copyright (C) 2008-2009 MontaVista Software, Inc. <source@mvista.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/interrupt.h> | ||
15 | #include <linux/jiffies.h> | ||
16 | #include <linux/platform_device.h> | ||
17 | #include <linux/clk.h> | ||
18 | |||
19 | #include <mach/da8xx.h> | ||
20 | #include <mach/usb.h> | ||
21 | |||
22 | #ifndef CONFIG_ARCH_DAVINCI_DA8XX | ||
23 | #error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX." | ||
24 | #endif | ||
25 | |||
26 | #define CFGCHIP2 DA8XX_SYSCFG_VIRT(DA8XX_CFGCHIP2_REG) | ||
27 | |||
28 | static struct clk *usb11_clk; | ||
29 | static struct clk *usb20_clk; | ||
30 | |||
31 | /* Over-current indicator change bitmask */ | ||
32 | static volatile u16 ocic_mask; | ||
33 | |||
34 | static void ohci_da8xx_clock(int on) | ||
35 | { | ||
36 | u32 cfgchip2; | ||
37 | |||
38 | cfgchip2 = __raw_readl(CFGCHIP2); | ||
39 | if (on) { | ||
40 | clk_enable(usb11_clk); | ||
41 | |||
42 | /* | ||
43 | * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we | ||
44 | * need to enable the USB 2.0 module clocking, start its PHY, | ||
45 | * and not allow it to stop the clock during USB 2.0 suspend. | ||
46 | */ | ||
47 | if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) { | ||
48 | clk_enable(usb20_clk); | ||
49 | |||
50 | cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN); | ||
51 | cfgchip2 |= CFGCHIP2_PHY_PLLON; | ||
52 | __raw_writel(cfgchip2, CFGCHIP2); | ||
53 | |||
54 | pr_info("Waiting for USB PHY clock good...\n"); | ||
55 | while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD)) | ||
56 | cpu_relax(); | ||
57 | } | ||
58 | |||
59 | /* Enable USB 1.1 PHY */ | ||
60 | cfgchip2 |= CFGCHIP2_USB1SUSPENDM; | ||
61 | } else { | ||
62 | clk_disable(usb11_clk); | ||
63 | if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) | ||
64 | clk_disable(usb20_clk); | ||
65 | |||
66 | /* Disable USB 1.1 PHY */ | ||
67 | cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM; | ||
68 | } | ||
69 | __raw_writel(cfgchip2, CFGCHIP2); | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Handle the port over-current indicator change. | ||
74 | */ | ||
75 | static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, | ||
76 | unsigned port) | ||
77 | { | ||
78 | ocic_mask |= 1 << port; | ||
79 | |||
80 | /* Once over-current is detected, the port needs to be powered down */ | ||
81 | if (hub->get_oci(port) > 0) | ||
82 | hub->set_power(port, 0); | ||
83 | } | ||
84 | |||
85 | static int ohci_da8xx_init(struct usb_hcd *hcd) | ||
86 | { | ||
87 | struct device *dev = hcd->self.controller; | ||
88 | struct da8xx_ohci_root_hub *hub = dev->platform_data; | ||
89 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
90 | int result; | ||
91 | u32 rh_a; | ||
92 | |||
93 | dev_dbg(dev, "starting USB controller\n"); | ||
94 | |||
95 | ohci_da8xx_clock(1); | ||
96 | |||
97 | /* | ||
98 | * DA8xx only have 1 port connected to the pins but the HC root hub | ||
99 | * register A reports 2 ports, thus we'll have to override it... | ||
100 | */ | ||
101 | ohci->num_ports = 1; | ||
102 | |||
103 | result = ohci_init(ohci); | ||
104 | if (result < 0) | ||
105 | return result; | ||
106 | |||
107 | /* | ||
108 | * Since we're providing a board-specific root hub port power control | ||
109 | * and over-current reporting, we have to override the HC root hub A | ||
110 | * register's default value, so that ohci_hub_control() could return | ||
111 | * the correct hub descriptor... | ||
112 | */ | ||
113 | rh_a = ohci_readl(ohci, &ohci->regs->roothub.a); | ||
114 | if (hub->set_power) { | ||
115 | rh_a &= ~RH_A_NPS; | ||
116 | rh_a |= RH_A_PSM; | ||
117 | } | ||
118 | if (hub->get_oci) { | ||
119 | rh_a &= ~RH_A_NOCP; | ||
120 | rh_a |= RH_A_OCPM; | ||
121 | } | ||
122 | rh_a &= ~RH_A_POTPGT; | ||
123 | rh_a |= hub->potpgt << 24; | ||
124 | ohci_writel(ohci, rh_a, &ohci->regs->roothub.a); | ||
125 | |||
126 | return result; | ||
127 | } | ||
128 | |||
129 | static void ohci_da8xx_stop(struct usb_hcd *hcd) | ||
130 | { | ||
131 | ohci_stop(hcd); | ||
132 | ohci_da8xx_clock(0); | ||
133 | } | ||
134 | |||
135 | static int ohci_da8xx_start(struct usb_hcd *hcd) | ||
136 | { | ||
137 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
138 | int result; | ||
139 | |||
140 | result = ohci_run(ohci); | ||
141 | if (result < 0) | ||
142 | ohci_da8xx_stop(hcd); | ||
143 | |||
144 | return result; | ||
145 | } | ||
146 | |||
147 | /* | ||
148 | * Update the status data from the hub with the over-current indicator change. | ||
149 | */ | ||
150 | static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) | ||
151 | { | ||
152 | int length = ohci_hub_status_data(hcd, buf); | ||
153 | |||
154 | /* See if we have OCIC bit set on port 1 */ | ||
155 | if (ocic_mask & (1 << 1)) { | ||
156 | dev_dbg(hcd->self.controller, "over-current indicator change " | ||
157 | "on port 1\n"); | ||
158 | |||
159 | if (!length) | ||
160 | length = 1; | ||
161 | |||
162 | buf[0] |= 1 << 1; | ||
163 | } | ||
164 | return length; | ||
165 | } | ||
166 | |||
167 | /* | ||
168 | * Look at the control requests to the root hub and see if we need to override. | ||
169 | */ | ||
170 | static int ohci_da8xx_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | ||
171 | u16 wIndex, char *buf, u16 wLength) | ||
172 | { | ||
173 | struct device *dev = hcd->self.controller; | ||
174 | struct da8xx_ohci_root_hub *hub = dev->platform_data; | ||
175 | int temp; | ||
176 | |||
177 | switch (typeReq) { | ||
178 | case GetPortStatus: | ||
179 | /* Check the port number */ | ||
180 | if (wIndex != 1) | ||
181 | break; | ||
182 | |||
183 | dev_dbg(dev, "GetPortStatus(%u)\n", wIndex); | ||
184 | |||
185 | temp = roothub_portstatus(hcd_to_ohci(hcd), wIndex - 1); | ||
186 | |||
187 | /* The port power status (PPS) bit defaults to 1 */ | ||
188 | if (hub->get_power && hub->get_power(wIndex) == 0) | ||
189 | temp &= ~RH_PS_PPS; | ||
190 | |||
191 | /* The port over-current indicator (POCI) bit is always 0 */ | ||
192 | if (hub->get_oci && hub->get_oci(wIndex) > 0) | ||
193 | temp |= RH_PS_POCI; | ||
194 | |||
195 | /* The over-current indicator change (OCIC) bit is 0 too */ | ||
196 | if (ocic_mask & (1 << wIndex)) | ||
197 | temp |= RH_PS_OCIC; | ||
198 | |||
199 | put_unaligned(cpu_to_le32(temp), (__le32 *)buf); | ||
200 | return 0; | ||
201 | case SetPortFeature: | ||
202 | temp = 1; | ||
203 | goto check_port; | ||
204 | case ClearPortFeature: | ||
205 | temp = 0; | ||
206 | |||
207 | check_port: | ||
208 | /* Check the port number */ | ||
209 | if (wIndex != 1) | ||
210 | break; | ||
211 | |||
212 | switch (wValue) { | ||
213 | case USB_PORT_FEAT_POWER: | ||
214 | dev_dbg(dev, "%sPortFeature(%u): %s\n", | ||
215 | temp ? "Set" : "Clear", wIndex, "POWER"); | ||
216 | |||
217 | if (!hub->set_power) | ||
218 | return -EPIPE; | ||
219 | |||
220 | return hub->set_power(wIndex, temp) ? -EPIPE : 0; | ||
221 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
222 | dev_dbg(dev, "%sPortFeature(%u): %s\n", | ||
223 | temp ? "Set" : "Clear", wIndex, | ||
224 | "C_OVER_CURRENT"); | ||
225 | |||
226 | if (temp) | ||
227 | ocic_mask |= 1 << wIndex; | ||
228 | else | ||
229 | ocic_mask &= ~(1 << wIndex); | ||
230 | return 0; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); | ||
235 | } | ||
236 | |||
237 | static const struct hc_driver ohci_da8xx_hc_driver = { | ||
238 | .description = hcd_name, | ||
239 | .product_desc = "DA8xx OHCI", | ||
240 | .hcd_priv_size = sizeof(struct ohci_hcd), | ||
241 | |||
242 | /* | ||
243 | * generic hardware linkage | ||
244 | */ | ||
245 | .irq = ohci_irq, | ||
246 | .flags = HCD_USB11 | HCD_MEMORY, | ||
247 | |||
248 | /* | ||
249 | * basic lifecycle operations | ||
250 | */ | ||
251 | .reset = ohci_da8xx_init, | ||
252 | .start = ohci_da8xx_start, | ||
253 | .stop = ohci_da8xx_stop, | ||
254 | .shutdown = ohci_shutdown, | ||
255 | |||
256 | /* | ||
257 | * managing i/o requests and associated device resources | ||
258 | */ | ||
259 | .urb_enqueue = ohci_urb_enqueue, | ||
260 | .urb_dequeue = ohci_urb_dequeue, | ||
261 | .endpoint_disable = ohci_endpoint_disable, | ||
262 | |||
263 | /* | ||
264 | * scheduling support | ||
265 | */ | ||
266 | .get_frame_number = ohci_get_frame, | ||
267 | |||
268 | /* | ||
269 | * root hub support | ||
270 | */ | ||
271 | .hub_status_data = ohci_da8xx_hub_status_data, | ||
272 | .hub_control = ohci_da8xx_hub_control, | ||
273 | |||
274 | #ifdef CONFIG_PM | ||
275 | .bus_suspend = ohci_bus_suspend, | ||
276 | .bus_resume = ohci_bus_resume, | ||
277 | #endif | ||
278 | .start_port_reset = ohci_start_port_reset, | ||
279 | }; | ||
280 | |||
281 | /*-------------------------------------------------------------------------*/ | ||
282 | |||
283 | |||
284 | /** | ||
285 | * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs | ||
286 | * Context: !in_interrupt() | ||
287 | * | ||
288 | * Allocates basic resources for this USB host controller, and | ||
289 | * then invokes the start() method for the HCD associated with it | ||
290 | * through the hotplug entry's driver_data. | ||
291 | */ | ||
292 | static int usb_hcd_da8xx_probe(const struct hc_driver *driver, | ||
293 | struct platform_device *pdev) | ||
294 | { | ||
295 | struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; | ||
296 | struct usb_hcd *hcd; | ||
297 | struct resource *mem; | ||
298 | int error, irq; | ||
299 | |||
300 | if (hub == NULL) | ||
301 | return -ENODEV; | ||
302 | |||
303 | usb11_clk = clk_get(&pdev->dev, "usb11"); | ||
304 | if (IS_ERR(usb11_clk)) | ||
305 | return PTR_ERR(usb11_clk); | ||
306 | |||
307 | usb20_clk = clk_get(&pdev->dev, "usb20"); | ||
308 | if (IS_ERR(usb20_clk)) { | ||
309 | error = PTR_ERR(usb20_clk); | ||
310 | goto err0; | ||
311 | } | ||
312 | |||
313 | hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); | ||
314 | if (!hcd) { | ||
315 | error = -ENOMEM; | ||
316 | goto err1; | ||
317 | } | ||
318 | |||
319 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
320 | if (!mem) { | ||
321 | error = -ENODEV; | ||
322 | goto err2; | ||
323 | } | ||
324 | hcd->rsrc_start = mem->start; | ||
325 | hcd->rsrc_len = mem->end - mem->start + 1; | ||
326 | |||
327 | if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { | ||
328 | dev_dbg(&pdev->dev, "request_mem_region failed\n"); | ||
329 | error = -EBUSY; | ||
330 | goto err2; | ||
331 | } | ||
332 | |||
333 | hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); | ||
334 | if (!hcd->regs) { | ||
335 | dev_err(&pdev->dev, "ioremap failed\n"); | ||
336 | error = -ENOMEM; | ||
337 | goto err3; | ||
338 | } | ||
339 | |||
340 | ohci_hcd_init(hcd_to_ohci(hcd)); | ||
341 | |||
342 | irq = platform_get_irq(pdev, 0); | ||
343 | if (irq < 0) { | ||
344 | error = -ENODEV; | ||
345 | goto err4; | ||
346 | } | ||
347 | error = usb_add_hcd(hcd, irq, IRQF_DISABLED); | ||
348 | if (error) | ||
349 | goto err4; | ||
350 | |||
351 | if (hub->ocic_notify) { | ||
352 | error = hub->ocic_notify(ohci_da8xx_ocic_handler); | ||
353 | if (!error) | ||
354 | return 0; | ||
355 | } | ||
356 | |||
357 | usb_remove_hcd(hcd); | ||
358 | err4: | ||
359 | iounmap(hcd->regs); | ||
360 | err3: | ||
361 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
362 | err2: | ||
363 | usb_put_hcd(hcd); | ||
364 | err1: | ||
365 | clk_put(usb20_clk); | ||
366 | err0: | ||
367 | clk_put(usb11_clk); | ||
368 | return error; | ||
369 | } | ||
370 | |||
371 | /** | ||
372 | * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs | ||
373 | * @dev: USB Host Controller being removed | ||
374 | * Context: !in_interrupt() | ||
375 | * | ||
376 | * Reverses the effect of usb_hcd_da8xx_probe(), first invoking | ||
377 | * the HCD's stop() method. It is always called from a thread | ||
378 | * context, normally "rmmod", "apmd", or something similar. | ||
379 | */ | ||
380 | static inline void | ||
381 | usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) | ||
382 | { | ||
383 | struct da8xx_ohci_root_hub *hub = pdev->dev.platform_data; | ||
384 | |||
385 | hub->ocic_notify(NULL); | ||
386 | usb_remove_hcd(hcd); | ||
387 | iounmap(hcd->regs); | ||
388 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | ||
389 | usb_put_hcd(hcd); | ||
390 | clk_put(usb20_clk); | ||
391 | clk_put(usb11_clk); | ||
392 | } | ||
393 | |||
394 | static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev) | ||
395 | { | ||
396 | return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev); | ||
397 | } | ||
398 | |||
399 | static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev) | ||
400 | { | ||
401 | struct usb_hcd *hcd = platform_get_drvdata(dev); | ||
402 | |||
403 | usb_hcd_da8xx_remove(hcd, dev); | ||
404 | platform_set_drvdata(dev, NULL); | ||
405 | |||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | #ifdef CONFIG_PM | ||
410 | static int ohci_da8xx_suspend(struct platform_device *dev, pm_message_t message) | ||
411 | { | ||
412 | struct usb_hcd *hcd = platform_get_drvdata(dev); | ||
413 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
414 | |||
415 | if (time_before(jiffies, ohci->next_statechange)) | ||
416 | msleep(5); | ||
417 | ohci->next_statechange = jiffies; | ||
418 | |||
419 | ohci_da8xx_clock(0); | ||
420 | hcd->state = HC_STATE_SUSPENDED; | ||
421 | dev->dev.power.power_state = PMSG_SUSPEND; | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | static int ohci_da8xx_resume(struct platform_device *dev) | ||
426 | { | ||
427 | struct usb_hcd *hcd = platform_get_drvdata(dev); | ||
428 | struct ohci_hcd *ohci = hcd_to_ohci(hcd); | ||
429 | |||
430 | if (time_before(jiffies, ohci->next_statechange)) | ||
431 | msleep(5); | ||
432 | ohci->next_statechange = jiffies; | ||
433 | |||
434 | ohci_da8xx_clock(1); | ||
435 | dev->dev.power.power_state = PMSG_ON; | ||
436 | usb_hcd_resume_root_hub(hcd); | ||
437 | return 0; | ||
438 | } | ||
439 | #endif | ||
440 | |||
441 | /* | ||
442 | * Driver definition to register with platform structure. | ||
443 | */ | ||
444 | static struct platform_driver ohci_hcd_da8xx_driver = { | ||
445 | .probe = ohci_hcd_da8xx_drv_probe, | ||
446 | .remove = ohci_hcd_da8xx_drv_remove, | ||
447 | .shutdown = usb_hcd_platform_shutdown, | ||
448 | #ifdef CONFIG_PM | ||
449 | .suspend = ohci_da8xx_suspend, | ||
450 | .resume = ohci_da8xx_resume, | ||
451 | #endif | ||
452 | .driver = { | ||
453 | .owner = THIS_MODULE, | ||
454 | .name = "ohci", | ||
455 | }, | ||
456 | }; | ||
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 24eb74781919..afe59be23645 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c | |||
@@ -1051,6 +1051,11 @@ MODULE_LICENSE ("GPL"); | |||
1051 | #define PLATFORM_DRIVER usb_hcd_pnx4008_driver | 1051 | #define PLATFORM_DRIVER usb_hcd_pnx4008_driver |
1052 | #endif | 1052 | #endif |
1053 | 1053 | ||
1054 | #ifdef CONFIG_ARCH_DAVINCI_DA8XX | ||
1055 | #include "ohci-da8xx.c" | ||
1056 | #define PLATFORM_DRIVER ohci_hcd_da8xx_driver | ||
1057 | #endif | ||
1058 | |||
1054 | #if defined(CONFIG_CPU_SUBTYPE_SH7720) || \ | 1059 | #if defined(CONFIG_CPU_SUBTYPE_SH7720) || \ |
1055 | defined(CONFIG_CPU_SUBTYPE_SH7721) || \ | 1060 | defined(CONFIG_CPU_SUBTYPE_SH7721) || \ |
1056 | defined(CONFIG_CPU_SUBTYPE_SH7763) || \ | 1061 | defined(CONFIG_CPU_SUBTYPE_SH7763) || \ |