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;
}
Beispiel #2
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;
}