diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 628ba7d943da..1cdda6c05238 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -163,6 +163,32 @@ put_3p3: return rc; } +#ifdef CONFIG_PM_SLEEP +#define USB_PHY_SUSP_DIG_VOL 500000 +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_VDD_DIG_VOL_MIN; + else + min_vol = USB_PHY_SUSP_DIG_VOL; + + ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator " + "HSUSB_VDDCX\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} +#endif + static int msm_hsusb_ldo_set_mode(int on) { int ret = 0; @@ -434,27 +460,28 @@ static int msm_otg_suspend(struct msm_otg *motg) disable_irq(motg->irq); /* + * Chipidea 45-nm PHY suspend sequence: + * * Interrupt Latch Register auto-clear feature is not present * in all PHY versions. Latch register is clear on read type. * Clear latch register to avoid spurious wakeup from * low power mode (LPM). - */ - ulpi_read(otg, 0x14); - - /* + * * PHY comparators are disabled when PHY enters into low power * mode (LPM). Keep PHY comparators ON in LPM only when we expect * VBUS/Id notifications from USB PHY. Otherwise turn off USB * PHY comparators. This save significant amount of power. - */ - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(otg, 0x01, 0x30); - - /* + * * PLL is not turned off when PHY enters into low power mode (LPM). * Disable PLL for maximum power savings. */ - ulpi_write(otg, 0x08, 0x09); + + if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { + ulpi_read(otg, 0x14); + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(otg, 0x01, 0x30); + ulpi_write(otg, 0x08, 0x09); + } /* * PHY may take some time or even fail to enter into low power @@ -485,6 +512,10 @@ 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); + clk_disable(motg->pclk); clk_disable(motg->clk); if (motg->core_clk) @@ -493,6 +524,12 @@ 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) { + msm_hsusb_ldo_set_mode(0); + msm_hsusb_config_vddcx(0); + } + if (device_may_wakeup(otg->dev)) enable_irq_wake(motg->irq); if (bus) @@ -524,6 +561,13 @@ 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) { + msm_hsusb_ldo_set_mode(1); + msm_hsusb_config_vddcx(1); + writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + } + temp = readl(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 7d1babbff071..6e97a2d3d39f 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -24,6 +24,7 @@ #define USB_PORTSC (MSM_USB_BASE + 0x0184) #define USB_OTGSC (MSM_USB_BASE + 0x01A4) #define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -42,6 +43,7 @@ #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ +#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16)