aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ohci-at91.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/host/ohci-at91.c')
-rw-r--r--drivers/usb/host/ohci-at91.c245
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
36static void at91_start_clock(void) 36static 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
221static 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
232static 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 */
246static 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 */
266static 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
228static const struct hc_driver ohci_at91_hc_driver = { 379static 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
423static 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
272static int ohci_hcd_at91_drv_probe(struct platform_device *pdev) 461static 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);