diff options
author | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
---|---|---|
committer | Glenn Elliott <gelliott@cs.unc.edu> | 2012-03-04 19:47:13 -0500 |
commit | c71c03bda1e86c9d5198c5d83f712e695c4f2a1e (patch) | |
tree | ecb166cb3e2b7e2adb3b5e292245fefd23381ac8 /drivers/usb/musb/blackfin.c | |
parent | ea53c912f8a86a8567697115b6a0d8152beee5c8 (diff) | |
parent | 6a00f206debf8a5c8899055726ad127dbeeed098 (diff) |
Merge branch 'mpi-master' into wip-k-fmlpwip-k-fmlp
Conflicts:
litmus/sched_cedf.c
Diffstat (limited to 'drivers/usb/musb/blackfin.c')
-rw-r--r-- | drivers/usb/musb/blackfin.c | 267 |
1 files changed, 232 insertions, 35 deletions
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index b611420a8050..ae8c39617743 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c | |||
@@ -15,17 +15,27 @@ | |||
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include <linux/gpio.h> | 16 | #include <linux/gpio.h> |
17 | #include <linux/io.h> | 17 | #include <linux/io.h> |
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/dma-mapping.h> | ||
18 | 20 | ||
19 | #include <asm/cacheflush.h> | 21 | #include <asm/cacheflush.h> |
20 | 22 | ||
21 | #include "musb_core.h" | 23 | #include "musb_core.h" |
24 | #include "musbhsdma.h" | ||
22 | #include "blackfin.h" | 25 | #include "blackfin.h" |
23 | 26 | ||
27 | struct bfin_glue { | ||
28 | struct device *dev; | ||
29 | struct platform_device *musb; | ||
30 | }; | ||
31 | #define glue_to_musb(g) platform_get_drvdata(g->musb) | ||
32 | |||
24 | /* | 33 | /* |
25 | * Load an endpoint's FIFO | 34 | * Load an endpoint's FIFO |
26 | */ | 35 | */ |
27 | void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) | 36 | void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) |
28 | { | 37 | { |
38 | struct musb *musb = hw_ep->musb; | ||
29 | void __iomem *fifo = hw_ep->fifo; | 39 | void __iomem *fifo = hw_ep->fifo; |
30 | void __iomem *epio = hw_ep->regs; | 40 | void __iomem *epio = hw_ep->regs; |
31 | u8 epnum = hw_ep->epnum; | 41 | u8 epnum = hw_ep->epnum; |
@@ -34,7 +44,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) | |||
34 | 44 | ||
35 | musb_writew(epio, MUSB_TXCOUNT, len); | 45 | musb_writew(epio, MUSB_TXCOUNT, len); |
36 | 46 | ||
37 | DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", | 47 | dev_dbg(musb->controller, "TX ep%d fifo %p count %d buf %p, epio %p\n", |
38 | hw_ep->epnum, fifo, len, src, epio); | 48 | hw_ep->epnum, fifo, len, src, epio); |
39 | 49 | ||
40 | dump_fifo_data(src, len); | 50 | dump_fifo_data(src, len); |
@@ -89,6 +99,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) | |||
89 | */ | 99 | */ |
90 | void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) | 100 | void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) |
91 | { | 101 | { |
102 | struct musb *musb = hw_ep->musb; | ||
92 | void __iomem *fifo = hw_ep->fifo; | 103 | void __iomem *fifo = hw_ep->fifo; |
93 | u8 epnum = hw_ep->epnum; | 104 | u8 epnum = hw_ep->epnum; |
94 | 105 | ||
@@ -145,7 +156,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) | |||
145 | *(dst + len - 1) = (u8)inw((unsigned long)fifo + 4); | 156 | *(dst + len - 1) = (u8)inw((unsigned long)fifo + 4); |
146 | } | 157 | } |
147 | } | 158 | } |
148 | DBG(4, "%cX ep%d fifo %p count %d buf %p\n", | 159 | dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", |
149 | 'R', hw_ep->epnum, fifo, len, dst); | 160 | 'R', hw_ep->epnum, fifo, len, dst); |
150 | 161 | ||
151 | dump_fifo_data(dst, len); | 162 | dump_fifo_data(dst, len); |
@@ -171,8 +182,9 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci) | |||
171 | } | 182 | } |
172 | 183 | ||
173 | /* Start sampling ID pin, when plug is removed from MUSB */ | 184 | /* Start sampling ID pin, when plug is removed from MUSB */ |
174 | if (is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE | 185 | if ((is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE |
175 | || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { | 186 | || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) || |
187 | (musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) { | ||
176 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | 188 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); |
177 | musb->a_wait_bcon = TIMER_DELAY; | 189 | musb->a_wait_bcon = TIMER_DELAY; |
178 | } | 190 | } |
@@ -269,15 +281,17 @@ static void musb_conn_timer_handler(unsigned long _musb) | |||
269 | } | 281 | } |
270 | break; | 282 | break; |
271 | default: | 283 | default: |
272 | DBG(1, "%s state not handled\n", otg_state_string(musb)); | 284 | dev_dbg(musb->controller, "%s state not handled\n", |
285 | otg_state_string(musb->xceiv->state)); | ||
273 | break; | 286 | break; |
274 | } | 287 | } |
275 | spin_unlock_irqrestore(&musb->lock, flags); | 288 | spin_unlock_irqrestore(&musb->lock, flags); |
276 | 289 | ||
277 | DBG(4, "state is %s\n", otg_state_string(musb)); | 290 | dev_dbg(musb->controller, "state is %s\n", |
291 | otg_state_string(musb->xceiv->state)); | ||
278 | } | 292 | } |
279 | 293 | ||
280 | void musb_platform_enable(struct musb *musb) | 294 | static void bfin_musb_enable(struct musb *musb) |
281 | { | 295 | { |
282 | if (!is_otg_enabled(musb) && is_host_enabled(musb)) { | 296 | if (!is_otg_enabled(musb) && is_host_enabled(musb)) { |
283 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | 297 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); |
@@ -285,66 +299,67 @@ void musb_platform_enable(struct musb *musb) | |||
285 | } | 299 | } |
286 | } | 300 | } |
287 | 301 | ||
288 | void musb_platform_disable(struct musb *musb) | 302 | static void bfin_musb_disable(struct musb *musb) |
289 | { | 303 | { |
290 | } | 304 | } |
291 | 305 | ||
292 | static void bfin_set_vbus(struct musb *musb, int is_on) | 306 | static void bfin_musb_set_vbus(struct musb *musb, int is_on) |
293 | { | 307 | { |
294 | int value = musb->config->gpio_vrsel_active; | 308 | int value = musb->config->gpio_vrsel_active; |
295 | if (!is_on) | 309 | if (!is_on) |
296 | value = !value; | 310 | value = !value; |
297 | gpio_set_value(musb->config->gpio_vrsel, value); | 311 | gpio_set_value(musb->config->gpio_vrsel, value); |
298 | 312 | ||
299 | DBG(1, "VBUS %s, devctl %02x " | 313 | dev_dbg(musb->controller, "VBUS %s, devctl %02x " |
300 | /* otg %3x conf %08x prcm %08x */ "\n", | 314 | /* otg %3x conf %08x prcm %08x */ "\n", |
301 | otg_state_string(musb), | 315 | otg_state_string(musb->xceiv->state), |
302 | musb_readb(musb->mregs, MUSB_DEVCTL)); | 316 | musb_readb(musb->mregs, MUSB_DEVCTL)); |
303 | } | 317 | } |
304 | 318 | ||
305 | static int bfin_set_power(struct otg_transceiver *x, unsigned mA) | 319 | static int bfin_musb_set_power(struct otg_transceiver *x, unsigned mA) |
306 | { | 320 | { |
307 | return 0; | 321 | return 0; |
308 | } | 322 | } |
309 | 323 | ||
310 | void musb_platform_try_idle(struct musb *musb, unsigned long timeout) | 324 | static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout) |
311 | { | 325 | { |
312 | if (!is_otg_enabled(musb) && is_host_enabled(musb)) | 326 | if (!is_otg_enabled(musb) && is_host_enabled(musb)) |
313 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); | 327 | mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY); |
314 | } | 328 | } |
315 | 329 | ||
316 | int musb_platform_get_vbus_status(struct musb *musb) | 330 | static int bfin_musb_vbus_status(struct musb *musb) |
317 | { | 331 | { |
318 | return 0; | 332 | return 0; |
319 | } | 333 | } |
320 | 334 | ||
321 | int musb_platform_set_mode(struct musb *musb, u8 musb_mode) | 335 | static int bfin_musb_set_mode(struct musb *musb, u8 musb_mode) |
322 | { | 336 | { |
323 | return -EIO; | 337 | return -EIO; |
324 | } | 338 | } |
325 | 339 | ||
326 | int __init musb_platform_init(struct musb *musb, void *board_data) | 340 | static int bfin_musb_adjust_channel_params(struct dma_channel *channel, |
341 | u16 packet_sz, u8 *mode, | ||
342 | dma_addr_t *dma_addr, u32 *len) | ||
327 | { | 343 | { |
344 | struct musb_dma_channel *musb_channel = channel->private_data; | ||
328 | 345 | ||
329 | /* | 346 | /* |
330 | * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE | 347 | * Anomaly 05000450 might cause data corruption when using DMA |
331 | * and OTG HOST modes, while rev 1.1 and greater require PE7 to | 348 | * MODE 1 transmits with short packet. So to work around this, |
332 | * be low for DEVICE mode and high for HOST mode. We set it high | 349 | * we truncate all MODE 1 transfers down to a multiple of the |
333 | * here because we are in host mode | 350 | * max packet size, and then do the last short packet transfer |
351 | * (if there is any) using MODE 0. | ||
334 | */ | 352 | */ |
335 | 353 | if (ANOMALY_05000450) { | |
336 | if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { | 354 | if (musb_channel->transmit && *mode == 1) |
337 | printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d \n", | 355 | *len = *len - (*len % packet_sz); |
338 | musb->config->gpio_vrsel); | ||
339 | return -ENODEV; | ||
340 | } | 356 | } |
341 | gpio_direction_output(musb->config->gpio_vrsel, 0); | ||
342 | 357 | ||
343 | usb_nop_xceiv_register(); | 358 | return 0; |
344 | musb->xceiv = otg_get_transceiver(); | 359 | } |
345 | if (!musb->xceiv) | ||
346 | return -ENODEV; | ||
347 | 360 | ||
361 | static void bfin_musb_reg_init(struct musb *musb) | ||
362 | { | ||
348 | if (ANOMALY_05000346) { | 363 | if (ANOMALY_05000346) { |
349 | bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); | 364 | bfin_write_USB_APHY_CALIB(ANOMALY_05000346_value); |
350 | SSYNC(); | 365 | SSYNC(); |
@@ -356,7 +371,8 @@ int __init musb_platform_init(struct musb *musb, void *board_data) | |||
356 | } | 371 | } |
357 | 372 | ||
358 | /* Configure PLL oscillator register */ | 373 | /* Configure PLL oscillator register */ |
359 | bfin_write_USB_PLLOSC_CTRL(0x30a8); | 374 | bfin_write_USB_PLLOSC_CTRL(0x3080 | |
375 | ((480/musb->config->clkin) << 1)); | ||
360 | SSYNC(); | 376 | SSYNC(); |
361 | 377 | ||
362 | bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); | 378 | bfin_write_USB_SRP_CLKDIV((get_sclk()/1000) / 32 - 1); |
@@ -378,24 +394,205 @@ int __init musb_platform_init(struct musb *musb, void *board_data) | |||
378 | EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | | 394 | EP2_RX_ENA | EP3_RX_ENA | EP4_RX_ENA | |
379 | EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); | 395 | EP5_RX_ENA | EP6_RX_ENA | EP7_RX_ENA); |
380 | SSYNC(); | 396 | SSYNC(); |
397 | } | ||
398 | |||
399 | static int bfin_musb_init(struct musb *musb) | ||
400 | { | ||
401 | |||
402 | /* | ||
403 | * Rev 1.0 BF549 EZ-KITs require PE7 to be high for both DEVICE | ||
404 | * and OTG HOST modes, while rev 1.1 and greater require PE7 to | ||
405 | * be low for DEVICE mode and high for HOST mode. We set it high | ||
406 | * here because we are in host mode | ||
407 | */ | ||
408 | |||
409 | if (gpio_request(musb->config->gpio_vrsel, "USB_VRSEL")) { | ||
410 | printk(KERN_ERR "Failed ro request USB_VRSEL GPIO_%d\n", | ||
411 | musb->config->gpio_vrsel); | ||
412 | return -ENODEV; | ||
413 | } | ||
414 | gpio_direction_output(musb->config->gpio_vrsel, 0); | ||
415 | |||
416 | usb_nop_xceiv_register(); | ||
417 | musb->xceiv = otg_get_transceiver(); | ||
418 | if (!musb->xceiv) { | ||
419 | gpio_free(musb->config->gpio_vrsel); | ||
420 | return -ENODEV; | ||
421 | } | ||
422 | |||
423 | bfin_musb_reg_init(musb); | ||
381 | 424 | ||
382 | if (is_host_enabled(musb)) { | 425 | if (is_host_enabled(musb)) { |
383 | musb->board_set_vbus = bfin_set_vbus; | ||
384 | setup_timer(&musb_conn_timer, | 426 | setup_timer(&musb_conn_timer, |
385 | musb_conn_timer_handler, (unsigned long) musb); | 427 | musb_conn_timer_handler, (unsigned long) musb); |
386 | } | 428 | } |
387 | if (is_peripheral_enabled(musb)) | 429 | if (is_peripheral_enabled(musb)) |
388 | musb->xceiv->set_power = bfin_set_power; | 430 | musb->xceiv->set_power = bfin_musb_set_power; |
389 | 431 | ||
390 | musb->isr = blackfin_interrupt; | 432 | musb->isr = blackfin_interrupt; |
433 | musb->double_buffer_not_ok = true; | ||
391 | 434 | ||
392 | return 0; | 435 | return 0; |
393 | } | 436 | } |
394 | 437 | ||
395 | int musb_platform_exit(struct musb *musb) | 438 | static int bfin_musb_exit(struct musb *musb) |
396 | { | 439 | { |
397 | |||
398 | gpio_free(musb->config->gpio_vrsel); | 440 | gpio_free(musb->config->gpio_vrsel); |
399 | 441 | ||
442 | otg_put_transceiver(musb->xceiv); | ||
443 | usb_nop_xceiv_unregister(); | ||
444 | return 0; | ||
445 | } | ||
446 | |||
447 | static const struct musb_platform_ops bfin_ops = { | ||
448 | .init = bfin_musb_init, | ||
449 | .exit = bfin_musb_exit, | ||
450 | |||
451 | .enable = bfin_musb_enable, | ||
452 | .disable = bfin_musb_disable, | ||
453 | |||
454 | .set_mode = bfin_musb_set_mode, | ||
455 | .try_idle = bfin_musb_try_idle, | ||
456 | |||
457 | .vbus_status = bfin_musb_vbus_status, | ||
458 | .set_vbus = bfin_musb_set_vbus, | ||
459 | |||
460 | .adjust_channel_params = bfin_musb_adjust_channel_params, | ||
461 | }; | ||
462 | |||
463 | static u64 bfin_dmamask = DMA_BIT_MASK(32); | ||
464 | |||
465 | static int __init bfin_probe(struct platform_device *pdev) | ||
466 | { | ||
467 | struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data; | ||
468 | struct platform_device *musb; | ||
469 | struct bfin_glue *glue; | ||
470 | |||
471 | int ret = -ENOMEM; | ||
472 | |||
473 | glue = kzalloc(sizeof(*glue), GFP_KERNEL); | ||
474 | if (!glue) { | ||
475 | dev_err(&pdev->dev, "failed to allocate glue context\n"); | ||
476 | goto err0; | ||
477 | } | ||
478 | |||
479 | musb = platform_device_alloc("musb-hdrc", -1); | ||
480 | if (!musb) { | ||
481 | dev_err(&pdev->dev, "failed to allocate musb device\n"); | ||
482 | goto err1; | ||
483 | } | ||
484 | |||
485 | musb->dev.parent = &pdev->dev; | ||
486 | musb->dev.dma_mask = &bfin_dmamask; | ||
487 | musb->dev.coherent_dma_mask = bfin_dmamask; | ||
488 | |||
489 | glue->dev = &pdev->dev; | ||
490 | glue->musb = musb; | ||
491 | |||
492 | pdata->platform_ops = &bfin_ops; | ||
493 | |||
494 | platform_set_drvdata(pdev, glue); | ||
495 | |||
496 | ret = platform_device_add_resources(musb, pdev->resource, | ||
497 | pdev->num_resources); | ||
498 | if (ret) { | ||
499 | dev_err(&pdev->dev, "failed to add resources\n"); | ||
500 | goto err2; | ||
501 | } | ||
502 | |||
503 | ret = platform_device_add_data(musb, pdata, sizeof(*pdata)); | ||
504 | if (ret) { | ||
505 | dev_err(&pdev->dev, "failed to add platform_data\n"); | ||
506 | goto err2; | ||
507 | } | ||
508 | |||
509 | ret = platform_device_add(musb); | ||
510 | if (ret) { | ||
511 | dev_err(&pdev->dev, "failed to register musb device\n"); | ||
512 | goto err2; | ||
513 | } | ||
514 | |||
515 | return 0; | ||
516 | |||
517 | err2: | ||
518 | platform_device_put(musb); | ||
519 | |||
520 | err1: | ||
521 | kfree(glue); | ||
522 | |||
523 | err0: | ||
524 | return ret; | ||
525 | } | ||
526 | |||
527 | static int __exit bfin_remove(struct platform_device *pdev) | ||
528 | { | ||
529 | struct bfin_glue *glue = platform_get_drvdata(pdev); | ||
530 | |||
531 | platform_device_del(glue->musb); | ||
532 | platform_device_put(glue->musb); | ||
533 | kfree(glue); | ||
534 | |||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | #ifdef CONFIG_PM | ||
539 | static int bfin_suspend(struct device *dev) | ||
540 | { | ||
541 | struct bfin_glue *glue = dev_get_drvdata(dev); | ||
542 | struct musb *musb = glue_to_musb(glue); | ||
543 | |||
544 | if (is_host_active(musb)) | ||
545 | /* | ||
546 | * During hibernate gpio_vrsel will change from high to low | ||
547 | * low which will generate wakeup event resume the system | ||
548 | * immediately. Set it to 0 before hibernate to avoid this | ||
549 | * wakeup event. | ||
550 | */ | ||
551 | gpio_set_value(musb->config->gpio_vrsel, 0); | ||
552 | |||
400 | return 0; | 553 | return 0; |
401 | } | 554 | } |
555 | |||
556 | static int bfin_resume(struct device *dev) | ||
557 | { | ||
558 | struct bfin_glue *glue = dev_get_drvdata(dev); | ||
559 | struct musb *musb = glue_to_musb(glue); | ||
560 | |||
561 | bfin_musb_reg_init(musb); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static struct dev_pm_ops bfin_pm_ops = { | ||
567 | .suspend = bfin_suspend, | ||
568 | .resume = bfin_resume, | ||
569 | }; | ||
570 | |||
571 | #define DEV_PM_OPS &bfin_pm_ops | ||
572 | #else | ||
573 | #define DEV_PM_OPS NULL | ||
574 | #endif | ||
575 | |||
576 | static struct platform_driver bfin_driver = { | ||
577 | .remove = __exit_p(bfin_remove), | ||
578 | .driver = { | ||
579 | .name = "musb-blackfin", | ||
580 | .pm = DEV_PM_OPS, | ||
581 | }, | ||
582 | }; | ||
583 | |||
584 | MODULE_DESCRIPTION("Blackfin MUSB Glue Layer"); | ||
585 | MODULE_AUTHOR("Bryan Wy <cooloney@kernel.org>"); | ||
586 | MODULE_LICENSE("GPL v2"); | ||
587 | |||
588 | static int __init bfin_init(void) | ||
589 | { | ||
590 | return platform_driver_probe(&bfin_driver, bfin_probe); | ||
591 | } | ||
592 | subsys_initcall(bfin_init); | ||
593 | |||
594 | static void __exit bfin_exit(void) | ||
595 | { | ||
596 | platform_driver_unregister(&bfin_driver); | ||
597 | } | ||
598 | module_exit(bfin_exit); | ||