static int msm_otg_set_peripheral(struct otg_transceiver *xceiv, struct usb_gadget *gadget) { struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg); if (!dev || (dev != the_msm_otg)) return -ENODEV; if (!gadget) { msm_otg_start_peripheral(xceiv, 0); dev->otg.gadget = 0; disable_sess_valid(dev); return 0; } dev->otg.gadget = gadget; enable_sess_valid(dev); pr_info("peripheral driver registered w/ tranceiver\n"); if (is_b_sess_vld()) msm_otg_start_peripheral(&dev->otg, 1); else if (is_host()) msm_otg_start_host(&dev->otg, 1); else msm_otg_suspend(dev); return 0; }
static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); struct otg_transceiver *otg = &motg->otg; switch (otg->state) { case OTG_STATE_UNDEFINED: dev_dbg(otg->dev, "OTG_STATE_UNDEFINED state\n"); msm_otg_reset(otg); msm_otg_init_sm(motg); otg->state = OTG_STATE_B_IDLE; /* FALL THROUGH */ case OTG_STATE_B_IDLE: dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n"); if (!test_bit(ID, &motg->inputs) && otg->host) { /* disable BSV bit */ writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); msm_otg_start_host(otg, 1); otg->state = OTG_STATE_A_HOST; } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { msm_otg_start_peripheral(otg, 1); otg->state = OTG_STATE_B_PERIPHERAL; } pm_runtime_put_sync(otg->dev); break; case OTG_STATE_B_PERIPHERAL: dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); if (!test_bit(B_SESS_VLD, &motg->inputs) || !test_bit(ID, &motg->inputs)) { msm_otg_start_peripheral(otg, 0); otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg); schedule_work(w); } break; case OTG_STATE_A_HOST: dev_dbg(otg->dev, "OTG_STATE_A_HOST state\n"); if (test_bit(ID, &motg->inputs)) { msm_otg_start_host(otg, 0); otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg); schedule_work(w); } break; default: break; } }
static int msm_otg_set_peripheral(struct otg_transceiver *xceiv, struct usb_gadget *gadget) { struct msm_otg *dev = container_of(xceiv, struct msm_otg, otg); if (!dev || (dev != the_msm_otg)) return -ENODEV; if (!gadget) { msm_otg_start_peripheral(xceiv, 0); dev->otg.gadget = 0; disable_sess_valid(dev); if (dev->pmic_notif_supp && dev->pdata->pmic_unregister_vbus_sn) dev->pdata->pmic_unregister_vbus_sn (&msm_otg_set_vbus_state); return 0; } dev->otg.gadget = gadget; enable_sess_valid(dev); if (dev->pmic_notif_supp && dev->pdata->pmic_register_vbus_sn) dev->pdata->pmic_register_vbus_sn(&msm_otg_set_vbus_state); pr_info("peripheral driver registered w/ tranceiver\n"); wake_lock(&dev->wlock); queue_work(dev->wq, &dev->sm_work); return 0; }
static irqreturn_t msm_otg_irq(int irq, void *data) { struct msm_otg *dev = data; u32 otgsc = 0; if (dev->in_lpm) { msm_otg_resume(dev); return IRQ_HANDLED; } otgsc = readl(USB_OTGSC); if (!otgsc & OTGSC_INTR_STS_MASK) return IRQ_HANDLED; if ((otgsc & OTGSC_IDIS) && (otgsc & OTGSC_IDIE)) { pr_info("ID -> (%s)\n", (otgsc & OTGSC_ID) ? "B" : "A"); msm_otg_start_host(&dev->otg, is_host()); } else if ((otgsc & OTGSC_BSVIS) && (otgsc & OTGSC_BSVIE)) { pr_info("VBUS - (%s)\n", otgsc & OTGSC_BSV ? "ON" : "OFF"); if (!is_host()) msm_otg_start_peripheral(&dev->otg, is_b_sess_vld()); } writel(otgsc, USB_OTGSC); return IRQ_HANDLED; }
static int msm_otg_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget) { struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy); /* * Fail peripheral registration if this board can support * only host configuration. */ if (motg->pdata->mode == USB_DR_MODE_HOST) { dev_info(otg->usb_phy->dev, "Peripheral mode is not supported\n"); return -ENODEV; } if (!gadget) { if (otg->state == OTG_STATE_B_PERIPHERAL) { pm_runtime_get_sync(otg->usb_phy->dev); msm_otg_start_peripheral(otg->usb_phy, 0); otg->gadget = NULL; otg->state = OTG_STATE_UNDEFINED; schedule_work(&motg->sm_work); } else { otg->gadget = NULL; } return 0; } otg->gadget = gadget; dev_dbg(otg->usb_phy->dev, "peripheral driver registered w/ tranceiver\n"); /* * Kick the state machine work, if host is not supported * or host is already registered with us. */ if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL || motg->pdata->mode == USB_DR_MODE_OTG || otg->host) { pm_runtime_get_sync(otg->usb_phy->dev); schedule_work(&motg->sm_work); } return 0; }
static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *motg = container_of(w, struct msm_otg, sm_work); struct usb_otg *otg = motg->phy.otg; switch (otg->state) { case OTG_STATE_UNDEFINED: dev_dbg(otg->usb_phy->dev, "OTG_STATE_UNDEFINED state\n"); msm_otg_reset(otg->usb_phy); msm_otg_init_sm(motg); otg->state = OTG_STATE_B_IDLE; /* FALL THROUGH */ case OTG_STATE_B_IDLE: dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_IDLE state\n"); if (!test_bit(ID, &motg->inputs) && otg->host) { /* disable BSV bit */ writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); msm_otg_start_host(otg->usb_phy, 1); otg->state = OTG_STATE_A_HOST; } else if (test_bit(B_SESS_VLD, &motg->inputs)) { switch (motg->chg_state) { case USB_CHG_STATE_UNDEFINED: msm_chg_detect_work(&motg->chg_work.work); break; case USB_CHG_STATE_DETECTED: switch (motg->chg_type) { case USB_DCP_CHARGER: msm_otg_notify_charger(motg, IDEV_CHG_MAX); break; case USB_CDP_CHARGER: msm_otg_notify_charger(motg, IDEV_CHG_MAX); msm_otg_start_peripheral(otg->usb_phy, 1); otg->state = OTG_STATE_B_PERIPHERAL; break; case USB_SDP_CHARGER: msm_otg_notify_charger(motg, IUNIT); msm_otg_start_peripheral(otg->usb_phy, 1); otg->state = OTG_STATE_B_PERIPHERAL; break; default: break; } break; default: break; } } else { /* * If charger detection work is pending, decrement * the pm usage counter to balance with the one that * is incremented in charger detection work. */ if (cancel_delayed_work_sync(&motg->chg_work)) { pm_runtime_put_sync(otg->usb_phy->dev); msm_otg_reset(otg->usb_phy); } msm_otg_notify_charger(motg, 0); motg->chg_state = USB_CHG_STATE_UNDEFINED; motg->chg_type = USB_INVALID_CHARGER; } if (otg->state == OTG_STATE_B_IDLE) pm_runtime_put_sync(otg->usb_phy->dev); break; case OTG_STATE_B_PERIPHERAL: dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_PERIPHERAL state\n"); if (!test_bit(B_SESS_VLD, &motg->inputs) || !test_bit(ID, &motg->inputs)) { msm_otg_notify_charger(motg, 0); msm_otg_start_peripheral(otg->usb_phy, 0); motg->chg_state = USB_CHG_STATE_UNDEFINED; motg->chg_type = USB_INVALID_CHARGER; otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg->usb_phy); schedule_work(w); } break; case OTG_STATE_A_HOST: dev_dbg(otg->usb_phy->dev, "OTG_STATE_A_HOST state\n"); if (test_bit(ID, &motg->inputs)) { msm_otg_start_host(otg->usb_phy, 0); otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg->usb_phy); schedule_work(w); } break; default: break; } }
static void msm_otg_sm_work(struct work_struct *w) { struct msm_otg *dev = container_of(w, struct msm_otg, sm_work); int ret; int work = 0; enum usb_otg_state state; if (atomic_read(&dev->in_lpm)) msm_otg_set_suspend(&dev->otg, 0); spin_lock_irq(&dev->lock); state = dev->otg.state; spin_unlock_irq(&dev->lock); pr_debug("state: %s\n", state_string(state)); switch (state) { case OTG_STATE_UNDEFINED: if (!dev->otg.host || !is_host()) set_bit(ID, &dev->inputs); if (dev->otg.gadget && is_b_sess_vld()) set_bit(B_SESS_VLD, &dev->inputs); spin_lock_irq(&dev->lock); if (test_bit(ID, &dev->inputs)) { dev->otg.state = OTG_STATE_B_IDLE; } else { set_bit(A_BUS_REQ, &dev->inputs); dev->otg.state = OTG_STATE_A_IDLE; } spin_unlock_irq(&dev->lock); work = 1; break; case OTG_STATE_B_IDLE: dev->otg.default_a = 0; if (!test_bit(ID, &dev->inputs)) { pr_debug("!id\n"); clear_bit(B_BUS_REQ, &dev->inputs); otg_reset(dev, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("b_sess_vld\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 1); } else if (test_bit(B_BUS_REQ, &dev->inputs)) { pr_debug("b_sess_end && b_bus_req\n"); ret = msm_otg_start_srp(&dev->otg); if (ret < 0) { /* notify user space */ clear_bit(B_BUS_REQ, &dev->inputs); work = 1; break; } spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_SRP_INIT; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TB_SRP_FAIL, B_SRP_FAIL); break; } else { pr_debug("entering into lpm\n"); msm_otg_suspend(dev); } break; case OTG_STATE_B_SRP_INIT: if (!test_bit(ID, &dev->inputs) || test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || b_sess_vld\n"); msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (test_bit(B_SRP_FAIL, &dev->tmouts)) { pr_debug("b_srp_fail\n"); /* notify user space */ clear_bit(B_BUS_REQ, &dev->inputs); clear_bit(B_SRP_FAIL, &dev->tmouts); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); dev->b_last_se0_sess = jiffies; work = 1; } break; case OTG_STATE_B_PERIPHERAL: if (!test_bit(ID, &dev->inputs) || !test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || !b_sess_vld\n"); clear_bit(B_BUS_REQ, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->b_last_se0_sess = jiffies; /* Workaround: Reset phy after session */ otg_reset(dev, 1); /* come back later to put hardware in * lpm. This removes addition checks in * suspend routine for missing BSV */ work = 1; } else if (test_bit(B_BUS_REQ, &dev->inputs) && dev->otg.gadget->b_hnp_enable && test_bit(A_BUS_SUSPEND, &dev->inputs)) { pr_debug("b_bus_req && b_hnp_en && a_bus_suspend\n"); msm_otg_start_timer(dev, TB_ASE0_BRST, B_ASE0_BRST); msm_otg_start_peripheral(&dev->otg, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_WAIT_ACON; spin_unlock_irq(&dev->lock); /* start HCD even before A-device enable * pull-up to meet HNP timings. */ dev->otg.host->is_b_host = 1; msm_otg_start_host(&dev->otg, REQUEST_START); } break; case OTG_STATE_B_WAIT_ACON: if (!test_bit(ID, &dev->inputs) || !test_bit(B_SESS_VLD, &dev->inputs)) { pr_debug("!id || !b_sess_vld\n"); msm_otg_del_timer(dev); /* A-device is physically disconnected during * HNP. Remove HCD. */ msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->otg.host->is_b_host = 0; clear_bit(B_BUS_REQ, &dev->inputs); clear_bit(A_BUS_SUSPEND, &dev->inputs); dev->b_last_se0_sess = jiffies; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); /* Workaround: Reset phy after session */ otg_reset(dev, 1); work = 1; } else if (test_bit(A_CONN, &dev->inputs)) { pr_debug("a_conn\n"); clear_bit(A_BUS_SUSPEND, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_HOST; spin_unlock_irq(&dev->lock); } else if (test_bit(B_ASE0_BRST, &dev->tmouts)) { /* TODO: A-device may send reset after * enabling HNP; a_bus_resume case is * not handled for now. */ pr_debug("b_ase0_brst_tmout\n"); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->otg.host->is_b_host = 0; clear_bit(B_ASE0_BRST, &dev->tmouts); clear_bit(A_BUS_SUSPEND, &dev->inputs); clear_bit(B_BUS_REQ, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); } break; case OTG_STATE_B_HOST: /* B_BUS_REQ is not exposed to user space. So * it must be A_CONN for now. */ if (!test_bit(B_BUS_REQ, &dev->inputs) || !test_bit(A_CONN, &dev->inputs)) { pr_debug("!b_bus_req || !a_conn\n"); clear_bit(A_CONN, &dev->inputs); clear_bit(B_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, 0); dev->otg.host->is_b_host = 0; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); /* Workaround: Reset phy after session */ otg_reset(dev, 1); work = 1; } break; case OTG_STATE_A_IDLE: dev->otg.default_a = 1; if (test_bit(ID, &dev->inputs)) { pr_debug("id\n"); dev->otg.default_a = 0; otg_reset(dev, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_B_IDLE; spin_unlock_irq(&dev->lock); work = 1; } else if (!test_bit(A_BUS_DROP, &dev->inputs) && (test_bit(A_SRP_DET, &dev->inputs) || test_bit(A_BUS_REQ, &dev->inputs))) { pr_debug("!a_bus_drop && (a_srp_det || a_bus_req)\n"); clear_bit(A_SRP_DET, &dev->inputs); /* Disable SRP detection */ writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) & ~OTGSC_DPIE, USB_OTGSC); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VRISE; spin_unlock_irq(&dev->lock); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 1); msm_otg_start_timer(dev, TA_WAIT_VRISE, A_WAIT_VRISE); /* no need to schedule work now */ } else { pr_debug("No session requested\n"); /* A-device is not providing power on VBUS. * Enable SRP detection. */ writel((readl(USB_OTGSC) & ~OTGSC_INTR_STS_MASK) | OTGSC_DPIE, USB_OTGSC); msm_otg_suspend(dev); } break; case OTG_STATE_A_WAIT_VRISE: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_WAIT_VRISE, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_wait_vrise_tmout\n"); clear_bit(A_BUS_REQ, &dev->inputs); msm_otg_del_timer(dev); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("a_vbus_vld\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); /* Start HCD to detect peripherals. */ msm_otg_start_host(&dev->otg, REQUEST_START); } break; case OTG_STATE_A_WAIT_BCON: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_WAIT_BCON, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_wait_bcon_tmout\n"); msm_otg_del_timer(dev); clear_bit(A_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (test_bit(B_CONN, &dev->inputs)) { pr_debug("b_conn\n"); msm_otg_del_timer(dev); /* HCD is added already. just move to * A_HOST state. */ spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_HOST; spin_unlock_irq(&dev->lock); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); msm_otg_del_timer(dev); msm_otg_start_host(&dev->otg, REQUEST_STOP); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); } break; case OTG_STATE_A_HOST: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs)) { pr_debug("id || a_bus_drop\n"); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); /* no work */ } else if (!test_bit(A_BUS_REQ, &dev->inputs)) { /* a_bus_req is de-asserted when root hub is * suspended or HNP is in progress. */ pr_debug("!a_bus_req\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_SUSPEND; spin_unlock_irq(&dev->lock); if (dev->otg.host->b_hnp_enable) { msm_otg_start_timer(dev, TA_AIDL_BDIS, A_AIDL_BDIS); } else { /* No HNP. Root hub suspended */ msm_otg_suspend(dev); } } else if (!test_bit(B_CONN, &dev->inputs)) { pr_debug("!b_conn\n"); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_SUSPEND: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_AIDL_BDIS, &dev->tmouts)) { pr_debug("id || a_bus_drop || a_aidl_bdis_tmout\n"); msm_otg_del_timer(dev); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); msm_otg_del_timer(dev); clear_bit(B_CONN, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_STOP); } else if (!test_bit(B_CONN, &dev->inputs) && dev->otg.host->b_hnp_enable) { pr_debug("!b_conn && b_hnp_enable"); /* Clear AIDL_BDIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_PERIPHERAL; spin_unlock_irq(&dev->lock); msm_otg_start_host(&dev->otg, REQUEST_HNP_SUSPEND); /* We may come here even when B-dev is physically * disconnected during HNP. We go back to host * role if bus is idle for BIDL_ADIS time. */ dev->otg.gadget->is_a_peripheral = 1; msm_otg_start_peripheral(&dev->otg, 1); } else if (!test_bit(B_CONN, &dev->inputs) && !dev->otg.host->b_hnp_enable) { pr_debug("!b_conn && !b_hnp_enable"); /* bus request is dropped during suspend. * acquire again for next device. */ set_bit(A_BUS_REQ, &dev->inputs); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_PERIPHERAL: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs)) { pr_debug("id || a_bus_drop\n"); /* Clear BIDL_ADIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; /* HCD was suspended before. Stop it now */ msm_otg_start_host(&dev->otg, REQUEST_STOP); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } else if (!test_bit(A_VBUS_VLD, &dev->inputs)) { pr_debug("!a_vbus_vld\n"); /* Clear BIDL_ADIS timer */ msm_otg_del_timer(dev); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_VBUS_ERR; spin_unlock_irq(&dev->lock); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; /* HCD was suspended before. Stop it now */ msm_otg_start_host(&dev->otg, REQUEST_STOP); } else if (test_bit(A_BIDL_ADIS, &dev->tmouts)) { pr_debug("a_bidl_adis_tmout\n"); msm_otg_start_peripheral(&dev->otg, 0); dev->otg.gadget->is_a_peripheral = 0; spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_BCON; spin_unlock_irq(&dev->lock); set_bit(A_BUS_REQ, &dev->inputs); msm_otg_start_host(&dev->otg, REQUEST_HNP_RESUME); msm_otg_start_timer(dev, TA_WAIT_BCON, A_WAIT_BCON); } break; case OTG_STATE_A_WAIT_VFALL: if (test_bit(A_WAIT_VFALL, &dev->tmouts)) { clear_bit(A_VBUS_VLD, &dev->inputs); /* Reset both phy and link */ otg_reset(dev, 1); spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_IDLE; spin_unlock_irq(&dev->lock); work = 1; } break; case OTG_STATE_A_VBUS_ERR: if (test_bit(ID, &dev->inputs) || test_bit(A_BUS_DROP, &dev->inputs) || test_bit(A_CLR_ERR, &dev->inputs)) { spin_lock_irq(&dev->lock); dev->otg.state = OTG_STATE_A_WAIT_VFALL; spin_unlock_irq(&dev->lock); dev->pdata->vbus_power(USB_PHY_INTEGRATED, 0); msm_otg_start_timer(dev, TA_WAIT_VFALL, A_WAIT_VFALL); } break; default: pr_err("invalid OTG state\n"); } if (work) queue_work(dev->wq, &dev->sm_work); /* IRQ/sysfs may queue work. Check work_pending. otherwise * we might endup releasing wakelock after it is acquired * in IRQ/sysfs. */ if (!work_pending(&dev->sm_work) && !hrtimer_active(&dev->timer)) wake_unlock(&dev->wlock); }
static int msm_otg_suspend(struct msm_otg *dev) { unsigned long timeout; int vbus = 0; unsigned otgsc; disable_irq(dev->irq); if (dev->in_lpm) goto out; /* Don't reset if mini-A cable is connected */ if (!is_host()) otg_reset(dev); /* In case of fast plug-in and plug-out inside the otg_reset() the * servicing of BSV is missed (in the window of after phy and link * reset). Handle it if any missing bsv is detected */ if (is_b_sess_vld() && !is_host()) { otgsc = readl(USB_OTGSC); writel(otgsc, USB_OTGSC); pr_info("%s:Process mising BSV\n", __func__); msm_otg_start_peripheral(&dev->otg, 1); enable_irq(dev->irq); return -1; } ulpi_read(dev, 0x14);/* clear PHY interrupt latch register */ /* If there is no pmic notify support turn on phy comparators. */ if (!dev->pmic_notif_supp) ulpi_write(dev, 0x01, 0x30); ulpi_write(dev, 0x08, 0x09);/* turn off PLL on integrated phy */ timeout = jiffies + msecs_to_jiffies(500); disable_phy_clk(); while (!is_phy_clk_disabled()) { if (time_after(jiffies, timeout)) { pr_err("%s: Unable to suspend phy\n", __func__); otg_reset(dev); goto out; } msleep(1); } writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); clk_disable(dev->pclk); if (dev->cclk) clk_disable(dev->cclk); if (device_may_wakeup(dev->otg.dev)) { enable_irq_wake(dev->irq); if (dev->vbus_on_irq) enable_irq_wake(dev->vbus_on_irq); } dev->in_lpm = 1; if (!vbus && dev->pmic_notif_supp) dev->pmic_enable_ldo(0); pr_info("%s: usb in low power mode\n", __func__); out: enable_irq(dev->irq); /* TBD: as there is no bus suspend implemented as of now * it should be dummy check */ return 0; }