示例#1
0
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 ssize_t debug_write_phy_data(struct file *file, const char __user *buf,
				 size_t count, loff_t *ppos)
{
	struct msm_hcd *mhcd = file->private_data;
	char kbuf[10];
	u32 data = 0;

	memset(kbuf, 0, 10);

	if (copy_from_user(kbuf, buf, min_t(size_t, sizeof(kbuf) - 1, count)))
		return -EFAULT;

	if (sscanf(kbuf, "%x", &data) != 1)
		return -EINVAL;

	pm_runtime_get(mhcd->dev);
	if (msm_ulpi_write(mhcd, data, addr) < 0) {
		dev_err(mhcd->dev,
				"%s(): ulpi write timeout\n", __func__);
		return -ETIMEDOUT;
	}
	pm_runtime_put(mhcd->dev);

	return count;
}
示例#3
0
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_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 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;
}