diff options
Diffstat (limited to 'drivers/usb/host/ohci-at91.c')
-rw-r--r-- | drivers/usb/host/ohci-at91.c | 239 |
1 files changed, 220 insertions, 19 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 944291e10f97..ba3a46b78b75 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,156 @@ 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 | gpio_set_value(pdata->vbus_pin[port], !pdata->vbus_pin_inverted ^ enable); | ||
227 | } | ||
228 | |||
229 | static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port) | ||
230 | { | ||
231 | if (port < 0 || port >= 2) | ||
232 | return -EINVAL; | ||
233 | |||
234 | return gpio_get_value(pdata->vbus_pin[port]) ^ !pdata->vbus_pin_inverted; | ||
235 | } | ||
236 | |||
237 | /* | ||
238 | * Update the status data from the hub with the over-current indicator change. | ||
239 | */ | ||
240 | static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) | ||
241 | { | ||
242 | struct at91_usbh_data *pdata = hcd->self.controller->platform_data; | ||
243 | int length = ohci_hub_status_data(hcd, buf); | ||
244 | int port; | ||
245 | |||
246 | for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { | ||
247 | if (pdata->overcurrent_changed[port]) { | ||
248 | if (! length) | ||
249 | length = 1; | ||
250 | buf[0] |= 1 << (port + 1); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | return length; | ||
255 | } | ||
256 | |||
257 | /* | ||
258 | * Look at the control requests to the root hub and see if we need to override. | ||
259 | */ | ||
260 | static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | ||
261 | u16 wIndex, char *buf, u16 wLength) | ||
262 | { | ||
263 | struct at91_usbh_data *pdata = hcd->self.controller->platform_data; | ||
264 | struct usb_hub_descriptor *desc; | ||
265 | int ret = -EINVAL; | ||
266 | u32 *data = (u32 *)buf; | ||
267 | |||
268 | dev_dbg(hcd->self.controller, | ||
269 | "ohci_at91_hub_control(%p,0x%04x,0x%04x,0x%04x,%p,%04x)\n", | ||
270 | hcd, typeReq, wValue, wIndex, buf, wLength); | ||
271 | |||
272 | switch (typeReq) { | ||
273 | case SetPortFeature: | ||
274 | if (wValue == USB_PORT_FEAT_POWER) { | ||
275 | dev_dbg(hcd->self.controller, "SetPortFeat: POWER\n"); | ||
276 | ohci_at91_usb_set_power(pdata, wIndex - 1, 1); | ||
277 | goto out; | ||
278 | } | ||
279 | break; | ||
280 | |||
281 | case ClearPortFeature: | ||
282 | switch (wValue) { | ||
283 | case USB_PORT_FEAT_C_OVER_CURRENT: | ||
284 | dev_dbg(hcd->self.controller, | ||
285 | "ClearPortFeature: C_OVER_CURRENT\n"); | ||
286 | |||
287 | if (wIndex == 1 || wIndex == 2) { | ||
288 | pdata->overcurrent_changed[wIndex-1] = 0; | ||
289 | pdata->overcurrent_status[wIndex-1] = 0; | ||
290 | } | ||
291 | |||
292 | goto out; | ||
293 | |||
294 | case USB_PORT_FEAT_OVER_CURRENT: | ||
295 | dev_dbg(hcd->self.controller, | ||
296 | "ClearPortFeature: OVER_CURRENT\n"); | ||
297 | |||
298 | if (wIndex == 1 || wIndex == 2) { | ||
299 | pdata->overcurrent_status[wIndex-1] = 0; | ||
300 | } | ||
301 | |||
302 | goto out; | ||
303 | |||
304 | case USB_PORT_FEAT_POWER: | ||
305 | dev_dbg(hcd->self.controller, | ||
306 | "ClearPortFeature: POWER\n"); | ||
307 | |||
308 | if (wIndex == 1 || wIndex == 2) { | ||
309 | ohci_at91_usb_set_power(pdata, wIndex - 1, 0); | ||
310 | return 0; | ||
311 | } | ||
312 | } | ||
313 | break; | ||
314 | } | ||
315 | |||
316 | ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); | ||
317 | if (ret) | ||
318 | goto out; | ||
319 | |||
320 | switch (typeReq) { | ||
321 | case GetHubDescriptor: | ||
322 | |||
323 | /* update the hub's descriptor */ | ||
324 | |||
325 | desc = (struct usb_hub_descriptor *)buf; | ||
326 | |||
327 | dev_dbg(hcd->self.controller, "wHubCharacteristics 0x%04x\n", | ||
328 | desc->wHubCharacteristics); | ||
329 | |||
330 | /* remove the old configurations for power-switching, and | ||
331 | * over-current protection, and insert our new configuration | ||
332 | */ | ||
333 | |||
334 | desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_LPSM); | ||
335 | desc->wHubCharacteristics |= cpu_to_le16(0x0001); | ||
336 | |||
337 | if (pdata->overcurrent_supported) { | ||
338 | desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); | ||
339 | desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); | ||
340 | } | ||
341 | |||
342 | dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", | ||
343 | desc->wHubCharacteristics); | ||
344 | |||
345 | return ret; | ||
346 | |||
347 | case GetPortStatus: | ||
348 | /* check port status */ | ||
349 | |||
350 | dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); | ||
351 | |||
352 | if (wIndex == 1 || wIndex == 2) { | ||
353 | if (! ohci_at91_usb_get_power(pdata, wIndex-1)) { | ||
354 | *data &= ~cpu_to_le32(RH_PS_PPS); | ||
355 | } | ||
356 | |||
357 | if (pdata->overcurrent_changed[wIndex-1]) { | ||
358 | *data |= cpu_to_le32(RH_PS_OCIC); | ||
359 | } | ||
360 | |||
361 | if (pdata->overcurrent_status[wIndex-1]) { | ||
362 | *data |= cpu_to_le32(RH_PS_POCI); | ||
363 | } | ||
364 | } | ||
365 | } | ||
366 | |||
367 | out: | ||
368 | return ret; | ||
369 | } | ||
370 | |||
226 | /*-------------------------------------------------------------------------*/ | 371 | /*-------------------------------------------------------------------------*/ |
227 | 372 | ||
228 | static const struct hc_driver ohci_at91_hc_driver = { | 373 | static const struct hc_driver ohci_at91_hc_driver = { |
@@ -258,8 +403,8 @@ static const struct hc_driver ohci_at91_hc_driver = { | |||
258 | /* | 403 | /* |
259 | * root hub support | 404 | * root hub support |
260 | */ | 405 | */ |
261 | .hub_status_data = ohci_hub_status_data, | 406 | .hub_status_data = ohci_at91_hub_status_data, |
262 | .hub_control = ohci_hub_control, | 407 | .hub_control = ohci_at91_hub_control, |
263 | #ifdef CONFIG_PM | 408 | #ifdef CONFIG_PM |
264 | .bus_suspend = ohci_bus_suspend, | 409 | .bus_suspend = ohci_bus_suspend, |
265 | .bus_resume = ohci_bus_resume, | 410 | .bus_resume = ohci_bus_resume, |
@@ -269,22 +414,71 @@ static const struct hc_driver ohci_at91_hc_driver = { | |||
269 | 414 | ||
270 | /*-------------------------------------------------------------------------*/ | 415 | /*-------------------------------------------------------------------------*/ |
271 | 416 | ||
417 | static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data) | ||
418 | { | ||
419 | struct platform_device *pdev = data; | ||
420 | struct at91_usbh_data *pdata = pdev->dev.platform_data; | ||
421 | int val, gpio, port; | ||
422 | |||
423 | /* From the GPIO notifying the over-current situation, find | ||
424 | * out the corresponding port */ | ||
425 | gpio = irq_to_gpio(irq); | ||
426 | for (port = 0; port < ARRAY_SIZE(pdata->overcurrent_pin); port++) { | ||
427 | if (pdata->overcurrent_pin[port] == gpio) | ||
428 | break; | ||
429 | } | ||
430 | |||
431 | if (port == ARRAY_SIZE(pdata->overcurrent_pin)) { | ||
432 | dev_err(& pdev->dev, "overcurrent interrupt from unknown GPIO\n"); | ||
433 | return IRQ_HANDLED; | ||
434 | } | ||
435 | |||
436 | val = gpio_get_value(gpio); | ||
437 | |||
438 | /* When notified of an over-current situation, disable power | ||
439 | on the corresponding port, and mark this port in | ||
440 | over-current. */ | ||
441 | if (! val) { | ||
442 | ohci_at91_usb_set_power(pdata, port, 0); | ||
443 | pdata->overcurrent_status[port] = 1; | ||
444 | pdata->overcurrent_changed[port] = 1; | ||
445 | } | ||
446 | |||
447 | dev_dbg(& pdev->dev, "overcurrent situation %s\n", | ||
448 | val ? "exited" : "notified"); | ||
449 | |||
450 | return IRQ_HANDLED; | ||
451 | } | ||
452 | |||
453 | /*-------------------------------------------------------------------------*/ | ||
454 | |||
272 | static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) | 455 | static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) |
273 | { | 456 | { |
274 | struct at91_usbh_data *pdata = pdev->dev.platform_data; | 457 | struct at91_usbh_data *pdata = pdev->dev.platform_data; |
275 | int i; | 458 | int i; |
276 | 459 | ||
277 | if (pdata) { | 460 | 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++) { | 461 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { |
284 | if (pdata->vbus_pin[i] <= 0) | 462 | if (pdata->vbus_pin[i] <= 0) |
285 | continue; | 463 | continue; |
286 | gpio_request(pdata->vbus_pin[i], "ohci_vbus"); | 464 | gpio_request(pdata->vbus_pin[i], "ohci_vbus"); |
287 | gpio_direction_output(pdata->vbus_pin[i], 0); | 465 | ohci_at91_usb_set_power(pdata, i, 1); |
466 | } | ||
467 | |||
468 | for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { | ||
469 | int ret; | ||
470 | |||
471 | if (pdata->overcurrent_pin[i] <= 0) | ||
472 | continue; | ||
473 | gpio_request(pdata->overcurrent_pin[i], "ohci_overcurrent"); | ||
474 | |||
475 | ret = request_irq(gpio_to_irq(pdata->overcurrent_pin[i]), | ||
476 | ohci_hcd_at91_overcurrent_irq, | ||
477 | IRQF_SHARED, "ohci_overcurrent", pdev); | ||
478 | if (ret) { | ||
479 | gpio_free(pdata->overcurrent_pin[i]); | ||
480 | dev_warn(& pdev->dev, "cannot get GPIO IRQ for overcurrent\n"); | ||
481 | } | ||
288 | } | 482 | } |
289 | } | 483 | } |
290 | 484 | ||
@@ -301,9 +495,16 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev) | |||
301 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { | 495 | for (i = 0; i < ARRAY_SIZE(pdata->vbus_pin); i++) { |
302 | if (pdata->vbus_pin[i] <= 0) | 496 | if (pdata->vbus_pin[i] <= 0) |
303 | continue; | 497 | continue; |
304 | gpio_direction_output(pdata->vbus_pin[i], 1); | 498 | ohci_at91_usb_set_power(pdata, i, 0); |
305 | gpio_free(pdata->vbus_pin[i]); | 499 | gpio_free(pdata->vbus_pin[i]); |
306 | } | 500 | } |
501 | |||
502 | for (i = 0; i < ARRAY_SIZE(pdata->overcurrent_pin); i++) { | ||
503 | if (pdata->overcurrent_pin[i] <= 0) | ||
504 | continue; | ||
505 | free_irq(gpio_to_irq(pdata->overcurrent_pin[i]), pdev); | ||
506 | gpio_free(pdata->overcurrent_pin[i]); | ||
507 | } | ||
307 | } | 508 | } |
308 | 509 | ||
309 | device_init_wakeup(&pdev->dev, 0); | 510 | device_init_wakeup(&pdev->dev, 0); |