diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 7cd0bd158d8..57bc58ba01a 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -511,12 +511,14 @@ static int msm_otg_suspend(struct msm_otg *motg) struct usb_phy *phy = &motg->phy; struct usb_bus *bus = phy->otg->host; struct msm_otg_platform_data *pdata = motg->pdata; - int cnt = 0; + int cnt = 0, val; + bool host_bus_suspend; if (atomic_read(&motg->in_lpm)) return 0; disable_irq(motg->irq); + host_bus_suspend = otg->host && !test_bit(ID, &motg->inputs); /* * Chipidea 45-nm PHY suspend sequence: * @@ -541,6 +543,17 @@ static int msm_otg_suspend(struct msm_otg *motg) ulpi_write(phy, 0x08, 0x09); } + /* + * Turn off the OTG comparators, if depends on PMIC for + * VBUS and ID notifications. + */ + if (motg->caps & ALLOW_PHY_COMP_DISABLE) { + val = ulpi_read(phy, ULPI_PWR_CLK_MNG_REG); + val |= OTG_COMP_DISABLE; + ulpi_write(phy, val, ULPI_PWR_CLK_MNG_REG); + motg->lpm_flags |= PHY_OTG_COMP_DISABLED; + } + /* * 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 @@ -570,9 +583,11 @@ static int msm_otg_suspend(struct msm_otg *motg) */ writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) - writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + if (motg->caps & ALLOW_PHY_RETENTION && !host_bus_suspend) { + writel_relaxed(readl_relaxed(USB_PHY_CTRL) & ~PHY_RETEN, + USB_PHY_CTRL); + motg->lpm_flags |= PHY_RETENTIONED; + } /* Ensure that above operation is completed before turning off clocks */ mb(); @@ -583,14 +598,19 @@ static int msm_otg_suspend(struct msm_otg *motg) if (!IS_ERR(motg->pclk_src)) clk_disable(motg->pclk_src); - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { + if (motg->caps & ALLOW_PHY_POWER_COLLAPSE && !host_bus_suspend) { msm_hsusb_ldo_enable(motg, 0); - msm_hsusb_config_vddcx(0); + motg->lpm_flags |= PHY_PWR_COLLAPSED; } - if (device_may_wakeup(phy->dev)) + if (motg->lpm_flags & PHY_RETENTIONED) + msm_hsusb_config_vddcx(0); + + if (device_may_wakeup(phy->dev)) { enable_irq_wake(motg->irq); + if (motg->pdata->pmic_id_irq) + enable_irq_wake(motg->pdata->pmic_id_irq); + } if (bus) clear_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); @@ -621,11 +641,16 @@ static int msm_otg_resume(struct msm_otg *motg) if (motg->core_clk) clk_enable(motg->core_clk); - if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && - motg->pdata->otg_control == OTG_PMIC_CONTROL) { + if (motg->lpm_flags & PHY_PWR_COLLAPSED) { msm_hsusb_ldo_enable(motg, 1); + motg->lpm_flags &= ~PHY_PWR_COLLAPSED; + } + + if (motg->lpm_flags & PHY_RETENTIONED) { msm_hsusb_config_vddcx(1); - writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + writel_relaxed(readl_relaxed(USB_PHY_CTRL) | PHY_RETEN, + USB_PHY_CTRL); + motg->lpm_flags &= ~PHY_RETENTIONED; } temp = readl(USB_USBCMD); @@ -660,8 +685,18 @@ static int msm_otg_resume(struct msm_otg *motg) } skip_phy_resume: + /* Turn on the OTG comparators on resume */ + if (motg->lpm_flags & PHY_OTG_COMP_DISABLED) { + temp = ulpi_read(phy, ULPI_PWR_CLK_MNG_REG); + temp &= ~OTG_COMP_DISABLE; + ulpi_write(phy, temp, ULPI_PWR_CLK_MNG_REG); + motg->lpm_flags &= ~PHY_OTG_COMP_DISABLED; + } if (device_may_wakeup(phy->dev)) disable_irq_wake(motg->irq); + if (motg->pdata->pmic_id_irq) + disable_irq_wake(motg->pdata->pmic_id_irq); + } if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); @@ -1564,7 +1599,7 @@ static irqreturn_t msm_pmic_id_irq(int irq, void *data) { struct msm_otg *motg = data; - if (atomic_read(&motg->in_lpm)) + if (atomic_read(&motg->in_lpm) && !motg->async_int) msm_otg_irq(motg->irq, motg); return IRQ_HANDLED; @@ -1861,13 +1896,20 @@ static int __init msm_otg_probe(struct platform_device *pdev) goto free_irq; } - if (motg->pdata->pmic_id_irq) { - ret = request_irq(motg->pdata->pmic_id_irq, msm_pmic_id_irq, - IRQF_TRIGGER_RISING | - IRQF_TRIGGER_FALLING, - "msm_otg", motg); - if (ret) { - dev_err(&pdev->dev, "request irq failed for PMIC ID\n"); + if (motg->pdata->otg_control == OTG_PMIC_CONTROL) { + if (motg->pdata->pmic_id_irq) { + ret = request_irq(motg->pdata->pmic_id_irq, + msm_pmic_id_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "msm_otg", motg); + if (ret) { + dev_err(&pdev->dev, "request irq failed for PMIC ID\n"); + goto remove_phy; + } + } else { + ret = -ENODEV; + dev_err(&pdev->dev, "PMIC IRQ for ID notifications doesn't exist\n"); goto remove_phy; } } @@ -1887,6 +1929,13 @@ static int __init msm_otg_probe(struct platform_device *pdev) if (motg->pdata->otg_control == OTG_PMIC_CONTROL) pm8921_charger_register_vbus_sn(&msm_otg_set_vbus_state); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL && + motg->pdata->pmic_id_irq) + motg->caps = ALLOW_PHY_POWER_COLLAPSE | + ALLOW_PHY_RETENTION | + ALLOW_PHY_COMP_DISABLE; + wake_lock(&motg->wlock); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 960a36af91f..8c67c56b85f 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -229,6 +229,29 @@ struct msm_otg { struct wake_lock wlock; struct notifier_block usbdev_nb; unsigned mA_port; + unsigned long caps; + /* + * Allowing PHY power collpase turns off the HSUSB 3.3v and 1.8v + * analog regulators while going to low power mode. + * Currently only 8960(28nm PHY) has the support to allowing PHY + * power collapse since it doesn't have leakage currents while + * turning off the power rails. + */ +#define ALLOW_PHY_POWER_COLLAPSE BIT(0) + /* + * Allow PHY RETENTION mode before turning off the digital + * voltage regulator(VDDCX). + */ +#define ALLOW_PHY_RETENTION BIT(1) + /* + * Disable the OTG comparators to save more power + * if depends on PMIC for VBUS and ID interrupts. + */ +#define ALLOW_PHY_COMP_DISABLE BIT(2) + unsigned long lpm_flags; +#define PHY_PWR_COLLAPSED BIT(0) +#define PHY_RETENTIONED BIT(1) +#define PHY_OTG_COMP_DISABLED BIT(2) }; struct msm_hsic_host_platform_data { diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index cb1889593da..b8a626b3a4c 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -43,6 +43,10 @@ #define ULPI_DATA(n) ((n) & 255) #define ULPI_DATA_READ(n) (((n) >> 8) & 255) +/* synopsys 28nm phy registers */ +#define ULPI_PWR_CLK_MNG_REG 0x89 +#define OTG_COMP_DISABLE BIT(0); + #define PHY_ALT_INT (1 << 28) /* PHY alternate interrupt */ #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */