static ssize_t set_a_bus_req(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mv_otg *mvotg = dev_get_drvdata(dev); if (count > 2) return -1; /* We will use this interface to change to A device */ if (mvotg->phy.state != OTG_STATE_B_IDLE && mvotg->phy.state != OTG_STATE_A_IDLE) return -1; /* The clock may disabled and we need to set irq for ID detected */ mv_otg_enable(mvotg); mv_otg_init_irq(mvotg); if (buf[0] == '1') { mvotg->otg_ctrl.a_bus_req = 1; mvotg->otg_ctrl.a_bus_drop = 0; dev_dbg(&mvotg->pdev->dev, "User request: a_bus_req = 1\n"); if (spin_trylock(&mvotg->wq_lock)) { mv_otg_run_state_machine(mvotg, 0); spin_unlock(&mvotg->wq_lock); } } return count; }
static irqreturn_t mv_otg_inputs_irq(int irq, void *dev) { struct mv_otg *mvotg = dev; /* The clock may disabled at this time */ if (!mvotg->active) { mv_otg_enable(mvotg); mv_otg_init_irq(mvotg); } mv_otg_run_state_machine(mvotg, 0); return IRQ_HANDLED; }
static int mv_otg_notifier_callback(struct notifier_block *nb, unsigned long val, void *v) { struct mv_otg *mvotg = container_of(nb, struct mv_otg, notifier); /* The clock may disabled at this time */ if (!mvotg->active) { mv_otg_enable(mvotg); mv_otg_init_irq(mvotg); } mv_otg_run_state_machine(mvotg, 0); 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; } }