aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLi Jun <b47624@freescale.com>2014-03-17 09:12:24 -0400
committerNitin Garg <nitin.garg@freescale.com>2014-04-16 12:02:35 -0400
commitb3cf01e5acd05132877c865cba91491baac97efd (patch)
tree4e73b4a4de5d696c12c4203a3414487c27a7fed5
parent6a623a35c0e5b268f6f4725c2a39cba9e3346b95 (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.c24
-rw-r--r--drivers/usb/chipidea/otg.c9
-rw-r--r--drivers/usb/chipidea/otg_fsm.c243
-rw-r--r--drivers/usb/chipidea/otg_fsm.h18
-rw-r--r--drivers/usb/chipidea/udc.c7
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 */
79static uintptr_t ci_regs_nolpm[] = { 79static 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 */
104static 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 */
97static void set_tmout(void *ptr, unsigned long indicator) 128static 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
429int 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 */
475static 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 */
578irqreturn_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
635void 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
398int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci) 641int 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
94int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci); 94int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
95int ci_otg_fsm_work(struct ci_hdrc *ci);
96irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci);
97void 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
106static inline int ci_otg_fsm_work(struct ci_hdrc *ci)
107{
108 return -ENXIO;
109}
110
111static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
112{
113 return IRQ_NONE;
114}
115
116static 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 */
32static const struct usb_endpoint_descriptor 33static 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);