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 /drivers/usb | |
| 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>
Diffstat (limited to 'drivers/usb')
| -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); |
