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_usb_reset(struct usb_phy *phy) { struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int ret; if (!IS_ERR(motg->core_clk)) clk_prepare_enable(motg->core_clk); ret = msm_link_reset(motg); if (ret) { dev_err(phy->dev, "phy_reset failed\n"); return ret; } ret = msm_otg_reset(&motg->phy); if (ret) { dev_err(phy->dev, "link reset failed\n"); return ret; } msleep(100); /* Reset USB PHY after performing USB Link RESET */ msm_phy_reset(motg); if (!IS_ERR(motg->core_clk)) clk_disable_unprepare(motg->core_clk); return 0; }
static int msm_otg_resume(struct msm_otg *motg) { struct usb_phy *phy = &motg->phy; struct usb_bus *bus = phy->otg->host; void __iomem *addr; int cnt = 0; unsigned temp; if (!atomic_read(&motg->in_lpm)) return 0; clk_prepare_enable(motg->pclk); clk_prepare_enable(motg->clk); if (!IS_ERR(motg->core_clk)) clk_prepare_enable(motg->core_clk); if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { addr = USB_PHY_CTRL; if (motg->phy_number) addr = USB_PHY_CTRL2; msm_hsusb_ldo_set_mode(motg, 1); msm_hsusb_config_vddcx(motg, 1); writel(readl(addr) & ~PHY_RETEN, addr); } temp = readl(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel(temp, USB_USBCMD); /* * PHY comes out of low power mode (LPM) in case of wakeup * from asynchronous interrupt. */ if (!(readl(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); while (cnt < PHY_RESUME_TIMEOUT_USEC) { if (!(readl(USB_PORTSC) & PORTSC_PHCD)) break; udelay(1); cnt++; } if (cnt >= PHY_RESUME_TIMEOUT_USEC) { /* * This is a fatal error. Reset the link and * PHY. USB state can not be restored. Re-insertion * of USB cable is the only way to get USB working. */ dev_err(phy->dev, "Unable to resume USB. Re-plugin the cable\n"); msm_otg_reset(phy); } skip_phy_resume: if (device_may_wakeup(phy->dev)) disable_irq_wake(motg->irq); if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); atomic_set(&motg->in_lpm, 0); if (motg->async_int) { motg->async_int = 0; pm_runtime_put(phy->dev); enable_irq(motg->irq); } dev_info(phy->dev, "USB exited from low power mode\n"); return 0; }
static int msm_otg_suspend(struct msm_otg *motg) { struct usb_phy *phy = &motg->phy; struct usb_bus *bus = phy->otg->host; struct msm_otg_platform_data *pdata = motg->pdata; void __iomem *addr; int cnt = 0; if (atomic_read(&motg->in_lpm)) return 0; disable_irq(motg->irq); /* * Chipidea 45-nm PHY suspend sequence: * * Interrupt Latch Register auto-clear feature is not present * in all PHY versions. Latch register is clear on read type. * Clear latch register to avoid spurious wakeup from * low power mode (LPM). * * PHY comparators are disabled when PHY enters into low power * mode (LPM). Keep PHY comparators ON in LPM only when we expect * VBUS/Id notifications from USB PHY. Otherwise turn off USB * PHY comparators. This save significant amount of power. * * PLL is not turned off when PHY enters into low power mode (LPM). * Disable PLL for maximum power savings. */ if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { ulpi_read(phy, 0x14); if (pdata->otg_control == OTG_PHY_CONTROL) ulpi_write(phy, 0x01, 0x30); ulpi_write(phy, 0x08, 0x09); } /* * PHY may take some time or even fail to enter into low power * mode (LPM). Hence poll for 500 msec and reset the PHY and link * in failure case. */ writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC); while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { if (readl(USB_PORTSC) & PORTSC_PHCD) break; udelay(1); cnt++; } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { dev_err(phy->dev, "Unable to suspend PHY\n"); msm_otg_reset(phy); enable_irq(motg->irq); return -ETIMEDOUT; } /* * PHY has capability to generate interrupt asynchronously in low * power mode (LPM). This interrupt is level triggered. So USB IRQ * line must be disabled till async interrupt enable bit is cleared * in USBCMD register. Assert STP (ULPI interface STOP signal) to * block data communication from PHY. */ writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); addr = USB_PHY_CTRL; if (motg->phy_number) addr = USB_PHY_CTRL2; if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) writel(readl(addr) | PHY_RETEN, addr); clk_disable_unprepare(motg->pclk); clk_disable_unprepare(motg->clk); if (!IS_ERR(motg->core_clk)) clk_disable_unprepare(motg->core_clk); if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && motg->pdata->otg_control == OTG_PMIC_CONTROL) { msm_hsusb_ldo_set_mode(motg, 0); msm_hsusb_config_vddcx(motg, 0); } if (device_may_wakeup(phy->dev)) enable_irq_wake(motg->irq); if (bus) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); atomic_set(&motg->in_lpm, 1); enable_irq(motg->irq); dev_info(phy->dev, "USB in low power mode\n"); 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 int msm_otg_resume(struct msm_otg *motg) { struct otg_transceiver *otg = &motg->otg; struct usb_bus *bus = otg->host; int cnt = 0; unsigned temp; if (!atomic_read(&motg->in_lpm)) return 0; clk_enable(motg->pclk); clk_enable(motg->clk); if (motg->core_clk) clk_enable(motg->core_clk); temp = readl(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel(temp, USB_USBCMD); /* * PHY comes out of low power mode (LPM) in case of wakeup * from asynchronous interrupt. */ if (!(readl(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; writel(readl(USB_PORTSC) & ~PORTSC_PHCD, USB_PORTSC); while (cnt < PHY_RESUME_TIMEOUT_USEC) { if (!(readl(USB_PORTSC) & PORTSC_PHCD)) break; udelay(1); cnt++; } if (cnt >= PHY_RESUME_TIMEOUT_USEC) { /* * This is a fatal error. Reset the link and * PHY. USB state can not be restored. Re-insertion * of USB cable is the only way to get USB working. */ dev_err(otg->dev, "Unable to resume USB." "Re-plugin the cable\n"); msm_otg_reset(otg); } skip_phy_resume: if (device_may_wakeup(otg->dev)) disable_irq_wake(motg->irq); if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); if (motg->async_int) { motg->async_int = 0; pm_runtime_put(otg->dev); enable_irq(motg->irq); } atomic_set(&motg->in_lpm, 0); dev_info(otg->dev, "USB exited from low power mode\n"); return 0; }