diff options
author | Li Jun <b47624@freescale.com> | 2014-03-17 09:12:24 -0400 |
---|---|---|
committer | Nitin Garg <nitin.garg@freescale.com> | 2014-04-16 12:02:35 -0400 |
commit | b3cf01e5acd05132877c865cba91491baac97efd (patch) | |
tree | 4e73b4a4de5d696c12c4203a3414487c27a7fed5 | |
parent | 6a623a35c0e5b268f6f4725c2a39cba9e3346b95 (diff) |
ENGR00307558-7 usb: chipidea: OTG HNP and SRP fsm implementation.
USB OTG interrupt handling and fsm transitions according to USB OTG
and EH 2.0.
Signed-off-by: Li Jun <b47624@freescale.com>
-rw-r--r-- | drivers/usb/chipidea/core.c | 24 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg.c | 9 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.c | 243 | ||||
-rw-r--r-- | drivers/usb/chipidea/otg_fsm.h | 18 | ||||
-rw-r--r-- | drivers/usb/chipidea/udc.c | 7 |
5 files changed, 293 insertions, 8 deletions
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 572403932fc4..8e525b6c023d 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c | |||
@@ -42,7 +42,6 @@ | |||
42 | * - Not Supported: 15 & 16 (ISO) | 42 | * - Not Supported: 15 & 16 (ISO) |
43 | * | 43 | * |
44 | * TODO List | 44 | * TODO List |
45 | * - OTG | ||
46 | * - Interrupt Traffic | 45 | * - Interrupt Traffic |
47 | * - GET_STATUS(device) - always reports 0 | 46 | * - GET_STATUS(device) - always reports 0 |
48 | * - Gadget API (majority of optional features) | 47 | * - Gadget API (majority of optional features) |
@@ -74,6 +73,7 @@ | |||
74 | #include "host.h" | 73 | #include "host.h" |
75 | #include "debug.h" | 74 | #include "debug.h" |
76 | #include "otg.h" | 75 | #include "otg.h" |
76 | #include "otg_fsm.h" | ||
77 | 77 | ||
78 | /* Controller register map */ | 78 | /* Controller register map */ |
79 | static uintptr_t ci_regs_nolpm[] = { | 79 | static uintptr_t ci_regs_nolpm[] = { |
@@ -435,8 +435,14 @@ static irqreturn_t ci_irq(int irq, void *data) | |||
435 | return IRQ_HANDLED; | 435 | return IRQ_HANDLED; |
436 | } | 436 | } |
437 | 437 | ||
438 | if (ci->is_otg) | 438 | if (ci->is_otg) { |
439 | otgsc = hw_read_otgsc(ci, ~0); | 439 | otgsc = hw_read_otgsc(ci, ~0); |
440 | if (ci_otg_is_fsm_mode(ci)) { | ||
441 | ret = ci_otg_fsm_irq(ci); | ||
442 | if (ret == IRQ_HANDLED) | ||
443 | return ret; | ||
444 | } | ||
445 | } | ||
440 | 446 | ||
441 | /* | 447 | /* |
442 | * Handle id change interrupt, it indicates device/host function | 448 | * Handle id change interrupt, it indicates device/host function |
@@ -736,10 +742,13 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
736 | if (ci->role == CI_ROLE_GADGET) | 742 | if (ci->role == CI_ROLE_GADGET) |
737 | ci_handle_vbus_connected(ci); | 743 | ci_handle_vbus_connected(ci); |
738 | 744 | ||
739 | ret = ci_role_start(ci, ci->role); | 745 | if (!ci_otg_is_fsm_mode(ci)) { |
740 | if (ret) { | 746 | ret = ci_role_start(ci, ci->role); |
741 | dev_err(dev, "can't start %s role\n", ci_role(ci)->name); | 747 | if (ret) { |
742 | goto stop; | 748 | dev_err(dev, "can't start %s role\n", |
749 | ci_role(ci)->name); | ||
750 | goto stop; | ||
751 | } | ||
743 | } | 752 | } |
744 | 753 | ||
745 | platform_set_drvdata(pdev, ci); | 754 | platform_set_drvdata(pdev, ci); |
@@ -755,6 +764,9 @@ static int ci_hdrc_probe(struct platform_device *pdev) | |||
755 | pm_runtime_enable(&pdev->dev); | 764 | pm_runtime_enable(&pdev->dev); |
756 | } | 765 | } |
757 | 766 | ||
767 | if (ci_otg_is_fsm_mode(ci)) | ||
768 | ci_hdrc_otg_fsm_start(ci); | ||
769 | |||
758 | setup_timer(&ci->timer, delay_runtime_pm_put_timer, | 770 | setup_timer(&ci->timer, delay_runtime_pm_put_timer, |
759 | (unsigned long)ci); | 771 | (unsigned long)ci); |
760 | ret = dbg_create_files(ci); | 772 | ret = dbg_create_files(ci); |
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index dc7ad4096385..82712fbabadc 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c | |||
@@ -11,8 +11,8 @@ | |||
11 | */ | 11 | */ |
12 | 12 | ||
13 | /* | 13 | /* |
14 | * This file mainly handles otgsc register, it may include OTG operation | 14 | * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP |
15 | * in the future. | 15 | * are also included. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include <linux/usb/otg.h> | 18 | #include <linux/usb/otg.h> |
@@ -120,6 +120,11 @@ static void ci_otg_work(struct work_struct *work) | |||
120 | { | 120 | { |
121 | struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); | 121 | struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work); |
122 | 122 | ||
123 | if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) { | ||
124 | enable_irq(ci->irq); | ||
125 | return; | ||
126 | } | ||
127 | |||
123 | if (ci->id_event) { | 128 | if (ci->id_event) { |
124 | ci->id_event = false; | 129 | ci->id_event = false; |
125 | /* Keep controller active during id switch */ | 130 | /* Keep controller active during id switch */ |
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 51b8a0fff3e5..5f523971ff14 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c | |||
@@ -13,6 +13,10 @@ | |||
13 | /* | 13 | /* |
14 | * This file mainly handles OTG fsm, it includes OTG fsm operations | 14 | * This file mainly handles OTG fsm, it includes OTG fsm operations |
15 | * for HNP and SRP. | 15 | * for HNP and SRP. |
16 | * | ||
17 | * TODO List | ||
18 | * - ADP | ||
19 | * - OTG test device | ||
16 | */ | 20 | */ |
17 | 21 | ||
18 | #include <linux/usb/otg.h> | 22 | #include <linux/usb/otg.h> |
@@ -93,6 +97,33 @@ static void ci_otg_del_timer(struct ci_hdrc *ci, enum ci_otg_fsm_timer_index t) | |||
93 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); | 97 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); |
94 | } | 98 | } |
95 | 99 | ||
100 | /* | ||
101 | * Reduce timer count by 1, and find timeout conditions. | ||
102 | * Called by otg 1ms timer interrupt | ||
103 | */ | ||
104 | static inline int ci_otg_tick_timer(struct ci_hdrc *ci) | ||
105 | { | ||
106 | struct ci_otg_fsm_timer *tmp_timer, *del_tmp; | ||
107 | struct list_head *active_timers = &ci->fsm_timer->active_timers; | ||
108 | int expired = 0; | ||
109 | |||
110 | list_for_each_entry_safe(tmp_timer, del_tmp, active_timers, list) { | ||
111 | tmp_timer->count--; | ||
112 | /* check if timer expires */ | ||
113 | if (!tmp_timer->count) { | ||
114 | list_del(&tmp_timer->list); | ||
115 | tmp_timer->function(ci, tmp_timer->data); | ||
116 | expired = 1; | ||
117 | } | ||
118 | } | ||
119 | |||
120 | /* disable 1ms irq if there is no any timer active */ | ||
121 | if ((expired == 1) && list_empty(active_timers)) | ||
122 | hw_write_otgsc(ci, OTGSC_1MSIE, 0); | ||
123 | |||
124 | return expired; | ||
125 | } | ||
126 | |||
96 | /* The timeout callback function to set time out bit */ | 127 | /* The timeout callback function to set time out bit */ |
97 | static void set_tmout(void *ptr, unsigned long indicator) | 128 | static void set_tmout(void *ptr, unsigned long indicator) |
98 | { | 129 | { |
@@ -395,6 +426,218 @@ static struct otg_fsm_ops ci_otg_ops = { | |||
395 | .start_gadget = ci_otg_start_gadget, | 426 | .start_gadget = ci_otg_start_gadget, |
396 | }; | 427 | }; |
397 | 428 | ||
429 | int ci_otg_fsm_work(struct ci_hdrc *ci) | ||
430 | { | ||
431 | /* | ||
432 | * Don't do fsm transition for B device | ||
433 | * when there is no gadget class driver | ||
434 | */ | ||
435 | if (ci->fsm.id && !(ci->driver) && | ||
436 | ci->transceiver->state < OTG_STATE_A_IDLE) | ||
437 | return 0; | ||
438 | |||
439 | if (otg_statemachine(&ci->fsm)) { | ||
440 | if (ci->transceiver->state == OTG_STATE_A_IDLE) { | ||
441 | /* | ||
442 | * Further state change for cases: | ||
443 | * a_idle to b_idle, or | ||
444 | * a_idle to a_wait_vrise due to ID change(1->0), so | ||
445 | * B-dev becomes A-dev can try to start new session | ||
446 | * consequently; or | ||
447 | * a_idle to a_wait_vrise when power up | ||
448 | */ | ||
449 | if ((ci->fsm.id) || (ci->id_event) || | ||
450 | (ci->fsm.power_up)) { | ||
451 | disable_irq_nosync(ci->irq); | ||
452 | queue_work(ci->wq, &ci->work); | ||
453 | } | ||
454 | if (ci->id_event) | ||
455 | ci->id_event = false; | ||
456 | } else if (ci->transceiver->state == OTG_STATE_B_IDLE) { | ||
457 | if (ci->fsm.b_sess_vld) { | ||
458 | ci->fsm.power_up = 0; | ||
459 | /* | ||
460 | * Further transite to b_periphearl | ||
461 | * when register gadget driver with vbus on | ||
462 | */ | ||
463 | disable_irq_nosync(ci->irq); | ||
464 | queue_work(ci->wq, &ci->work); | ||
465 | } | ||
466 | } | ||
467 | } | ||
468 | return 0; | ||
469 | } | ||
470 | |||
471 | /* | ||
472 | * Update fsm variables in each state if catching expected interrupts, | ||
473 | * called by otg fsm isr. | ||
474 | */ | ||
475 | static void ci_otg_fsm_event(struct ci_hdrc *ci) | ||
476 | { | ||
477 | u32 intr_sts, otg_bsess_vld, port_conn; | ||
478 | struct otg_fsm *fsm = &ci->fsm; | ||
479 | |||
480 | intr_sts = hw_read_intr_status(ci); | ||
481 | otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV); | ||
482 | port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS); | ||
483 | |||
484 | switch (ci->transceiver->state) { | ||
485 | case OTG_STATE_A_WAIT_BCON: | ||
486 | if (port_conn) { | ||
487 | fsm->b_conn = 1; | ||
488 | fsm->a_bus_req = 1; | ||
489 | disable_irq_nosync(ci->irq); | ||
490 | queue_work(ci->wq, &ci->work); | ||
491 | } | ||
492 | break; | ||
493 | case OTG_STATE_B_IDLE: | ||
494 | if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) { | ||
495 | fsm->b_sess_vld = 1; | ||
496 | disable_irq_nosync(ci->irq); | ||
497 | queue_work(ci->wq, &ci->work); | ||
498 | } | ||
499 | break; | ||
500 | case OTG_STATE_B_PERIPHERAL: | ||
501 | if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) { | ||
502 | fsm->a_bus_suspend = 1; | ||
503 | disable_irq_nosync(ci->irq); | ||
504 | queue_work(ci->wq, &ci->work); | ||
505 | } else if (intr_sts & USBi_PCI) { | ||
506 | if (fsm->a_bus_suspend == 1) | ||
507 | fsm->a_bus_suspend = 0; | ||
508 | } | ||
509 | break; | ||
510 | case OTG_STATE_B_HOST: | ||
511 | if ((intr_sts & USBi_PCI) && !port_conn) { | ||
512 | fsm->a_conn = 0; | ||
513 | fsm->b_bus_req = 0; | ||
514 | disable_irq_nosync(ci->irq); | ||
515 | queue_work(ci->wq, &ci->work); | ||
516 | ci_otg_add_timer(ci, B_SESS_VLD); | ||
517 | } | ||
518 | break; | ||
519 | case OTG_STATE_A_PERIPHERAL: | ||
520 | if (intr_sts & USBi_SLI) { | ||
521 | fsm->b_bus_suspend = 1; | ||
522 | /* | ||
523 | * Init a timer to know how long this suspend | ||
524 | * will contine, if time out, indicates B no longer | ||
525 | * wants to be host role | ||
526 | */ | ||
527 | ci_otg_add_timer(ci, A_BIDL_ADIS); | ||
528 | } | ||
529 | |||
530 | if (intr_sts & USBi_URI) | ||
531 | ci_otg_del_timer(ci, A_BIDL_ADIS); | ||
532 | |||
533 | if (intr_sts & USBi_PCI) { | ||
534 | if (fsm->b_bus_suspend == 1) { | ||
535 | ci_otg_del_timer(ci, A_BIDL_ADIS); | ||
536 | fsm->b_bus_suspend = 0; | ||
537 | } | ||
538 | } | ||
539 | break; | ||
540 | case OTG_STATE_A_SUSPEND: | ||
541 | if ((intr_sts & USBi_PCI) && !port_conn) { | ||
542 | fsm->b_conn = 0; | ||
543 | |||
544 | /* if gadget driver is binded */ | ||
545 | if (ci->driver) { | ||
546 | /* A device to be peripheral mode */ | ||
547 | ci->gadget.is_a_peripheral = 1; | ||
548 | } | ||
549 | disable_irq_nosync(ci->irq); | ||
550 | queue_work(ci->wq, &ci->work); | ||
551 | } | ||
552 | break; | ||
553 | case OTG_STATE_A_HOST: | ||
554 | if ((intr_sts & USBi_PCI) && !port_conn) { | ||
555 | fsm->b_conn = 0; | ||
556 | disable_irq_nosync(ci->irq); | ||
557 | queue_work(ci->wq, &ci->work); | ||
558 | } | ||
559 | break; | ||
560 | case OTG_STATE_B_WAIT_ACON: | ||
561 | if ((intr_sts & USBi_PCI) && port_conn) { | ||
562 | fsm->a_conn = 1; | ||
563 | disable_irq_nosync(ci->irq); | ||
564 | queue_work(ci->wq, &ci->work); | ||
565 | } | ||
566 | break; | ||
567 | default: | ||
568 | break; | ||
569 | } | ||
570 | } | ||
571 | |||
572 | /* | ||
573 | * ci_otg_irq - otg fsm related irq handling | ||
574 | * and also update otg fsm variable by monitoring usb host and udc | ||
575 | * state change interrupts. | ||
576 | * @ci: ci_hdrc | ||
577 | */ | ||
578 | irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) | ||
579 | { | ||
580 | irqreturn_t retval = IRQ_NONE; | ||
581 | u32 otgsc, otg_int_src = 0; | ||
582 | struct otg_fsm *fsm = &ci->fsm; | ||
583 | |||
584 | otgsc = hw_read_otgsc(ci, ~0); | ||
585 | otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8); | ||
586 | fsm->id = (otgsc & OTGSC_ID) ? 1 : 0; | ||
587 | |||
588 | if (otg_int_src) { | ||
589 | if (otg_int_src & OTGSC_1MSIS) { | ||
590 | hw_write_otgsc(ci, OTGSC_1MSIS, OTGSC_1MSIS); | ||
591 | retval = ci_otg_tick_timer(ci); | ||
592 | return IRQ_HANDLED; | ||
593 | } else if (otg_int_src & OTGSC_DPIS) { | ||
594 | hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS); | ||
595 | fsm->a_srp_det = 1; | ||
596 | fsm->a_bus_drop = 0; | ||
597 | } else if (otg_int_src & OTGSC_IDIS) { | ||
598 | hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS); | ||
599 | if (fsm->id == 0) { | ||
600 | fsm->a_bus_drop = 0; | ||
601 | fsm->a_bus_req = 1; | ||
602 | ci->id_event = true; | ||
603 | } | ||
604 | } else if (otg_int_src & OTGSC_BSVIS) { | ||
605 | hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS); | ||
606 | if (otgsc & OTGSC_BSV) { | ||
607 | fsm->b_sess_vld = 1; | ||
608 | ci_otg_del_timer(ci, B_SSEND_SRP); | ||
609 | ci_otg_del_timer(ci, B_SRP_FAIL); | ||
610 | fsm->b_ssend_srp = 0; | ||
611 | } else { | ||
612 | fsm->b_sess_vld = 0; | ||
613 | if (fsm->id) | ||
614 | ci_otg_add_timer(ci, B_SSEND_SRP); | ||
615 | } | ||
616 | } else if (otg_int_src & OTGSC_AVVIS) { | ||
617 | hw_write_otgsc(ci, OTGSC_AVVIS, OTGSC_AVVIS); | ||
618 | if (otgsc & OTGSC_AVV) { | ||
619 | fsm->a_vbus_vld = 1; | ||
620 | } else { | ||
621 | fsm->a_vbus_vld = 0; | ||
622 | fsm->b_conn = 0; | ||
623 | } | ||
624 | } | ||
625 | disable_irq_nosync(ci->irq); | ||
626 | queue_work(ci->wq, &ci->work); | ||
627 | return IRQ_HANDLED; | ||
628 | } | ||
629 | |||
630 | ci_otg_fsm_event(ci); | ||
631 | |||
632 | return retval; | ||
633 | } | ||
634 | |||
635 | void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) | ||
636 | { | ||
637 | disable_irq_nosync(ci->irq); | ||
638 | queue_work(ci->wq, &ci->work); | ||
639 | } | ||
640 | |||
398 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) | 641 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) |
399 | { | 642 | { |
400 | int retval = 0; | 643 | int retval = 0; |
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h index 420f081e7e6a..6ec8247d8179 100644 --- a/drivers/usb/chipidea/otg_fsm.h +++ b/drivers/usb/chipidea/otg_fsm.h | |||
@@ -92,6 +92,9 @@ struct ci_otg_fsm_timer_list { | |||
92 | #ifdef CONFIG_USB_OTG_FSM | 92 | #ifdef CONFIG_USB_OTG_FSM |
93 | 93 | ||
94 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); | 94 | int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); |
95 | int ci_otg_fsm_work(struct ci_hdrc *ci); | ||
96 | irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci); | ||
97 | void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci); | ||
95 | 98 | ||
96 | #else | 99 | #else |
97 | 100 | ||
@@ -100,6 +103,21 @@ static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) | |||
100 | return 0; | 103 | return 0; |
101 | } | 104 | } |
102 | 105 | ||
106 | static inline int ci_otg_fsm_work(struct ci_hdrc *ci) | ||
107 | { | ||
108 | return -ENXIO; | ||
109 | } | ||
110 | |||
111 | static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci) | ||
112 | { | ||
113 | return IRQ_NONE; | ||
114 | } | ||
115 | |||
116 | static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci) | ||
117 | { | ||
118 | |||
119 | } | ||
120 | |||
103 | #endif | 121 | #endif |
104 | 122 | ||
105 | #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ | 123 | #endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */ |
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index eeb96fbb9b6b..5c20822160ea 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "bits.h" | 27 | #include "bits.h" |
28 | #include "debug.h" | 28 | #include "debug.h" |
29 | #include "otg.h" | 29 | #include "otg.h" |
30 | #include "otg_fsm.h" | ||
30 | 31 | ||
31 | /* control endpoint description */ | 32 | /* control endpoint description */ |
32 | static const struct usb_endpoint_descriptor | 33 | static const struct usb_endpoint_descriptor |
@@ -1689,6 +1690,12 @@ static int ci_udc_start(struct usb_gadget *gadget, | |||
1689 | return retval; | 1690 | return retval; |
1690 | 1691 | ||
1691 | ci->driver = driver; | 1692 | ci->driver = driver; |
1693 | /* Start otg fsm for B-device */ | ||
1694 | if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) { | ||
1695 | ci_hdrc_otg_fsm_start(ci); | ||
1696 | return retval; | ||
1697 | } | ||
1698 | |||
1692 | pm_runtime_get_sync(&ci->gadget.dev); | 1699 | pm_runtime_get_sync(&ci->gadget.dev); |
1693 | if (ci->vbus_active) { | 1700 | if (ci->vbus_active) { |
1694 | spin_lock_irqsave(&ci->lock, flags); | 1701 | spin_lock_irqsave(&ci->lock, flags); |