static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0; /* initiate write operation */ writel_relaxed(ULPI_RUN | ULPI_WRITE | ULPI_ADDR(reg) | ULPI_DATA(val), USB_ULPI_VIEWPORT); /* wait for completion */ while (cnt < ULPI_IO_TIMEOUT_USEC) { if (!(readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN)) break; udelay(1); cnt++; } if (cnt >= ULPI_IO_TIMEOUT_USEC) { dev_err(mehci->dev, "ulpi_write: timeout ULPI_VIEWPORT: %08x\n", readl_relaxed(USB_ULPI_VIEWPORT)); dev_err(mehci->dev, "PORTSC: %08x USBCMD: %08x FRINDEX: %08x\n", readl_relaxed(USB_PORTSC), readl_relaxed(USB_USBCMD), readl_relaxed(USB_FRINDEX)); /*frame counter increments afte 125us*/ udelay(130); dev_err(mehci->dev, "ulpi_write: FRINDEX: %08x\n", readl_relaxed(USB_FRINDEX)); return -ETIMEDOUT; } return 0; }
static int mxhci_hsic_resume(struct mxhci_hsic_hcd *mxhci) { struct usb_hcd *hcd = hsic_to_hcd(mxhci); int ret; unsigned long flags; if (!mxhci->in_lpm) { dev_dbg(mxhci->dev, "%s called in !in_lpm\n", __func__); return 0; } pm_stay_awake(mxhci->dev); /* enable force-on mode for periph_on */ clk_set_flags(mxhci->system_clk, CLKFLAG_RETAIN_PERIPH); if (mxhci->bus_perf_client) { mxhci->bus_vote = true; queue_work(mxhci->wq, &mxhci->bus_vote_w); } spin_lock_irqsave(&mxhci->wakeup_lock, flags); if (mxhci->wakeup_irq_enabled) { disable_irq_wake(mxhci->wakeup_irq); disable_irq_nosync(mxhci->wakeup_irq); mxhci->wakeup_irq_enabled = 0; } if (mxhci->pm_usage_cnt) { mxhci->pm_usage_cnt = 0; pm_runtime_put_noidle(mxhci->dev); } spin_unlock_irqrestore(&mxhci->wakeup_lock, flags); ret = regulator_set_voltage(mxhci->hsic_vddcx, mxhci->vdd_low_vol_level, mxhci->vdd_high_vol_level); if (ret < 0) dev_err(mxhci->dev, "unable to set nominal vddcx voltage (no VDD MIN)\n"); clk_prepare_enable(mxhci->system_clk); clk_prepare_enable(mxhci->cal_clk); clk_prepare_enable(mxhci->hsic_clk); clk_prepare_enable(mxhci->utmi_clk); clk_prepare_enable(mxhci->core_clk); if (mxhci->wakeup_irq) usb_hcd_resume_root_hub(hcd); mxhci->in_lpm = 0; dev_dbg(mxhci->dev, "HSIC-USB exited from low power mode\n"); xhci_dbg_log_event(&dbg_hsic, NULL, "Controller resumed", 0); return 0; }
static void mxhci_hsic_reset(struct mxhci_hsic_hcd *mxhci) { u32 reg; int ret; struct usb_hcd *hcd = hsic_to_hcd(mxhci); /* start controller reset */ reg = readl_relaxed(MSM_HSIC_GCTL); reg |= GCTL_CORESOFTRESET; writel_relaxed(reg, MSM_HSIC_GCTL); usleep(1000); /* phy reset using asynchronous block reset */ clk_disable_unprepare(mxhci->cal_clk); clk_disable_unprepare(mxhci->utmi_clk); clk_disable_unprepare(mxhci->hsic_clk); clk_disable_unprepare(mxhci->core_clk); clk_disable_unprepare(mxhci->system_clk); clk_disable_unprepare(mxhci->phy_sleep_clk); ret = clk_reset(mxhci->hsic_clk, CLK_RESET_ASSERT); if (ret) { dev_err(mxhci->dev, "hsic clk assert failed:%d\n", ret); return; } usleep_range(10000, 12000); ret = clk_reset(mxhci->hsic_clk, CLK_RESET_DEASSERT); if (ret) dev_err(mxhci->dev, "hsic clk deassert failed:%d\n", ret); /* * Required delay between the deassertion and * clock enablement. */ ndelay(200); clk_prepare_enable(mxhci->phy_sleep_clk); clk_prepare_enable(mxhci->system_clk); clk_prepare_enable(mxhci->core_clk); clk_prepare_enable(mxhci->hsic_clk); clk_prepare_enable(mxhci->utmi_clk); clk_prepare_enable(mxhci->cal_clk); /* After PHY is stable we can take Core out of reset state */ reg = readl_relaxed(MSM_HSIC_GCTL); reg &= ~GCTL_CORESOFTRESET; writel_relaxed(reg, MSM_HSIC_GCTL); usleep(1000); }
static irqreturn_t mxhci_hsic_pwr_event_irq(int irq, void *data) { struct mxhci_hsic_hcd *mxhci = data; struct usb_hcd *hcd = hsic_to_hcd(mxhci); u32 stat; stat = readl_relaxed(MSM_HSIC_PWR_EVENT_IRQ_STAT); if (stat & LPM_IN_L2_IRQ_STAT) { xhci_dbg_log_event(&dbg_hsic, NULL, "LPM_IN_L2_IRQ", 0); writel_relaxed(stat, MSM_HSIC_PWR_EVENT_IRQ_STAT); complete(&mxhci->phy_in_lpm); } else { xhci_dbg_log_event(&dbg_hsic, NULL, "spurious pwr evt irq", 0); dev_info(mxhci->dev, "%s: spurious interrupt.pwr_event_irq stat = %x\n", __func__, stat); } return IRQ_HANDLED; }
static int mxhci_hsic_ulpi_write(struct mxhci_hsic_hcd *mxhci, u32 val, u32 reg) { struct usb_hcd *hcd = hsic_to_hcd(mxhci); unsigned long timeout; /* set the reg write request and perfom ULPI phy reg write */ writel_relaxed(GUSB2PHYACC_NEWREGREQ | GUSB2PHYACC_REGWR | GUSB2PHYACC_REGADDR(reg) | GUSB2PHYACC_REGDATA(val), MSM_HSIC_GUSB2PHYACC); /* poll for write done */ timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USECS); while (!(readl_relaxed(MSM_HSIC_GUSB2PHYACC) & GUSB2PHYACC_VSTSDONE)) { if (time_after(jiffies, timeout)) { dev_err(mxhci->dev, "mxhci_hsic_ulpi_write: timeout\n"); return -ETIMEDOUT; } udelay(1); } return 0; }
static int msm_hsic_resume_thread(void *data) { struct msm_hsic_hcd *mehci = data; struct usb_hcd *hcd = hsic_to_hcd(mehci); struct ehci_hcd *ehci = hcd_to_ehci(hcd); u32 temp; unsigned long resume_needed = 0; int retry_cnt = 0; int tight_resume = 0; dbg_log_event(NULL, "Resume RH", 0); /* keep delay between bus states */ if (time_before(jiffies, ehci->next_statechange)) usleep_range(5000, 5000); spin_lock_irq(&ehci->lock); if (!HCD_HW_ACCESSIBLE(hcd)) { spin_unlock_irq(&ehci->lock); mehci->resume_status = -ESHUTDOWN; complete(&mehci->rt_completion); return 0; } if (unlikely(ehci->debug)) { if (!dbgp_reset_prep()) ehci->debug = NULL; else dbgp_external_startup(); } /* at least some APM implementations will try to deliver * IRQs right away, so delay them until we're ready. */ ehci_writel(ehci, 0, &ehci->regs->intr_enable); /* re-init operational registers */ ehci_writel(ehci, 0, &ehci->regs->segment); ehci_writel(ehci, ehci->periodic_dma, &ehci->regs->frame_list); ehci_writel(ehci, (u32) ehci->async->qh_dma, &ehci->regs->async_next); /*CMD_RUN will be set after, PORT_RESUME gets cleared*/ if (ehci->resume_sof_bug) ehci->command &= ~CMD_RUN; /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); /* manually resume the ports we suspended during bus_suspend() */ resume_again: if (retry_cnt >= RESUME_RETRY_LIMIT) { pr_info("retry count(%d) reached max, resume in tight loop\n", retry_cnt); tight_resume = 1; } temp = ehci_readl(ehci, &ehci->regs->port_status[0]); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(0, &ehci->bus_suspended) && (temp & PORT_SUSPEND)) { temp |= PORT_RESUME; set_bit(0, &resume_needed); } dbg_log_event(NULL, "FPR: Set", temp); ehci_writel(ehci, temp, &ehci->regs->port_status[0]); /* HSIC controller has a h/w bug due to which it can try to send SOFs * (start of frames) during port resume resulting in phy lockup. HSIC hw * controller in MSM clears FPR bit after driving the resume signal for * 20ms. Workaround is to stop SOFs before driving resume and then start * sending SOFs immediately. Need to send SOFs within 3ms of resume * completion otherwise peripheral may enter undefined state. As * usleep_range does not gurantee exact sleep time, GPTimer is used to * to time the resume sequence. If driver exceeds allowable time SOFs, * repeat the resume process. */ if (ehci->resume_sof_bug && resume_needed) { if (!tight_resume) { mehci->resume_again = 0; ehci_writel(ehci, GPT_LD(RESUME_SIGNAL_TIME_MS), &mehci->timer->gptimer0_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer0_ctrl); ehci_writel(ehci, INTR_MASK | STS_GPTIMER0_INTERRUPT, &ehci->regs->intr_enable); ehci_writel(ehci, GPT_LD(RESUME_SIGNAL_TIME_SOF_MS), &mehci->timer->gptimer1_ld); ehci_writel(ehci, GPT_RESET | GPT_RUN, &mehci->timer->gptimer1_ctrl); spin_unlock_irq(&ehci->lock); wait_for_completion(&mehci->gpt0_completion); spin_lock_irq(&ehci->lock); } else { dbg_log_event(NULL, "FPR: Tightloop", 0); /* do the resume in a tight loop */ handshake(ehci, &ehci->regs->port_status[0], PORT_RESUME, 0, 22 * 1000); ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); } if (mehci->resume_again) { int temp; dbg_log_event(NULL, "FPR: Re-Resume", retry_cnt); pr_info("FPR: retry count: %d\n", retry_cnt); spin_unlock_irq(&ehci->lock); temp = ehci_readl(ehci, &ehci->regs->port_status[0]); temp &= ~PORT_RWC_BITS; temp |= PORT_SUSPEND; ehci_writel(ehci, temp, &ehci->regs->port_status[0]); /* Keep the bus idle for 5ms so that peripheral * can detect and initiate suspend */ usleep_range(5000, 5000); dbg_log_event(NULL, "FPR: RResume", ehci_readl(ehci, &ehci->regs->port_status[0])); spin_lock_irq(&ehci->lock); mehci->resume_again = 0; retry_cnt++; goto resume_again; } } dbg_log_event(NULL, "FPR: RT-Done", 0); mehci->resume_status = 1; spin_unlock_irq(&ehci->lock); complete(&mehci->rt_completion); return 0; }
static int msm_hsic_resume(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; unsigned temp; int min_vol, max_vol; if (!atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in !in_lpm\n", __func__); return 0; } if (mehci->wakeup_irq_enabled) { disable_irq_wake(mehci->wakeup_irq); disable_irq_nosync(mehci->wakeup_irq); mehci->wakeup_irq_enabled = 0; } wake_lock(&mehci->wlock); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = true; queue_work(ehci_wq, &mehci->bus_vote_w); } min_vol = vdd_val[mehci->vdd_type][VDD_MIN]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, min_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set nominal vddcx voltage (no VDD MIN)\n"); clk_prepare_enable(mehci->core_clk); clk_prepare_enable(mehci->phy_clk); clk_prepare_enable(mehci->cal_clk); clk_prepare_enable(mehci->ahb_clk); 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); temp &= ~(PORT_RWC_BITS | PORTSC_PHCD); writel_relaxed(temp, USB_PORTSC); while (cnt < PHY_RESUME_TIMEOUT_USEC) { if (!(readl_relaxed(USB_PORTSC) & PORTSC_PHCD) && (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_SYNC_STATE)) break; udelay(1); cnt++; } if (cnt >= PHY_RESUME_TIMEOUT_USEC) { /* * This is a fatal error. Reset the link and * PHY to make hsic working. */ dev_err(mehci->dev, "Unable to resume USB. Reset the hsic\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } skip_phy_resume: usb_hcd_resume_root_hub(hcd); atomic_set(&mehci->in_lpm, 0); if (mehci->async_int) { mehci->async_int = false; pm_runtime_put_noidle(mehci->dev); enable_irq(hcd->irq); } if (atomic_read(&mehci->pm_usage_cnt)) { atomic_set(&mehci->pm_usage_cnt, 0); pm_runtime_put_noidle(mehci->dev); } dev_dbg(mehci->dev, "HSIC-USB exited from low power mode\n"); return 0; }
static int msm_hsic_suspend(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int cnt = 0, ret; u32 val; int none_vol, max_vol; if (atomic_read(&mehci->in_lpm)) { dev_dbg(mehci->dev, "%s called in lpm\n", __func__); return 0; } if (!(readl_relaxed(USB_PORTSC) & PORT_PE)) { dev_dbg(mehci->dev, "%s:port is not enabled skip suspend\n", __func__); return -EAGAIN; } 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(mehci->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } /* * 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. */ val = readl_relaxed(USB_PORTSC); val &= ~PORT_RWC_BITS; val |= PORTSC_PHCD; writel_relaxed(val, USB_PORTSC); while (cnt < PHY_SUSPEND_TIMEOUT_USEC) { if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD) break; udelay(1); cnt++; } if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) { dev_err(mehci->dev, "Unable to suspend PHY\n"); msm_hsic_config_gpios(mehci, 0); msm_hsic_reset(mehci); } /* * 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(mehci->core_clk); clk_disable_unprepare(mehci->phy_clk); clk_disable_unprepare(mehci->cal_clk); clk_disable_unprepare(mehci->ahb_clk); none_vol = vdd_val[mehci->vdd_type][VDD_NONE]; max_vol = vdd_val[mehci->vdd_type][VDD_MAX]; ret = regulator_set_voltage(mehci->hsic_vddcx, none_vol, max_vol); if (ret < 0) dev_err(mehci->dev, "unable to set vddcx voltage for VDD MIN\n"); if (mehci->bus_perf_client && debug_bus_voting_enabled) { mehci->bus_vote = false; queue_work(ehci_wq, &mehci->bus_vote_w); } atomic_set(&mehci->in_lpm, 1); enable_irq(hcd->irq); mehci->wakeup_irq_enabled = 1; enable_irq_wake(mehci->wakeup_irq); enable_irq(mehci->wakeup_irq); wake_unlock(&mehci->wlock); dev_dbg(mehci->dev, "HSIC-USB in low power mode\n"); return 0; }
static int msm_hsic_reset(struct msm_hsic_hcd *mehci) { struct usb_hcd *hcd = hsic_to_hcd(mehci); int ret; struct msm_hsic_host_platform_data *pdata = mehci->dev->platform_data; msm_hsic_clk_reset(mehci); /* select ulpi phy */ writel_relaxed(0x80000000, USB_PORTSC); mb(); /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via GPIOs */ if (pdata && pdata->strobe && pdata->data) { /* Enable LV_MODE in HSIC_CAL_PAD_CTL register */ writel_relaxed(HSIC_LV_MODE, HSIC_CAL_PAD_CTL); mb(); /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable periodic IO calibration in HSIC_CFG register */ ulpi_write(mehci, HSIC_PAD_CALIBRATION, 0x30); /* Configure GPIO pins for HSIC functionality mode */ ret = msm_hsic_config_gpios(mehci, 1); if (ret) { dev_err(mehci->dev, " gpio configuarion failed\n"); return ret; } /* Set LV_MODE=0x1 and DCC=0x2 in HSIC_GPIO PAD_CTL register */ writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_STROBE_GPIO_PAD_CTL); writel_relaxed(HSIC_GPIO_PAD_VAL, HSIC_DATA_GPIO_PAD_CTL); mb(); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0x01, 0x31); } else { /* HSIC init sequence when HSIC signals (Strobe/Data) are routed via dedicated I/O */ /* programmable length of connect signaling (33.2ns) */ ret = ulpi_write(mehci, 3, HSIC_DBG1_REG); if (ret) { pr_err("%s: Unable to program length of connect " "signaling\n", __func__); } /*set periodic calibration interval to ~2.048sec in HSIC_IO_CAL_REG */ ulpi_write(mehci, 0xFF, 0x33); /* Enable HSIC mode in HSIC_CFG register */ ulpi_write(mehci, 0xA9, 0x30); } /*disable auto resume*/ ulpi_write(mehci, ULPI_IFC_CTRL_AUTORESUME, ULPI_CLR(ULPI_IFC_CTRL)); return 0; }
static int mxhci_hsic_suspend(struct mxhci_hsic_hcd *mxhci) { struct usb_hcd *hcd = hsic_to_hcd(mxhci); int ret; if (mxhci->in_lpm) { dev_dbg(mxhci->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(MSM_HSIC_PORTSC) & PORT_PLS_MASK) == XDEV_RESUME) { dev_dbg(mxhci->dev, "wakeup pending, aborting suspend\n"); enable_irq(hcd->irq); return -EBUSY; } /* make sure HSIC phy is in LPM */ ret = wait_for_completion_timeout( &mxhci->phy_in_lpm, msecs_to_jiffies(PHY_LPM_WAIT_TIMEOUT_MS)); if (!ret) { dev_err(mxhci->dev, "HSIC phy failed to enter lpm\n"); init_completion(&mxhci->phy_in_lpm); enable_irq(hcd->irq); return -EBUSY; } init_completion(&mxhci->phy_in_lpm); clk_disable_unprepare(mxhci->core_clk); clk_disable_unprepare(mxhci->utmi_clk); clk_disable_unprepare(mxhci->hsic_clk); clk_disable_unprepare(mxhci->cal_clk); clk_disable_unprepare(mxhci->system_clk); ret = regulator_set_voltage(mxhci->hsic_vddcx, mxhci->vdd_no_vol_level, mxhci->vdd_high_vol_level); if (ret < 0) dev_err(mxhci->dev, "unable to set vddcx voltage for VDD MIN\n"); if (mxhci->bus_perf_client) { mxhci->bus_vote = false; queue_work(mxhci->wq, &mxhci->bus_vote_w); } mxhci->in_lpm = 1; enable_irq(hcd->irq); if (mxhci->wakeup_irq) { mxhci->wakeup_irq_enabled = 1; enable_irq_wake(mxhci->wakeup_irq); enable_irq(mxhci->wakeup_irq); } /* disable force-on mode for periph_on */ clk_set_flags(mxhci->system_clk, CLKFLAG_NORETAIN_PERIPH); pm_relax(mxhci->dev); dev_dbg(mxhci->dev, "HSIC-USB in low power mode\n"); xhci_dbg_log_event(&dbg_hsic, NULL, "Controller suspended", 0); return 0; }