static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init) { int rc = 0; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); const struct msm_usb_host_platform_data *pdata; pdata = mhcd->dev->platform_data; if (!init) { if (pdata && pdata->dock_connect_irq) free_irq(pdata->dock_connect_irq, mhcd); return rc; } mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus"); if (IS_ERR(mhcd->vbus)) { pr_err("Unable to get vbus\n"); return -ENODEV; } if (pdata) { hcd->power_budget = pdata->power_budget; if (pdata->dock_connect_irq) { rc = request_threaded_irq(pdata->dock_connect_irq, NULL, msm_ehci_dock_connect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "msm_ehci_host", mhcd); if (!rc) enable_irq_wake(pdata->dock_connect_irq); } } return rc; }
static int msm_ulpi_write(struct msm_hcd *mhcd, u32 val, u32 reg) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; /* initiate write operation */ writel_relaxed(ULPI_RUN | ULPI_WRITE | ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT); /* wait for completion */ timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS); while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) { if (time_after(jiffies, timeout)) { dev_err(mhcd->dev, "msm_ulpi_write: timeout\n"); dev_err(mhcd->dev, "PORTSC: %08x USBCMD: %08x\n", readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD)); return -ETIMEDOUT; } udelay(1); } return 0; }
static int msm_ehci_phy_reset(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata; u32 val; int ret; ret = msm_ehci_link_clk_reset(mhcd, 1); if (ret) return ret; /* Minimum 10msec delay for block reset as per hardware spec */ usleep_range(10000, 12000); ret = msm_ehci_link_clk_reset(mhcd, 0); if (ret) return ret; pdata = mhcd->dev->platform_data; if (pdata && pdata->use_sec_phy) /* select secondary phy if offset is set for USB operation */ writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16), USB_PHY_CTRL2); val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK; writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC); dev_info(mhcd->dev, "phy_reset: success\n"); return 0; }
void usb_lpm_exit_w(struct work_struct *work) { struct msmusb_hcd *mhcd = container_of((void *) work, struct msmusb_hcd, lpm_exit_work); struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct device *dev = container_of((void *)hcd, struct device, driver_data); msm_xusb_enable_clks(mhcd); if (usb_wakeup_phy(hcd)) { pr_err("fatal error: cannot bring phy out of lpm\n"); return; } /* If resume signalling finishes before lpm exit, PCD is not set in * USBSTS register. Drive resume signal to the downstream device now * so that EHCI can process the upcoming port change interrupt.*/ writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC); if (mhcd->xceiv && mhcd->xceiv->set_suspend) mhcd->xceiv->set_suspend(0); if (device_may_wakeup(dev)) disable_irq_wake(hcd->irq); enable_irq(hcd->irq); }
static int msm_xusb_init_phy(struct msmusb_hcd *mhcd) { int ret = -ENODEV; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; switch (PHY_TYPE(pdata->phy_info)) { case USB_PHY_INTEGRATED: ret = 0; case USB_PHY_SERIAL_PMIC: msm_xusb_enable_clks(mhcd); writel(0, USB_USBINTR); #if 0 ret = msm_fsusb_rpc_init(&mhcd->otg_ops); if (!ret) msm_fsusb_init_phy(); #endif msm_xusb_disable_clks(mhcd); break; default: pr_err("%s: undefined phy type ( %X ) \n", __func__, pdata->phy_info); } return ret; }
static void msm_xusb_uninit_host(struct msmusb_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; switch (PHY_TYPE(pdata->phy_info)) { case USB_PHY_INTEGRATED: if (pdata->vbus_init) pdata->vbus_init(0); hcd_to_ehci(hcd)->transceiver = NULL; otg_set_host(mhcd->xceiv->otg, NULL); usb_put_transceiver(mhcd->xceiv); cancel_work_sync(&mhcd->otg_work); break; case USB_PHY_SERIAL_PMIC: iounmap(hcd->regs); clk_put(mhcd->alt_core_clk); clk_put(mhcd->iface_clk); msm_fsusb_reset_phy(); msm_fsusb_rpc_deinit(); break; default: pr_err("phy type is bad\n"); } }
void usb_lpm_exit_w(struct work_struct *work) { struct msmusb_hcd *mhcd = container_of((void *) work, struct msmusb_hcd, lpm_exit_work); struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct device *dev = container_of((void *)hcd, struct device, platform_data); msm_xusb_enable_clks(mhcd); if (usb_wakeup_phy(hcd)) { pr_err("fatal error: cannot bring phy out of lpm\n"); return; } writel(readl(USB_PORTSC) | PORTSC_FPR, USB_PORTSC); if (mhcd->xceiv && mhcd->xceiv->set_suspend) mhcd->xceiv->set_suspend(mhcd->xceiv, 0); if (device_may_wakeup(dev)) disable_irq_wake(hcd->irq); enable_irq(hcd->irq); }
static int msm_xusb_init_phy(struct msmusb_hcd *mhcd) { int ret = -ENODEV; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; unsigned temp; switch (PHY_TYPE(pdata->phy_info)) { case USB_PHY_INTEGRATED: msm_hsusb_rpc_connect(); /* VBUS might be present. Turn off vbus */ if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); msm_xusb_enable_clks(mhcd); clk_enable(mhcd->clk); if (pdata->phy_reset) ret = pdata->phy_reset(hcd->regs); else ret = msm_hsusb_phy_reset(); if (ret) break; /* Give some delay to settle phy after reset */ msleep(100); /* Disable VbusValid and SessionEnd comparators */ ulpi_write(hcd, ULPI_VBUS_VALID | ULPI_SESS_END, ULPI_INT_RISE_CLR); ulpi_write(hcd, ULPI_VBUS_VALID | ULPI_SESS_END, ULPI_INT_FALL_CLR); /* set hs driver amplitude to max * to avoid eye diagram failures */ temp = ulpi_read(hcd, ULPI_CONFIG_REG); temp |= ULPI_AMPLITUDE_MAX; ulpi_write(hcd, temp, ULPI_CONFIG_REG); /* Disable all interrupts */ writel(0, USB_USBINTR); writel(readl(USB_OTGSC) & ~OTGSC_INTR_MASK, USB_OTGSC); msm_xusb_disable_clks(mhcd); clk_disable(mhcd->clk); break; case USB_PHY_SERIAL_PMIC: msm_xusb_enable_clks(mhcd); writel(0, USB_USBINTR); ret = msm_fsusb_rpc_init(&mhcd->otg_ops); if (!ret) msm_fsusb_init_phy(); msm_xusb_disable_clks(mhcd); break; default: pr_err("%s: undefined phy type ( %X ) \n", __func__, pdata->phy_info); } return ret; }
static int msm_hsusb_reset(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata; unsigned long timeout; int ret; if (mhcd->alt_core_clk) clk_prepare_enable(mhcd->alt_core_clk); ret = msm_ehci_phy_reset(mhcd); if (ret) { dev_err(mhcd->dev, "phy_reset failed\n"); return ret; } writel_relaxed(USBCMD_RESET, USB_USBCMD); timeout = jiffies + usecs_to_jiffies(LINK_RESET_TIMEOUT_USEC); while (readl_relaxed(USB_USBCMD) & USBCMD_RESET) { if (time_after(jiffies, timeout)) return -ETIMEDOUT; udelay(1); } /* select ULPI phy */ writel_relaxed(0x80000000, USB_PORTSC); pdata = mhcd->dev->platform_data; if (pdata && pdata->use_sec_phy) writel_relaxed(readl_relaxed(USB_PHY_CTRL2) | (1<<16), USB_PHY_CTRL2); /* Reset USB PHY after performing USB Link RESET */ msm_usb_phy_reset(mhcd); msleep(100); writel_relaxed(0x0, USB_AHBBURST); writel_relaxed(0x08, USB_AHBMODE); /* Ensure that RESET operation is completed before turning off clock */ mb(); if (mhcd->alt_core_clk) clk_disable_unprepare(mhcd->alt_core_clk); /*rising edge interrupts with Dp rise and fall enabled*/ msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_RISE); msm_ulpi_write(mhcd, ULPI_INT_DP, ULPI_USB_INT_EN_FALL); /*Clear the PHY interrupts by reading the PHY interrupt latch register*/ msm_ulpi_read(mhcd, ULPI_USB_INT_LATCH); return 0; }
static void msm_hsusb_request_host(void *handle, int request) { struct msmusb_hcd *mhcd = handle; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, otg); switch (request) { case REQUEST_RESUME: usb_hcd_resume_root_hub(hcd); break; case REQUEST_START: if (mhcd->running) break; wake_lock(&mhcd->wlock); msm_xusb_pm_qos_update(mhcd, 1); msm_xusb_enable_clks(mhcd); if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) if (otg->set_clk) otg->set_clk(mhcd->xceiv, 1); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 1); if (pdata->config_gpio) pdata->config_gpio(1); usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); mhcd->running = 1; if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) if (otg->set_clk) otg->set_clk(mhcd->xceiv, 0); break; case REQUEST_STOP: if (!mhcd->running) break; mhcd->running = 0; if (PHY_TYPE(pdata->phy_info) == USB_PHY_SERIAL_PMIC) { usb_lpm_exit(hcd); if (cancel_work_sync(&(mhcd->lpm_exit_work))) usb_lpm_exit_w(&mhcd->lpm_exit_work); } usb_remove_hcd(hcd); if (pdata->config_gpio) pdata->config_gpio(0); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); msm_xusb_disable_clks(mhcd); wake_lock_timeout(&mhcd->wlock, HZ/2); msm_xusb_pm_qos_update(mhcd, 0); if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) { otg->reset(mhcd->xceiv); otg_set_suspend(mhcd->xceiv, 1); } break; } }
static void msm_ehci_phy_susp_fail_work(struct work_struct *w) { struct msm_hcd *mhcd = container_of(w, struct msm_hcd, phy_susp_fail_work); struct usb_hcd *hcd = mhcd_to_hcd(mhcd); msm_ehci_vbus_power(mhcd, 0); usb_remove_hcd(hcd); msm_hsusb_reset(mhcd); usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); msm_ehci_vbus_power(mhcd, 1); }
static void msm_xusb_pm_qos_update(struct msmusb_hcd *mhcd, int vote) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); if (vote) { if (mhcd->pdata->max_axi_khz) pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, (char *)hcd->self.bus_name, mhcd->pdata->max_axi_khz); } else { if (mhcd->pdata->max_axi_khz) pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, (char *) hcd->self.bus_name, PM_QOS_DEFAULT_VALUE); } }
static void msm_hsusb_request_host(void *handle, int request) { struct msmusb_hcd *mhcd = handle; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; switch (request) { case REQUEST_RESUME: usb_hcd_resume_root_hub(hcd); break; case REQUEST_START: if (mhcd->running) break; wake_lock(&mhcd->wlock); msm_xusb_pm_qos_update(mhcd, 1); msm_xusb_enable_clks(mhcd); if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) clk_enable(mhcd->clk); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 1); if (pdata->config_gpio) pdata->config_gpio(1); usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); mhcd->running = 1; if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) clk_disable(mhcd->clk); break; case REQUEST_STOP: if (!mhcd->running) break; mhcd->running = 0; /* come out of lpm before deregistration */ usb_lpm_exit(hcd); if (cancel_work_sync(&(mhcd->lpm_exit_work))) usb_lpm_exit_w(&mhcd->lpm_exit_work); usb_remove_hcd(hcd); if (pdata->config_gpio) pdata->config_gpio(0); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); msm_xusb_disable_clks(mhcd); wake_lock_timeout(&mhcd->wlock, HZ/2); msm_xusb_pm_qos_update(mhcd, 0); break; } }
static int msm_ehci_phy_reset(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); u32 val; int ret; int retries; ret = msm_ehci_link_clk_reset(mhcd, 1); if (ret) return ret; udelay(1); ret = msm_ehci_link_clk_reset(mhcd, 0); if (ret) return ret; val = readl_relaxed(USB_PORTSC) & ~PORTSC_PTS_MASK; writel_relaxed(val | PORTSC_PTS_ULPI, USB_PORTSC); for (retries = 3; retries > 0; retries--) { ret = msm_ulpi_write(mhcd, ULPI_FUNC_CTRL_SUSPENDM, ULPI_CLR(ULPI_FUNC_CTRL)); if (!ret) break; } if (!retries) return -ETIMEDOUT; /* Wakeup the PHY with a reg-access for calibration */ for (retries = 3; retries > 0; retries--) { ret = msm_ulpi_read(mhcd, ULPI_DEBUG); if (ret != -ETIMEDOUT) break; } if (!retries) return -ETIMEDOUT; dev_info(mhcd->dev, "phy_reset: success\n"); return 0; }
static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init) { int rc = 0; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); const struct msm_usb_host_platform_data *pdata; int ret = 0; pdata = mhcd->dev->platform_data; if (!init) { if (pdata && pdata->dock_connect_irq) free_irq(pdata->dock_connect_irq, mhcd); return rc; } if(!wan_present()){ mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus"); ret = PTR_ERR(mhcd->vbus); if (ret == -EPROBE_DEFER) { pr_debug("failed to get vbus handle, defer probe\n"); return ret; } else if (IS_ERR(mhcd->vbus)) { pr_err("Unable to get vbus\n"); return -ENODEV; } } if (pdata) { hcd->power_budget = pdata->power_budget; if (pdata->dock_connect_irq) { rc = request_threaded_irq(pdata->dock_connect_irq, NULL, msm_ehci_dock_connect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "msm_ehci_host", mhcd); if (!rc) enable_irq_wake(pdata->dock_connect_irq); } } return rc; }
static void msm_xusb_pm_qos_update(struct msmusb_hcd *mhcd, int vote) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); if (PHY_TYPE(mhcd->pdata->phy_info) == USB_PHY_SERIAL_PMIC) goto vote_for_axi; if (!depends_on_axi_freq(mhcd->xceiv)) return; vote_for_axi: if (vote) { pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, (char *)hcd->self.bus_name, MSM_AXI_MAX_FREQ); } else { pm_qos_update_requirement(PM_QOS_SYSTEM_BUS_FREQ, (char *) hcd->self.bus_name, PM_QOS_DEFAULT_VALUE); } }
static irqreturn_t msm_ehci_dock_connect_irq(int irq, void *data) { const struct msm_usb_host_platform_data *pdata; struct msm_hcd *mhcd = data; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); pdata = mhcd->dev->platform_data; if (atomic_read(&mhcd->in_lpm)) usb_hcd_resume_root_hub(hcd); if (irq_read_line(pdata->dock_connect_irq)) { dev_dbg(mhcd->dev, "%s:Dock removed disable vbus\n", __func__); msm_ehci_vbus_power(mhcd, 0); } else { dev_dbg(mhcd->dev, "%s:Dock connected enable vbus\n", __func__); msm_ehci_vbus_power(mhcd, 1); } return IRQ_HANDLED; }
static int msm_ulpi_read(struct msm_hcd *mhcd, u32 reg) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; /* initiate read operation */ writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg), USB_ULPI_VIEWPORT); /* wait for completion */ timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS); while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) { if (time_after(jiffies, timeout)) { dev_err(mhcd->dev, "msm_ulpi_read: timeout %08x\n", readl_relaxed(USB_ULPI_VIEWPORT)); return -ETIMEDOUT; } udelay(1); } return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT)); }
static int msm_ehci_init_vbus(struct msm_hcd *mhcd, int init) { int rc = 0; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); const struct msm_usb_host_platform_data *pdata; pdata = mhcd->dev->platform_data; if (!init) { if (pdata && pdata->dock_connect_irq) free_irq(pdata->dock_connect_irq, mhcd); return rc; } mhcd->vbus = devm_regulator_get(mhcd->dev, "vbus"); if (PTR_ERR(mhcd->vbus) == -EPROBE_DEFER) { dev_dbg(mhcd->dev, "failed to get vbus handle, defer probe\n"); return -EPROBE_DEFER; } else { dev_dbg(mhcd->dev, "vbus-supply not specified\n"); mhcd->vbus = NULL; } if (pdata) { hcd->power_budget = pdata->power_budget; if (pdata->dock_connect_irq) { rc = request_threaded_irq(pdata->dock_connect_irq, NULL, msm_ehci_dock_connect_irq, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, "msm_ehci_host", mhcd); if (!rc) enable_irq_wake(pdata->dock_connect_irq); } } return rc; }
static int msm_xusb_init_host(struct platform_device *pdev, struct msmusb_hcd *mhcd) { int ret = 0; struct msm_otg *otg; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; switch (PHY_TYPE(pdata->phy_info)) { case USB_PHY_INTEGRATED: msm_hsusb_rpc_connect(); if (pdata->vbus_init) pdata->vbus_init(1); /* VBUS might be present. Turn off vbus */ if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); INIT_WORK(&mhcd->otg_work, msm_hsusb_otg_work); mhcd->xceiv = usb_get_transceiver(); if (!mhcd->xceiv) return -ENODEV; otg = container_of(mhcd->xceiv, struct msm_otg, phy); hcd->regs = otg->regs; otg->start_host = msm_hsusb_start_host; ret = otg_set_host(mhcd->xceiv->otg, &hcd->self); ehci->transceiver = mhcd->xceiv; break; case USB_PHY_SERIAL_PMIC: hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) return -EFAULT; /* get usb clocks */ mhcd->alt_core_clk = clk_get(&pdev->dev, "alt_core_clk"); if (IS_ERR(mhcd->alt_core_clk)) { iounmap(hcd->regs); return PTR_ERR(mhcd->alt_core_clk); } mhcd->iface_clk = clk_get(&pdev->dev, "iface_clk"); if (IS_ERR(mhcd->iface_clk)) { iounmap(hcd->regs); clk_put(mhcd->alt_core_clk); return PTR_ERR(mhcd->iface_clk); } mhcd->otg_ops.request = msm_hsusb_request_host; mhcd->otg_ops.handle = (void *) mhcd; ret = msm_xusb_init_phy(mhcd); if (ret < 0) { iounmap(hcd->regs); clk_put(mhcd->alt_core_clk); clk_put(mhcd->iface_clk); } break; default: pr_err("phy type is bad\n"); } return ret; }
static int msm_ehci_resume(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; unsigned temp; int ret; unsigned long flags; if (!atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__); return 0; } spin_lock_irqsave(&mhcd->wakeup_lock, flags); if (mhcd->wakeup_irq_enabled) { disable_irq_wake(mhcd->wakeup_irq); disable_irq_nosync(mhcd->wakeup_irq); mhcd->wakeup_irq_enabled = 0; } spin_unlock_irqrestore(&mhcd->wakeup_lock, flags); /* Handles race with Async interrupt */ disable_irq(hcd->irq); if (mhcd->pmic_gpio_dp_irq_enabled) { disable_irq_wake(mhcd->pmic_gpio_dp_irq); disable_irq_nosync(mhcd->pmic_gpio_dp_irq); mhcd->pmic_gpio_dp_irq_enabled = 0; } spin_lock_irqsave(&mhcd->wakeup_lock, flags); if (mhcd->async_irq_enabled) { disable_irq_wake(mhcd->async_irq); disable_irq_nosync(mhcd->async_irq); mhcd->async_irq_enabled = 0; } spin_unlock_irqrestore(&mhcd->wakeup_lock, flags); wake_lock(&mhcd->wlock); /* Vote for TCXO when waking up the phy */ if (!IS_ERR(mhcd->xo_clk)) { clk_prepare_enable(mhcd->xo_clk); } else { ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON); if (ret) dev_err(mhcd->dev, "%s failed to vote for TCXO D0 %d\n", __func__, ret); } clk_prepare_enable(mhcd->core_clk); clk_prepare_enable(mhcd->iface_clk); msm_ehci_config_vddcx(mhcd, 1); if (mhcd->flags & ALLOW_EHCI_RETENTION) { u32 phy_ctrl_val; phy_ctrl_val = readl_relaxed(USB_PHY_CTRL); phy_ctrl_val |= PHY_RETEN; writel_relaxed(phy_ctrl_val, USB_PHY_CTRL); } temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC) & ~(PORTSC_PHCD | PORT_RWC_BITS); writel_relaxed(temp, USB_PORTSC); timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC); while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) || !(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) { if (time_after(jiffies, timeout)) { /*This is a fatal error. Reset the link and PHY*/ dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n"); msm_hsusb_reset(mhcd); break; } udelay(1); } skip_phy_resume: usb_hcd_resume_root_hub(hcd); atomic_set(&mhcd->in_lpm, 0); if (mhcd->async_int) { mhcd->async_int = false; pm_runtime_put_noidle(mhcd->dev); enable_irq(hcd->irq); } if (atomic_read(&mhcd->pm_usage_cnt)) { atomic_set(&mhcd->pm_usage_cnt, 0); pm_runtime_put_noidle(mhcd->dev); } enable_irq(hcd->irq); dev_info(mhcd->dev, "EHCI USB exited from low power mode\n"); return 0; }
static int msm_ehci_suspend(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; int ret; u32 portsc; if (atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in lpm\n", __func__); return 0; } disable_irq(hcd->irq); /* make sure we don't race against a remote wakeup */ if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) || readl_relaxed(USB_PORTSC) & PORT_RESUME) { dev_dbg(mhcd->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } /* If port is enabled wait 5ms for PHCD to come up. Reset PHY * and link if it fails to do so. * If port is not enabled set the PHCD bit and poll for it to * come up with in 500ms. Reset phy and link if it fails to do so. */ portsc = readl_relaxed(USB_PORTSC); if (portsc & PORT_PE) { usleep_range(5000, 5000); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) { dev_err(mhcd->dev, "Unable to suspend PHY. portsc: %8x\n", readl_relaxed(USB_PORTSC)); goto reset_phy_and_link; } } else { writel_relaxed(portsc | PORTSC_PHCD, USB_PORTSC); timeout = jiffies + msecs_to_jiffies(PHY_SUSP_TIMEOUT_MSEC); while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) { if (time_after(jiffies, timeout)) { dev_err(mhcd->dev, "Unable to suspend PHY. portsc: %8x\n", readl_relaxed(USB_PORTSC)); goto reset_phy_and_link; } usleep_range(10000, 10000); } } /* * 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. Enable asynchronous interrupt * only when wakeup gpio IRQ is not present. */ //if (mhcd->wakeup_irq) // writel_relaxed(readl_relaxed(USB_USBCMD) | ULPI_STP_CTRL, // USB_USBCMD); //else writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); /* Enable retention mode */ if (mhcd->flags & ALLOW_EHCI_RETENTION) { u32 phy_ctrl_val; phy_ctrl_val = readl_relaxed(USB_PHY_CTRL); phy_ctrl_val &= ~PHY_RETEN; writel_relaxed(phy_ctrl_val, USB_PHY_CTRL); } /* * Ensure that hardware is put in low power mode before * clocks are turned OFF and VDD is allowed to minimize. */ mb(); clk_disable_unprepare(mhcd->iface_clk); clk_disable_unprepare(mhcd->core_clk); /* usb phy does not require TCXO clock, hence vote for TCXO disable */ if (!IS_ERR(mhcd->xo_clk)) { clk_disable_unprepare(mhcd->xo_clk); } else { ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF); if (ret) dev_err(mhcd->dev, "%s failed to devote for TCXO %d\n", __func__, ret); } msm_ehci_config_vddcx(mhcd, 0); atomic_set(&mhcd->in_lpm, 1); enable_irq(hcd->irq); if (mhcd->wakeup_irq) { mhcd->wakeup_irq_enabled = 1; enable_irq_wake(mhcd->wakeup_irq); enable_irq(mhcd->wakeup_irq); } if (mhcd->pmic_gpio_dp_irq) { mhcd->pmic_gpio_dp_irq_enabled = 1; enable_irq_wake(mhcd->pmic_gpio_dp_irq); enable_irq(mhcd->pmic_gpio_dp_irq); } if (mhcd->async_irq) { mhcd->async_irq_enabled = 1; enable_irq_wake(mhcd->async_irq); enable_irq(mhcd->async_irq); } wake_unlock(&mhcd->wlock); dev_info(mhcd->dev, "EHCI USB in low power mode\n"); return 0; reset_phy_and_link: schedule_work(&mhcd->phy_susp_fail_work); return -ETIMEDOUT; }
static int msm_ehci_resume(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; unsigned temp; int ret; if (!atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__); return 0; } wake_lock(&mhcd->wlock); /* Vote for TCXO when waking up the phy */ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON); if (ret) dev_err(mhcd->dev, "%s failed to vote for " "TCXO D0 buffer%d\n", __func__, ret); clk_prepare_enable(mhcd->core_clk); clk_prepare_enable(mhcd->iface_clk); msm_ehci_config_vddcx(mhcd, 1); temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD; writel_relaxed(temp, USB_PORTSC); timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC); while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) || !(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) { if (time_after(jiffies, timeout)) { /*This is a fatal error. Reset the link and PHY*/ dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n"); msm_hsusb_reset(mhcd); break; } udelay(1); } skip_phy_resume: atomic_set(&mhcd->in_lpm, 0); if (mhcd->async_int) { mhcd->async_int = false; pm_runtime_put_noidle(mhcd->dev); enable_irq(hcd->irq); } dev_info(mhcd->dev, "EHCI USB exited from low power mode\n"); return 0; }
static int msm_ehci_resume(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; unsigned temp; int ret; if (!atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__); return 0; } //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" mutex_lock(&mhcd->ehci_mutex); //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" if (mhcd->pmic_gpio_dp_irq_enabled) { disable_irq_wake(mhcd->pmic_gpio_dp_irq); disable_irq_nosync(mhcd->pmic_gpio_dp_irq); mhcd->pmic_gpio_dp_irq_enabled = 0; } wake_lock(&mhcd->wlock); /* Vote for TCXO when waking up the phy */ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_ON); if (ret) dev_err(mhcd->dev, "%s failed to vote for " "TCXO D0 buffer%d\n", __func__, ret); clk_prepare_enable(mhcd->core_clk); clk_prepare_enable(mhcd->iface_clk); msm_ehci_config_vddcx(mhcd, 1); //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" if (mhcd->lpm_flags & PHY_PWR_COLLAPSED) { msm_ehci_ldo_enable(mhcd, 1); mhcd->lpm_flags &= ~PHY_PWR_COLLAPSED; } //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD; writel_relaxed(temp, USB_PORTSC); timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC); while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) || !(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) { if (time_after(jiffies, timeout)) { /*This is a fatal error. Reset the link and PHY*/ dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n"); msm_hsusb_reset(mhcd); break; } udelay(1); } skip_phy_resume: usb_hcd_resume_root_hub(hcd); atomic_set(&mhcd->in_lpm, 0); if (mhcd->async_int) { mhcd->async_int = false; pm_runtime_put_noidle(mhcd->dev); enable_irq(hcd->irq); } if (atomic_read(&mhcd->pm_usage_cnt)) { atomic_set(&mhcd->pm_usage_cnt, 0); pm_runtime_put_noidle(mhcd->dev); } //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" mutex_unlock(&mhcd->ehci_mutex); //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" dev_info(mhcd->dev, "EHCI USB exited from low power mode\n"); return 0; }
static int msm_ehci_suspend(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; int ret; u32 portsc; if (atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in lpm\n", __func__); return 0; } //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" mutex_lock(&mhcd->ehci_mutex); //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" disable_irq(hcd->irq); /* Set the PHCD bit, only if it is not set by the controller. * 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. */ portsc = readl_relaxed(USB_PORTSC); if (!(portsc & PORTSC_PHCD)) { writel_relaxed(portsc | PORTSC_PHCD, USB_PORTSC); timeout = jiffies + usecs_to_jiffies(PHY_SUSPEND_TIMEOUT_USEC); while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) { if (time_after(jiffies, timeout)) { dev_err(mhcd->dev, "Unable to suspend PHY\n"); schedule_work(&mhcd->phy_susp_fail_work); return -ETIMEDOUT; } udelay(1); } } /* * 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_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); /* * Ensure that hardware is put in low power mode before * clocks are turned OFF and VDD is allowed to minimize. */ mb(); clk_disable_unprepare(mhcd->iface_clk); clk_disable_unprepare(mhcd->core_clk); /* usb phy does not require TCXO clock, hence vote for TCXO disable */ ret = msm_xo_mode_vote(mhcd->xo_handle, MSM_XO_MODE_OFF); if (ret) dev_err(mhcd->dev, "%s failed to devote for " "TCXO D0 buffer%d\n", __func__, ret); //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" if (!test_bit(PHY_POWER, &mhcd->phy_power)) { msm_ehci_ldo_enable(mhcd, 0); mhcd->lpm_flags |= PHY_PWR_COLLAPSED; } //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" msm_ehci_config_vddcx(mhcd, 0); atomic_set(&mhcd->in_lpm, 1); enable_irq(hcd->irq); if (mhcd->pmic_gpio_dp_irq) { mhcd->pmic_gpio_dp_irq_enabled = 1; enable_irq_wake(mhcd->pmic_gpio_dp_irq); enable_irq(mhcd->pmic_gpio_dp_irq); } wake_unlock(&mhcd->wlock); //ASUS_BSP+++ BennyCheng "implement ehci3 phy power collapse mode" mutex_unlock(&mhcd->ehci_mutex); //ASUS_BSP--- BennyCheng "implement ehci3 phy power collapse mode" dev_info(mhcd->dev, "EHCI USB in low power mode\n"); return 0; }
static int msm_ehci_resume(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; unsigned temp; unsigned long flags; u32 func_ctrl; const struct msm_usb_host_platform_data *pdata; if (!atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in !in_lpm\n", __func__); return 0; } /* Handles race with Async interrupt */ disable_irq(hcd->irq); if (mhcd->pmic_gpio_dp_irq_enabled) { disable_irq_wake(mhcd->pmic_gpio_dp_irq); disable_irq_nosync(mhcd->pmic_gpio_dp_irq); mhcd->pmic_gpio_dp_irq_enabled = 0; } spin_lock_irqsave(&mhcd->wakeup_lock, flags); if (mhcd->async_irq_enabled) { disable_irq_wake(mhcd->async_irq); disable_irq_nosync(mhcd->async_irq); mhcd->async_irq_enabled = 0; } if (mhcd->wakeup_irq) { if (mhcd->wakeup_irq_enabled) { disable_irq_wake(mhcd->wakeup_irq); disable_irq_nosync(mhcd->wakeup_irq); mhcd->wakeup_irq_enabled = 0; } } spin_unlock_irqrestore(&mhcd->wakeup_lock, flags); pm_stay_awake(mhcd->dev); /* Vote for TCXO when waking up the phy */ if (mhcd->xo_clk) clk_prepare_enable(mhcd->xo_clk); clk_prepare_enable(mhcd->core_clk); clk_prepare_enable(mhcd->iface_clk); msm_ehci_config_vddcx(mhcd, 1); temp = readl_relaxed(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; writel_relaxed(temp, USB_USBCMD); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) goto skip_phy_resume; temp = readl_relaxed(USB_PORTSC) & ~PORTSC_PHCD; writel_relaxed(temp, USB_PORTSC); timeout = jiffies + usecs_to_jiffies(PHY_RESUME_TIMEOUT_USEC); while ((readl_relaxed(USB_PORTSC) & PORTSC_PHCD) || !(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) { if (time_after(jiffies, timeout)) { /*This is a fatal error. Reset the link and PHY*/ dev_err(mhcd->dev, "Unable to resume USB. Resetting the h/w\n"); msm_hsusb_reset(mhcd); break; } udelay(1); } skip_phy_resume: pdata = mhcd->dev->platform_data; if (pdata && pdata->is_uicc) { /* put the controller in normal mode */ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL); func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL); } set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); usb_hcd_resume_root_hub(hcd); atomic_set(&mhcd->in_lpm, 0); if (atomic_read(&mhcd->pm_usage_cnt)) { atomic_set(&mhcd->pm_usage_cnt, 0); pm_runtime_put_noidle(mhcd->dev); } enable_irq(hcd->irq); dev_info(mhcd->dev, "EHCI USB exited from low power mode\n"); return 0; }
static void msm_hsusb_request_host(void *handle, int request) { struct msmusb_hcd *mhcd = handle; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; struct msm_otg *otg = container_of(mhcd->xceiv, struct msm_otg, phy); #ifdef CONFIG_USB_OTG struct usb_device *udev = hcd->self.root_hub; #endif struct device *dev = hcd->self.controller; switch (request) { #ifdef CONFIG_USB_OTG case REQUEST_HNP_SUSPEND: /* disable Root hub auto suspend. As hardware is configured * for peripheral mode, mark hardware is not available. */ if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) { pm_runtime_disable(&udev->dev); /* Mark root hub as disconnected. This would * protect suspend/resume via sysfs. */ udev->state = USB_STATE_NOTATTACHED; clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); hcd->state = HC_STATE_HALT; pm_runtime_put_noidle(dev); pm_runtime_suspend(dev); } break; case REQUEST_HNP_RESUME: if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) { pm_runtime_get_noresume(dev); pm_runtime_resume(dev); disable_irq(hcd->irq); ehci_msm_reset(hcd); ehci_msm_run(hcd); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); pm_runtime_enable(&udev->dev); udev->state = USB_STATE_CONFIGURED; enable_irq(hcd->irq); } break; #endif case REQUEST_RESUME: usb_hcd_resume_root_hub(hcd); break; case REQUEST_START: if (mhcd->running) break; pm_runtime_get_noresume(dev); pm_runtime_resume(dev); wake_lock(&mhcd->wlock); msm_xusb_pm_qos_update(mhcd, 1); msm_xusb_enable_clks(mhcd); if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) if (otg->set_clk) otg->set_clk(mhcd->xceiv, 1); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 1); if (pdata->config_gpio) pdata->config_gpio(1); usb_add_hcd(hcd, hcd->irq, IRQF_SHARED); mhcd->running = 1; if (PHY_TYPE(pdata->phy_info) == USB_PHY_INTEGRATED) if (otg->set_clk) otg->set_clk(mhcd->xceiv, 0); break; case REQUEST_STOP: if (!mhcd->running) break; mhcd->running = 0; /* come out of lpm before deregistration */ if (PHY_TYPE(pdata->phy_info) == USB_PHY_SERIAL_PMIC) { usb_lpm_exit(hcd); if (cancel_work_sync(&(mhcd->lpm_exit_work))) usb_lpm_exit_w(&mhcd->lpm_exit_work); } usb_remove_hcd(hcd); if (pdata->config_gpio) pdata->config_gpio(0); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); msm_xusb_disable_clks(mhcd); wake_lock_timeout(&mhcd->wlock, HZ/2); msm_xusb_pm_qos_update(mhcd, 0); pm_runtime_put_noidle(dev); pm_runtime_suspend(dev); break; } }
static int msm_ehci_suspend(struct msm_hcd *mhcd) { struct usb_hcd *hcd = mhcd_to_hcd(mhcd); unsigned long timeout; u32 portsc; const struct msm_usb_host_platform_data *pdata; u32 func_ctrl; if (atomic_read(&mhcd->in_lpm)) { dev_dbg(mhcd->dev, "%s called in lpm\n", __func__); return 0; } disable_irq(hcd->irq); /* make sure we don't race against a remote wakeup */ if (test_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags) || readl_relaxed(USB_PORTSC) & PORT_RESUME) { dev_dbg(mhcd->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } pdata = mhcd->dev->platform_data; if (pdata && pdata->is_uicc) { /* put the controller in non-driving mode */ func_ctrl = msm_ulpi_read(mhcd, ULPI_FUNC_CTRL); func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; msm_ulpi_write(mhcd, func_ctrl, ULPI_FUNC_CTRL); } /* If port is enabled wait 5ms for PHCD to come up. Reset PHY * and link if it fails to do so. * If port is not enabled set the PHCD bit and poll for it to * come up with in 500ms. Reset phy and link if it fails to do so. */ portsc = readl_relaxed(USB_PORTSC); if (portsc & PORT_PE) { usleep_range(5000, 5000); if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) { dev_err(mhcd->dev, "Unable to suspend PHY. portsc: %8x\n", readl_relaxed(USB_PORTSC)); goto reset_phy_and_link; } } else { writel_relaxed(portsc | PORTSC_PHCD, USB_PORTSC); timeout = jiffies + msecs_to_jiffies(PHY_SUSP_TIMEOUT_MSEC); while (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD)) { if (time_after(jiffies, timeout)) { dev_err(mhcd->dev, "Unable to suspend PHY. portsc: %8x\n", readl_relaxed(USB_PORTSC)); goto reset_phy_and_link; } usleep_range(10000, 10000); } } /* * 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. Enable asynchronous interrupt * only when wakeup gpio IRQ is not present. */ if (mhcd->wakeup_irq) writel_relaxed(readl_relaxed(USB_USBCMD) | ULPI_STP_CTRL, USB_USBCMD); else writel_relaxed(readl_relaxed(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); /* * Ensure that hardware is put in low power mode before * clocks are turned OFF and VDD is allowed to minimize. */ mb(); clk_disable_unprepare(mhcd->iface_clk); clk_disable_unprepare(mhcd->core_clk); /* usb phy does not require TCXO clock, hence vote for TCXO disable */ if (mhcd->xo_clk) clk_disable_unprepare(mhcd->xo_clk); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); msm_ehci_config_vddcx(mhcd, 0); atomic_set(&mhcd->in_lpm, 1); enable_irq(hcd->irq); if (mhcd->wakeup_irq) { mhcd->wakeup_irq_enabled = 1; enable_irq_wake(mhcd->wakeup_irq); enable_irq(mhcd->wakeup_irq); } if (mhcd->pmic_gpio_dp_irq) { mhcd->pmic_gpio_dp_irq_enabled = 1; enable_irq_wake(mhcd->pmic_gpio_dp_irq); enable_irq(mhcd->pmic_gpio_dp_irq); } if (mhcd->async_irq) { mhcd->async_irq_enabled = 1; enable_irq_wake(mhcd->async_irq); enable_irq(mhcd->async_irq); } pm_relax(mhcd->dev); dev_info(mhcd->dev, "EHCI USB in low power mode\n"); return 0; reset_phy_and_link: schedule_work(&mhcd->phy_susp_fail_work); return -ETIMEDOUT; }
static int msm_xusb_init_host(struct msmusb_hcd *mhcd) { int ret = 0; struct msm_otg *otg; struct usb_hcd *hcd = mhcd_to_hcd(mhcd); struct msm_usb_host_platform_data *pdata = mhcd->pdata; struct device *dev = container_of((void *)hcd, struct device, platform_data); switch (PHY_TYPE(pdata->phy_info)) { case USB_PHY_INTEGRATED: msm_hsusb_rpc_connect(); if (pdata->vbus_init) pdata->vbus_init(1); if (pdata->vbus_power) pdata->vbus_power(pdata->phy_info, 0); INIT_WORK(&mhcd->otg_work, msm_hsusb_otg_work); mhcd->xceiv = otg_get_transceiver(); if (!mhcd->xceiv) return -ENODEV; otg = container_of(mhcd->xceiv, struct msm_otg, otg); hcd->regs = otg->regs; otg->start_host = msm_hsusb_start_host; ret = otg_set_host(mhcd->xceiv, &hcd->self); break; case USB_PHY_SERIAL_PMIC: hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) return -EFAULT; mhcd->clk = clk_get(dev, "usb_hs2_clk"); if (IS_ERR(mhcd->clk)) { iounmap(hcd->regs); return PTR_ERR(mhcd->clk); } mhcd->pclk = clk_get(dev, "usb_hs2_pclk"); if (IS_ERR(mhcd->pclk)) { iounmap(hcd->regs); clk_put(mhcd->clk); return PTR_ERR(mhcd->pclk); } mhcd->otg_ops.request = msm_hsusb_request_host; mhcd->otg_ops.handle = (void *) mhcd; ret = msm_xusb_init_phy(mhcd); if (ret < 0) { iounmap(hcd->regs); clk_put(mhcd->clk); clk_put(mhcd->pclk); } break; default: pr_err("phy type is bad\n"); } return ret; }