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_ehci_bus_resume(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); u32 temp; u32 power_okay; unsigned long resume_needed = 0; struct msm_hcd *mhcd = hcd_to_mhcd(hcd); struct msm_usb_host_platform_data *pdata = mhcd->dev->platform_data; if (mhcd->resume_gpio) gpio_direction_output(mhcd->resume_gpio, 1); if(pdata && !pdata->sw_fpr_ctrl) { ehci_bus_resume(hcd); } else { if (time_before (jiffies, ehci->next_statechange)) msleep(5); spin_lock_irq (&ehci->lock); if (!HCD_HW_ACCESSIBLE(hcd)) { spin_unlock_irq(&ehci->lock); return -ESHUTDOWN; } if (unlikely(ehci->debug)) { if (!dbgp_reset_prep()) ehci->debug = NULL; else dbgp_external_startup(); } /* Ideally and we've got a real resume here, and no port's power * was lost. (For PCI, that means Vaux was maintained.) But we * could instead be restoring a swsusp snapshot -- so that BIOS was * the last user of the controller, not reset/pm hardware keeping * state we gave to it. */ power_okay = ehci_readl(ehci, &ehci->regs->intr_enable); ehci_dbg(ehci, "resume root hub%s\n", power_okay ? "" : " after power loss"); /* 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){ temp = ehci_readl(ehci, &ehci->regs->port_status [0]); if (temp & PORT_PE) ehci->command &= ~CMD_RUN; } /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); ehci->rh_state = EHCI_RH_RUNNING; 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); } ehci_writel(ehci, temp, &ehci->regs->port_status [0]); if (resume_needed && ehci->resume_sof_bug) { spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); temp = ehci_readl(ehci, &ehci->regs->port_status [0]); temp &= ~(PORT_RWC_BITS | PORT_RESUME); ehci_writel(ehci, temp, &ehci->regs->port_status [0]); ehci_writel(ehci, ehci_readl(ehci, &ehci->regs->command) | CMD_RUN, &ehci->regs->command); goto skip_clear_resume; } /* msleep for 20ms only if code is trying to resume port */ if (resume_needed) { spin_unlock_irq(&ehci->lock); msleep(20); spin_lock_irq(&ehci->lock); } temp = ehci_readl(ehci, &ehci->regs->port_status [0]); if (test_bit(0, &resume_needed)) { temp &= ~(PORT_RWC_BITS | PORT_RESUME); ehci_writel(ehci, temp, &ehci->regs->port_status [0]); ehci_vdbg (ehci, "resumed port 0\n"); } skip_clear_resume: (void) ehci_readl(ehci, &ehci->regs->command); /* maybe re-activate the schedule(s) */ temp = 0; if (ehci->async->qh_next.qh) temp |= CMD_ASE; if (ehci->periodic_sched) temp |= CMD_PSE; if (temp) { ehci->command |= temp; ehci_writel(ehci, ehci->command, &ehci->regs->command); } ehci->next_statechange = jiffies + msecs_to_jiffies(5); /* Now we can safely re-enable irqs */ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); spin_unlock_irq (&ehci->lock); ehci_handover_companion_ports(ehci); } if (mhcd->resume_gpio) gpio_direction_output(mhcd->resume_gpio, 0); return 0; }