ARM: tegra: usb_phy: Power down USB PMC controls
Krishna Yarlagadda [Fri, 9 Mar 2012 17:02:48 +0000 (22:02 +0530)]
Fix leakage current on AVDD_USB when system is in low power
mode.

Bug 934597

Signed-off-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-on: http://git-master/r/89160
(cherry picked from commit 6ef00a561be37a909a1c254afc6a14b6492c670f)

Change-Id: I3b8be6eac1ff40148e2de0935db6369909c8bb0a
Reviewed-on: http://git-master/r/93813
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Tested-by: Krishna Yarlagadda <kyarlagadda@nvidia.com>
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>

arch/arm/mach-tegra/usb_phy.c

index d154b06..18a4aa0 100644 (file)
 #define UTMIP_MASTER_ENABLE_P2         (1 << 16)
 #define UTMIP_MASTER_ENABLE_P1         (1 << 8)
 #define UTMIP_MASTER_ENABLE_P0         (1 << 0)
+#define UHSIC_MASTER_ENABLE_P0         (1 << 24)
+#define UHSIC_WAKE_VAL_P0(x)           (((x) & 0xf) << 28)
 
 #define PMC_USB_AO                     0xf0
+#define PMC_POWER_DOWN_MASK            0xffff
+#define HSIC_RESERVED_P0               (3 << 14)
+#define HSIC_STOBE_VAL_PD_P0   (1 << 13)
+#define HSIC_DATA_VAL_PD_P0    (1 << 12)
+#define USB_ID_PD(inst)                        (1 << ((4*(inst))+3))
+#define VBUS_WAKEUP_PD(inst)           (1 << ((4*(inst))+2))
 #define USBON_VAL_PD(inst)             (1 << ((4*(inst))+1))
 #define USBON_VAL_PD_P2                        (1 << 9)
 #define USBON_VAL_PD_P1                        (1 << 5)
 #define PMC_TCTRL_VAL(x)       (((x) & 0x1f) << 5)
 #define PMC_RCTRL_VAL(x)       (((x) & 0x1f) << 0)
 
+#define UHSIC_SLEEPWALK_REG    0x210
+#define UHSIC_DATA_RPD_D               (1 << 25)
+#define UHSIC_STRB_RPD_D               (1 << 24)
+#define UHSIC_DATA_RPD_C               (1 << 17)
+#define UHSIC_STRB_RPD_C               (1 << 16)
+#define UHSIC_DATA_RPD_B               (1 << 9)
+#define UHSIC_STRB_RPD_B               (1 << 8)
+#define UHSIC_DATA_RPD_A               (1 << 1)
+#define UHSIC_STRB_RPD_A               (1 << 0)
+
 static u32 utmip_rctrl_val, utmip_tctrl_val;
 
 #endif
@@ -990,6 +1008,92 @@ static void utmip_phy_enable_trking_data(struct tegra_usb_phy *phy)
        clk_disable(phy->pad_clk);
        init_done = true;
 }
+
+static void utmip_powerdown_pmc_wake_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+       unsigned  int inst = phy->instance;
+
+       /* power down UTMIP interfaces */
+       val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
+       val |= UTMIP_PWR(inst);
+       writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
+
+       /* setup sleep walk usb controller */
+       val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A |
+               UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B |
+               UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C |
+               UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D;
+       writel(val, pmc_base + PMC_SLEEPWALK_REG(inst));
+
+       /* Program thermally encoded RCTRL_VAL, TCTRL_VAL into PMC space */
+       val = readl(pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+       val = PMC_TCTRL_VAL(utmip_tctrl_val) | PMC_RCTRL_VAL(utmip_rctrl_val);
+       writel(val, pmc_base + PMC_UTMIP_TERM_PAD_CFG);
+
+       /* Turn over pad configuration to PMC */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~UTMIP_WAKE_VAL(inst, ~0);
+       val |= UTMIP_WAKE_VAL(inst, WAKE_VAL_NONE) |
+               UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst) |
+               UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst);
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+}
+
+static void utmip_powerup_pmc_wake_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+       unsigned  int inst = phy->instance;
+
+       /* Disable PMC master mode by clearing MASTER_EN */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~(UTMIP_RCTRL_USE_PMC(inst) | UTMIP_TCTRL_USE_PMC(inst) |
+                       UTMIP_FSLS_USE_PMC(inst) | UTMIP_MASTER_ENABLE(inst));
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+       mdelay(1);
+}
+
+static void uhsic_powerdown_pmc_wake_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+       /* turn on pad detectors for HSIC*/
+       val = readl(pmc_base + PMC_USB_AO);
+       val |= (HSIC_RESERVED_P0 | HSIC_STOBE_VAL_PD_P0 | HSIC_DATA_VAL_PD_P0);
+       writel(val, pmc_base + PMC_USB_AO);
+
+       /* enable pull downs on HSIC PMC */
+       val = UHSIC_STRB_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STRB_RPD_B |
+               UHSIC_DATA_RPD_B | UHSIC_STRB_RPD_C | UHSIC_DATA_RPD_C |
+               UHSIC_STRB_RPD_D | UHSIC_DATA_RPD_D;
+       writel(val, pmc_base + UHSIC_SLEEPWALK_REG);
+
+       /* Turn over pad configuration to PMC */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~UHSIC_WAKE_VAL_P0(~0);
+       val |= UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) | UHSIC_MASTER_ENABLE_P0;
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+}
+
+static void uhsic_powerup_pmc_wake_detect(struct tegra_usb_phy *phy)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+       /* turn on pad detectors for HSIC*/
+       val = readl(pmc_base + PMC_USB_AO);
+       val &= ~(HSIC_RESERVED_P0 | HSIC_STOBE_VAL_PD_P0 | HSIC_DATA_VAL_PD_P0);
+       writel(val, pmc_base + PMC_USB_AO);
+
+       /* Disable PMC master mode by clearing MASTER_EN */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~(UHSIC_MASTER_ENABLE_P0);
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+       mdelay(1);
+}
 #endif
 
 static unsigned int tegra_phy_xcvr_setup_value(struct tegra_utmip_config *cfg)
@@ -1183,6 +1287,9 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
        val &= ~HOSTPC1_DEVLC_PTS(~0);
        val |= HOSTPC1_DEVLC_STS;
        writel(val, base + HOSTPC1_DEVLC);
+
+       if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE)
+               utmip_powerup_pmc_wake_detect(phy);
 #endif
 
        return 0;
@@ -1347,6 +1454,8 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
        if (phy->mode == TEGRA_USB_PHY_MODE_HOST)
                utmip_setup_pmc_wake_detect(phy);
+       else
+               utmip_powerdown_pmc_wake_detect(phy);
 #endif
        if (phy->mode == TEGRA_USB_PHY_MODE_DEVICE) {
                val = readl(base + USB_SUSP_CTRL);
@@ -2173,6 +2282,10 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
        void __iomem *base = phy->regs;
        struct tegra_uhsic_config *uhsic_config = phy->config;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       uhsic_powerup_pmc_wake_detect(phy);
+#endif
+
        if (uhsic_config->enable_gpio != -1) {
                gpio_set_value_cansleep(uhsic_config->enable_gpio, 1);
                /* keep hsic reset asserted for 1 ms */
@@ -2307,6 +2420,10 @@ static int uhsic_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
        if (uhsic_config->post_phy_off && uhsic_config->post_phy_off())
                return -EAGAIN;
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       uhsic_powerdown_pmc_wake_detect(phy);
+#endif
+
        return 0;
 }
 
@@ -2480,9 +2597,15 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
 #ifndef CONFIG_ARCH_TEGRA_2x_SOC
        /* Power-up the VBUS detector for UTMIP PHY */
        if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
-               writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) &
-                       ~(TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 | TEGRA_PMC_USB_AO_ID_PD_P0),
-                       (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO));
+               unsigned long val;
+               void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+               /* turn on pad detectors for HSIC*/
+               val = readl(pmc_base + PMC_USB_AO);
+               val &= ~(TEGRA_PMC_USB_AO_VBUS_WAKEUP_PD_P0 | TEGRA_PMC_USB_AO_ID_PD_P0);
+               writel(val, pmc_base + PMC_USB_AO);
+
+               utmip_powerup_pmc_wake_detect(phy);
 
                if (usb_phy_data[phy->instance].vbus_reg_supply) {
                        phy->reg_vbus = regulator_get(NULL, usb_phy_data[phy->instance].vbus_reg_supply);
@@ -2494,11 +2617,10 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
                        }
                }
        }
-       if (instance == 2) {
-               writel(readl((IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO)) &
-                       (TEGRA_PMC_USB_AO_PD_P2),
-                       (IO_ADDRESS(TEGRA_PMC_BASE) + TEGRA_PMC_USB_AO));
+       if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC) {
+               uhsic_powerup_pmc_wake_detect(phy);
        }
+
 #endif
        if (((instance == 2) || (instance == 0)) &&
                (phy->mode == TEGRA_USB_PHY_MODE_HOST)) {
@@ -2967,6 +3089,52 @@ bool tegra_usb_phy_charger_detect(struct tegra_usb_phy *phy)
        return status;
 }
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+void tegra_usb_phy_power_down_pmc(void)
+{
+       unsigned long val;
+       void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+       /* power down all 3 UTMIP interfaces */
+       val = readl(pmc_base + PMC_UTMIP_MASTER_CONFIG);
+       val |= UTMIP_PWR(0) | UTMIP_PWR(1) | UTMIP_PWR(2);
+       writel(val, pmc_base + PMC_UTMIP_MASTER_CONFIG);
+
+       /* turn on pad detectors */
+       writel(PMC_POWER_DOWN_MASK, pmc_base + PMC_USB_AO);
+
+       /* setup sleep walk fl all 3 usb controllers */
+       val = UTMIP_USBOP_RPD_A | UTMIP_USBON_RPD_A | UTMIP_HIGHZ_A |
+               UTMIP_USBOP_RPD_B | UTMIP_USBON_RPD_B | UTMIP_HIGHZ_B |
+               UTMIP_USBOP_RPD_C | UTMIP_USBON_RPD_C | UTMIP_HIGHZ_C |
+               UTMIP_USBOP_RPD_D | UTMIP_USBON_RPD_D | UTMIP_HIGHZ_D;
+       writel(val, pmc_base + PMC_SLEEPWALK_REG(0));
+       writel(val, pmc_base + PMC_SLEEPWALK_REG(1));
+       writel(val, pmc_base + PMC_SLEEPWALK_REG(2));
+
+       /* enable pull downs on HSIC PMC */
+       val = UHSIC_STRB_RPD_A | UHSIC_DATA_RPD_A | UHSIC_STRB_RPD_B |
+               UHSIC_DATA_RPD_B | UHSIC_STRB_RPD_C | UHSIC_DATA_RPD_C |
+               UHSIC_STRB_RPD_D | UHSIC_DATA_RPD_D;
+       writel(val, pmc_base + UHSIC_SLEEPWALK_REG);
+
+       /* Turn over pad configuration to PMC */
+       val = readl(pmc_base + PMC_SLEEP_CFG);
+       val &= ~UTMIP_WAKE_VAL(0, ~0);
+       val &= ~UTMIP_WAKE_VAL(1, ~0);
+       val &= ~UTMIP_WAKE_VAL(2, ~0);
+       val &= ~UHSIC_WAKE_VAL_P0(~0);
+       val |= UTMIP_WAKE_VAL(0, WAKE_VAL_NONE) | UHSIC_WAKE_VAL_P0(WAKE_VAL_NONE) |
+       UTMIP_WAKE_VAL(1, WAKE_VAL_NONE) | UTMIP_WAKE_VAL(2, WAKE_VAL_NONE) |
+       UTMIP_RCTRL_USE_PMC(0) | UTMIP_RCTRL_USE_PMC(1) | UTMIP_RCTRL_USE_PMC(2) |
+       UTMIP_TCTRL_USE_PMC(0) | UTMIP_TCTRL_USE_PMC(1) | UTMIP_TCTRL_USE_PMC(2) |
+       UTMIP_FSLS_USE_PMC(0) | UTMIP_FSLS_USE_PMC(1) | UTMIP_FSLS_USE_PMC(2) |
+       UTMIP_MASTER_ENABLE(0) | UTMIP_MASTER_ENABLE(1) | UTMIP_MASTER_ENABLE(2) |
+       UHSIC_MASTER_ENABLE_P0;
+       writel(val, pmc_base + PMC_SLEEP_CFG);
+}
+#endif
+
 int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size)
 {
        if (pdata) {
@@ -2980,6 +3148,10 @@ int __init tegra_usb_phy_init(struct usb_phy_plat_data *pdata, int size)
                }
        }
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       tegra_usb_phy_power_down_pmc();
+#endif
+
        return 0;
 }