diff options
Diffstat (limited to 'drivers/usb/host/ohci-at91.c')
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 245 |
1 files changed, 226 insertions, 19 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 944291e10f97..95a9fec38e89 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c | |||
@@ -35,8 +35,7 @@ extern int usb_disabled(void); | |||
35 | 35 | ||
36 | static void at91_start_clock(void) | 36 | static void at91_start_clock(void) |
37 | { | 37 | { |
38 | if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) | 38 | clk_enable(hclk); |
39 | clk_enable(hclk); | ||
40 | clk_enable(iclk); | 39 | clk_enable(iclk); |
41 | clk_enable(fclk); | 40 | clk_enable(fclk); |
42 | clocked = 1; | 41 | clocked = 1; |
@@ -46,8 +45,7 @@ static void at91_stop_clock(void) | |||
46 | { | 45 | { |
47 | clk_disable(fclk); | 46 | clk_disable(fclk); |
48 | clk_disable(iclk); | 47 | clk_disable(iclk); |
49 | if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) | 48 | clk_disable(hclk); |
50 | clk_disable(hclk); | ||
51 | clocked = 0; | 49 | clocked = 0; |
52 | } | 50 | } |
53 | 51 | ||
@@ -142,8 +140,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, | |||
142 | 140 | ||
143 | iclk = clk_get(&pdev->dev, "ohci_clk"); | 141 | iclk = clk_get(&pdev->dev, "ohci_clk"); |
144 | fclk = clk_get(&pdev->dev, "uhpck"); | 142 | fclk = clk_get(&pdev->dev, "uhpck"); |
145 | if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) | 143 | hclk = clk_get(&pdev->dev, "hclk"); |
146 | hclk = clk_get(&pdev->dev, "hck0"); | ||
147 | 144 | ||
148 | at91_start_hc(pdev); | 145 | at91_start_hc(pdev); |
149 | ohci_hcd_init(hcd_to_ohci(hcd)); | 146 | ohci_hcd_init(hcd_to_ohci(hcd)); |
@@ -155,8 +152,7 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, | |||
155 | /* Error handling */ | 152 | /* Error handling */ |
156 | at91_stop_hc(pdev); | 153 | at91_stop_hc(pdev); |
157 | 154 | ||
158 | if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) | 155 | clk_put(hclk); |
159 | clk_put(hclk); | ||
160 | clk_put(fclk); | 156 | clk_put(fclk); |
161 | clk_put(iclk); | 157 | clk_put(iclk); |
162 | 158 | ||
@@ -192,8 +188,7 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd, | |||
192 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); | 188 | release_mem_region(hcd->rsrc_start, hcd->rsrc_len); |
193 | usb_put_hcd(hcd); | 189 | usb_put_hcd(hcd); |
194 | 190 | ||
195 | if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) | 191 | clk_put(hclk); |
196 | clk_put(hclk); | ||
197 | clk_put(fclk); | 192 | clk_put(fclk); |
198 | clk_put(iclk); | 193 | clk_put(iclk); |
199 | fclk = iclk = hclk = NULL; | 194 | fclk = iclk = hclk = NULL; |
@@ -223,6 +218,162 @@ ohci_at91_start (struct usb_hcd *hcd) | |||
223 | return 0; | 218 | return 0; |
224 | } | 219 | } |
225 | 220 | ||
221 | static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable) | ||
222 | { | ||
223 | if (port < 0 || port >= 2) | ||
224 | return; | ||
225 | |||
226 | if (pdata->vbus_pin[port] <= 0) | ||
227 | return; | ||
228 | |||
229 | gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); | ||
230 | } | ||
231 | |||
232 | static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) | ||
233 | { | ||
234 | if (port < 0 || port >= 2) | ||
235 | return -EINVAL; | ||
236 | |||
237 | if (pdata->vbus_pin[port] <= 0) | ||
238 | return -EINVAL; | ||
239 | |||
240 | return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * Update the status data from the hub with the over-current indicator change. | ||
245 | */ | ||
246 | static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) | ||
247 | { | ||
248 | struct at91_usbh_data *pdata = hcd->self.controller->platform_data; | ||
249 | int length = ohci_hub_status_data(hcd, buf); | ||
250 | int port; | ||
251 | |||
252 | for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { | ||
253 | if (pdata->overcurrent_changed[port]) { | ||
254 | if (! length) | ||
255 | length = 1; | ||
256 | buf[0] |= 1 << (port + 1); | ||
257 | } | ||
258 | } | ||
259 | |||
260 | return length; | ||
261 | } | ||
262 | |||
263 | /* | ||
264 | * Look at the control requests to the root hub and see if we need to override. | ||
265 | */ | ||
266 | static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | ||
267 | u16 wIndex, char *buf, u16 wLength) | ||
268 | { | ||
269 | struct at91_usbh_data *pdata = hcd->self.controller->platform_data; | ||
270 | struct usb_hub_descriptor *desc; | ||
271 | int ret = -EINVAL; | ||
272 | u32 *data = (u32 *)buf; | ||
273 | |||
274 | dev_dbg(hcd->self.controller, | ||
275 | "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", | ||
276 | hcd, typeReq, wValue, wIndex, buf, wLength); | ||
277 | |||
278 | switch (typeReq) { | ||
279 | case SetPortFeature: | ||
280 | if (wValue == USB_PORT_FEAT_POWER) { | ||
281 | dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); | ||
282 | ohci_at91_usb_set_power(pdata, wIndex - 1, 1); | ||
283 | goto out; | ||
284 | } | ||
285 | break; | ||
286 | |||
287 | case ClearPortFeature: | ||
288 | switch (wValue) { | ||
289 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
290 | dev_dbg(hcd->self.controller, | ||
291 | "ClearPortFeature: C_OVER_CURRENT\n"); | ||
292 | |||
293 | if (wIndex == 1 || wIndex == 2) { | ||
294 | pdata->overcurrent_changed[wIndex-1] = 0; | ||
295 | pdata->overcurrent_status[wIndex-1] = 0; | ||
296 | } | ||
297 | |||
298 | goto out; | ||
299 | |||
300 | case USB_PORT_FEAT_OVER_CURRENT: | ||
301 | dev_dbg(hcd->self.controller, | ||
302 | "ClearPortFeature: OVER_CURRENT\n"); | ||
303 | |||
304 | if (wIndex == 1 || wIndex == 2) { | ||
305 | pdata->overcurrent_status[wIndex-1] = 0; | ||
306 | } | ||
307 | |||
308 | goto out; | ||
309 | |||
310 | case USB_PORT_FEAT_POWER: | ||
311 | dev_dbg(hcd->self.controller, | ||
312 | "ClearPortFeature: POWER\n"); | ||
313 | |||
314 | if (wIndex == 1 || wIndex == 2) { | ||
315 | ohci_at91_usb_set_power(pdata, wIndex - 1, 0); | ||
316 | return 0; | ||
317 | } | ||
318 | } | ||
319 | break; | ||
320 | } | ||
321 | |||
322 | ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); | ||
323 | if (ret) | ||
324 | goto out; | ||
325 | |||
326 | switch (typeReq) { | ||
327 | case GetHubDescriptor: | ||
328 | |||
329 | /* update the hub's descriptor */ | ||
330 | |||
331 | desc = (struct usb_hub_descriptor *)buf; | ||
332 | |||
333 | dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n", | ||
334 | desc->wHubCharacteristics); | ||
335 | |||
336 | /* remove the old configurations for power-switching, and | ||
337 | * over-current protection, and insert our new configuration | ||
338 | */ | ||
339 | |||
340 | desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM); | ||
341 | desc->wHubCharacteristics |= cpu_to_le16(0x0001); | ||
342 | |||
343 | if (pdata->overcurrent_supported) { | ||
344 | desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); | ||
345 | desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); | ||
346 | } | ||
347 | |||
348 | dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", | ||
349 | desc->wHubCharacteristics); | ||
350 | |||
351 | return ret; | ||
352 | |||
353 | case GetPortStatus: | ||
354 | /* check port status */ | ||
355 | |||
356 | dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); | ||
357 | |||
358 | if (wIndex == 1 || wIndex == 2) { | ||
359 | if (! ohci_at91_usb_get_power(pdata, wIndex-1)) { | ||
360 | *data &= ~cpu_to_le32(RH_PS_PPS); | ||
361 | } | ||
362 | |||
363 | if (pdata->overcurrent_changed[wIndex-1]) { | ||
364 | *data |= cpu_to_le32(RH_PS_OCIC); | ||
365 | } | ||
366 | |||
367 | if (pdata->overcurrent_status[wIndex-1]) { | ||
368 | *data |= cpu_to_le32(RH_PS_POCI); | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | out: | ||
374 | return ret; | ||
375 | } | ||
376 | |||
226 | /*-------------------------------------------------------------------------*/ | 377 | /*-------------------------------------------------------------------------*/ |
227 | 378 | ||
228 | static const struct hc_driver ohci_at91_hc_driver = { | 379 | static const struct hc_driver ohci_at91_hc_driver = { |
@@ -258,8 +409,8 @@ static const struct hc_driver ohci_at91_hc_driver = { | |||
258 | /* | 409 | /* |
259 | * root hub support | 410 | * root hub support |
260 | */ | 411 | */ |
261 | .hub_status_data = ohci_hub_status_data, | 412 | .hub_status_data = ohci_at91_hub_status_data, |
262 | .hub_control = ohci_hub_control, | 413 | .hub_control = ohci_at91_hub_control, |
263 | #ifdef CONFIG_PM | 414 | #ifdef CONFIG_PM |
264 | .bus_suspend = ohci_bus_suspend, | 415 | .bus_suspend = ohci_bus_suspend, |
265 | .bus_resume = ohci_bus_resume, | 416 | .bus_resume = ohci_bus_resume, |
@@ -269,22 +420,71 @@ static const struct hc_driver ohci_at91_hc_driver = { | |||
269 | 420 | ||
270 | /*-------------------------------------------------------------------------*/ | 421 | /*-------------------------------------------------------------------------*/ |
271 | 422 | ||
423 | static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) | ||
424 | { | ||
425 | struct platform_device *pdev = data; | ||
426 | struct at91_usbh_data *pdata = pdev->dev.platform_data; | ||
427 | int val, gpio, port; | ||
428 | |||
429 | /* From the GPIO notifying the over-current situation, find | ||
430 | * out the corresponding port */ | ||
431 | gpio = irq_to_gpio(irq); | ||
432 | for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { | ||
433 | if (pdata->overcurrent_pin[port] == gpio) | ||
434 | break; | ||
435 | } | ||
436 | |||
437 | if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { | ||
438 | dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); | ||
439 | return IRQ_HANDLED; | ||
440 | } | ||
441 | |||
442 | val = gpio_get_value(gpio); | ||
443 | |||
444 | /* When notified of an over-current situation, disable power | ||
445 | on the corresponding port, and mark this port in | ||
446 | over-current. */ | ||
447 | if (! val) { | ||
448 | ohci_at91_usb_set_power(pdata, port, 0); | ||
449 | pdata->overcurrent_status[port] = 1; | ||
450 | pdata->overcurrent_changed[port] = 1; | ||
451 | } | ||
452 | |||
453 | dev_dbg(& pdev->dev, "overcurrent situation %s\n", | ||
454 | val ? "exited" : "notified"); | ||
455 | |||
456 | return IRQ_HANDLED; | ||
457 | } | ||
458 | |||
459 | /*-------------------------------------------------------------------------*/ | ||
460 | |||
272 | static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) | 461 | static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) |
273 | { | 462 | { |
274 | struct at91_usbh_data *pdata = pdev->dev.platform_data; | 463 | struct at91_usbh_data *pdata = pdev->dev.platform_data; |
275 | int i; | 464 | int i; |
276 | 465 | ||
277 | if (pdata) { | 466 | if (pdata) { |
278 | /* REVISIT make the driver support per-port power switching, | ||
279 | * and also overcurrent detection. Here we assume the ports | ||
280 | * are always powered while this driver is active, and use | ||
281 | * active-low power switches. | ||
282 | */ | ||
283 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { | 467 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { |
284 | if (pdata->vbus_pin[i] <= 0) | 468 | if (pdata->vbus_pin[i] <= 0) |
285 | continue; | 469 | continue; |
286 | gpio_request(pdata->vbus_pin[i], "ohci_vbus"); | 470 | gpio_request(pdata->vbus_pin[i], "ohci_vbus"); |
287 | gpio_direction_output(pdata->vbus_pin[i], 0); | 471 | ohci_at91_usb_set_power(pdata, i, 1); |
472 | } | ||
473 | |||
474 | for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { | ||
475 | int ret; | ||
476 | |||
477 | if (pdata->overcurrent_pin[i] <= 0) | ||
478 | continue; | ||
479 | gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); | ||
480 | |||
481 | ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]), | ||
482 | ohci_hcd_at91_overcurrent_irq, | ||
483 | IRQF_SHARED, "ohci_overcurrent", pdev); | ||
484 | if (ret) { | ||
485 | gpio_free(pdata->overcurrent_pin[i]); | ||
486 | dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); | ||
487 | } | ||
288 | } | 488 | } |
289 | } | 489 | } |
290 | 490 | ||
@@ -301,9 +501,16 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) | |||
301 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { | 501 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { |
302 | if (pdata->vbus_pin[i] <= 0) | 502 | if (pdata->vbus_pin[i] <= 0) |
303 | continue; | 503 | continue; |
304 | gpio_direction_output(pdata->vbus_pin[i], 1); | 504 | ohci_at91_usb_set_power(pdata, i, 0); |
305 | gpio_free(pdata->vbus_pin[i]); | 505 | gpio_free(pdata->vbus_pin[i]); |
306 | } | 506 | } |
507 | |||
508 | for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { | ||
509 | if (pdata->overcurrent_pin[i] <= 0) | ||
510 | continue; | ||
511 | free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); | ||
512 | gpio_free(pdata->overcurrent_pin[i]); | ||
513 | } | ||
307 | } | 514 | } |
308 | 515 | ||
309 | device_init_wakeup(&pdev->dev, 0); | 516 | device_init_wakeup(&pdev->dev, 0); |