static int mv_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) { struct mv_otg *mvotg = container_of(otg_p, struct mv_otg, otg); if (host) { if ((otg_p->state == OTG_STATE_A_HOST) || (otg_p->state == OTG_STATE_A_WAIT_BCON)) { /* stop old host first */ if (otg_p->host) { mv_otg_start_host(mvotg, 0); } /* start the new host right away*/ otg_p->host = host; mv_otg_set_vbus(&mvotg->otg, 1); mv_otg_start_host(mvotg, 1); } } else { if (otg_p->host) { mv_otg_set_vbus(&mvotg->otg, 0); mv_otg_start_host(mvotg, 0); } } otg_p->host = host; return 0; }
static void mv_otg_work(struct work_struct *work) { struct mv_otg *mvotg; struct usb_phy *phy; struct usb_otg *otg; int old_state; mvotg = container_of(to_delayed_work(work), struct mv_otg, work); run: /* work queue is single thread, or we need spin_lock to protect */ phy = &mvotg->phy; otg = phy->otg; old_state = phy->state; if (!mvotg->active) return; mv_otg_update_inputs(mvotg); mv_otg_update_state(mvotg); if (old_state != phy->state) { dev_info(&mvotg->pdev->dev, "change from state %s to %s\n", state_string[old_state], state_string[phy->state]); switch (phy->state) { case OTG_STATE_B_IDLE: otg->default_a = 0; if (old_state == OTG_STATE_B_PERIPHERAL) mv_otg_start_periphrals(mvotg, 0); mv_otg_reset(mvotg); mv_otg_disable(mvotg); break; case OTG_STATE_B_PERIPHERAL: mv_otg_enable(mvotg); mv_otg_start_periphrals(mvotg, 1); break; case OTG_STATE_A_IDLE: otg->default_a = 1; mv_otg_enable(mvotg); if (old_state == OTG_STATE_A_WAIT_VFALL) mv_otg_start_host(mvotg, 0); mv_otg_reset(mvotg); break; case OTG_STATE_A_WAIT_VRISE: mv_otg_set_vbus(otg, 1); break; case OTG_STATE_A_WAIT_BCON: if (old_state != OTG_STATE_A_HOST) mv_otg_start_host(mvotg, 1); mv_otg_set_timer(mvotg, A_WAIT_BCON_TIMER, T_A_WAIT_BCON, mv_otg_timer_await_bcon); /* * Now, we directly enter A_HOST. So set b_conn = 1 * here. In fact, it need host driver to notify us. */ mvotg->otg_ctrl.b_conn = 1; break; case OTG_STATE_A_HOST: break; case OTG_STATE_A_WAIT_VFALL: /* * Now, we has exited A_HOST. So set b_conn = 0 * here. In fact, it need host driver to notify us. */ mvotg->otg_ctrl.b_conn = 0; mv_otg_set_vbus(otg, 0); break; case OTG_STATE_A_VBUS_ERR: break; default: break; } goto run; } }