From 791188bf77cb511eabd3bd3ac8935596cd850245 Mon Sep 17 00:00:00 2001 From: Anji jonnala Date: Wed, 15 Jun 2011 20:05:26 +0530 Subject: [PATCH] USB: OTG: Implement aggressive power management while going to LPM USB HW can turn off the voltage regulators, OTG comparators and can enable PHY retention mode as a part of aggressive power management while going to LPM provided that PMIC has capability to detect the VBUS/ID change and doesn't have any leakage currents while turning off the regulators. 8960 doesn't have the leakage currents and PM8921 has capability to detect VBUS and ID change. Hence Implement the aggressive power management for 8960. Initialize the PHY capabilities to support aggressive power management in the probe and use it to implement aggressive power management. Change-Id: If3e966b0c81b3588caa5ca93e9e877e4f777892c Signed-off-by: Anji jonnala --- drivers/usb/otg/msm_otg.c | 87 +++++++++++++++++++++++++------- include/linux/usb/msm_hsusb.h | 23 +++++++++ include/linux/usb/msm_hsusb_hw.h | 4 ++ 3 files changed, 95 insertions(+), 19 deletions(-) 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 */