Exemple #1
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;

	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");
    }
}
Exemple #7
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,
							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;
}
Exemple #9
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;
}
Exemple #10
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;
	}
}
Exemple #11
0
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;
	}
}
Exemple #14
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;
}
Exemple #15
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;
}
Exemple #16
0
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);
	}
}
Exemple #17
0
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;
}
Exemple #18
0
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;
}
Exemple #21
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;
	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;
}
Exemple #22
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;
}
Exemple #24
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;
}
Exemple #25
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;
}
Exemple #29
0
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;
}