diff options
author | Suresh Mangipudi <smangipudi@nvidia.com> | 2012-08-01 11:20:58 +0530 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-08-02 09:25:07 -0700 |
commit | d228a8472dfa7b12e8841bc340ce54b9a7c1db10 (patch) | |
tree | dfdbf9ffafb90b235383210ca7226d228d1eeeb8 /arch/arm/mach-tegra/tegra2_usb_phy.c | |
parent | d9b961a08ca88964276824fba2916154b0e7a0ca (diff) |
arm: tegra: usb: link ulpi resume sequence
lp0 resume for link_ulpi_phy has been modified.To fix the issue of
frequent disconnects while resuming from LP0.
Bug 997267
Change-Id: If92b8245b154e9b872b80d80a2ab359c87107a39
Signed-off-by: Suresh Mangipudi <smangipudi@nvidia.com>
Reviewed-on: http://git-master/r/119511
Reviewed-by: Simone Willett <swillett@nvidia.com>
Tested-by: Simone Willett <swillett@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_usb_phy.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_usb_phy.c | 186 |
1 files changed, 146 insertions, 40 deletions
diff --git a/arch/arm/mach-tegra/tegra2_usb_phy.c b/arch/arm/mach-tegra/tegra2_usb_phy.c index c7f4a771b1d8..aae2fe9bf56a 100644 --- a/arch/arm/mach-tegra/tegra2_usb_phy.c +++ b/arch/arm/mach-tegra/tegra2_usb_phy.c @@ -47,11 +47,13 @@ #define USB_USBSTS_SRI (1 << 7) #define USB_USBSTS_HCH (1 << 12) +#define USB_USBINTR 0x148 + #define USB_ASYNCLISTADDR 0x158 #define USB_TXFILLTUNING 0x164 #define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16) -#define USB_FIFO_TXFILL_MASK 0x1f0000 +#define USB_FIFO_TXFILL_MASK 0x3f0000 #define ULPI_VIEWPORT 0x170 #define ULPI_WAKEUP (1 << 31) @@ -69,6 +71,7 @@ #define USB_PORTSC_PP (1 << 12) #define USB_PORTSC_LS(x) (((x) & 0x3) << 10) #define USB_PORTSC_SUSP (1 << 7) +#define USB_PORTSC_RESUME (1 << 6) #define USB_PORTSC_OCC (1 << 5) #define USB_PORTSC_PEC (1 << 3) #define USB_PORTSC_PE (1 << 2) @@ -710,7 +713,8 @@ static int utmi_phy_power_off(struct tegra_usb_phy *phy) val |= USB_PHY_CLK_VALID_INT_ENB; writel(val, base + USB_SUSP_CTRL); } else { - /* Disable PHY clock valid interrupts while going into suspend*/ + /* Disable PHY clock valid interrupts + while going into suspend*/ val = readl(base + USB_SUSP_CTRL); val &= ~USB_PHY_CLK_VALID_INT_ENB; writel(val, base + USB_SUSP_CTRL); @@ -932,7 +936,8 @@ static int utmi_phy_resume(struct tegra_usb_phy *phy) if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RESET, 0, 2500) < 0) { - pr_err("%s: timeout waiting for reset\n", __func__); + pr_err("%s: timeout waiting for reset\n", + __func__); } val = readl(base + USB_USBMODE_REG_OFFSET); @@ -949,7 +954,8 @@ static int utmi_phy_resume(struct tegra_usb_phy *phy) if (usb_phy_reg_status_wait(base + USB_USBCMD, USB_USBCMD_RS, USB_USBCMD_RS, 2500) < 0) { - pr_err("%s: timeout waiting for run bit\n", __func__); + pr_err("%s: timeout waiting for run bit\n", + __func__); } /* Enable Port Power */ @@ -959,7 +965,8 @@ static int utmi_phy_resume(struct tegra_usb_phy *phy) udelay(10); DBG("USB_USBSTS[0x%x] USB_PORTSC[0x%x]\n", - readl(base + USB_USBSTS), readl(base + USB_PORTSC)); + readl(base + USB_USBSTS), + readl(base + USB_PORTSC)); } } else { /* Restoring the pad powers */ @@ -1354,7 +1361,7 @@ static int ulpi_link_phy_open(struct tegra_usb_phy *phy) phy->ulpi_vp = otg_ulpi_create(&ulpi_viewport_access_ops, 0); phy->ulpi_vp->io_priv = phy->regs + ULPI_VIEWPORT; - + phy->linkphy_init = true; return err; } @@ -1450,49 +1457,74 @@ static int ulpi_link_phy_power_on(struct tegra_usb_phy *phy) } val = readl(base + USB_SUSP_CTRL); - val |= UHSIC_RESET; - writel(val, base + USB_SUSP_CTRL); - val = readl(base + ULPI_TIMING_CTRL_0); - val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; - writel(val, base + ULPI_TIMING_CTRL_0); + /* Case for lp0 */ + if (!(val & UHSIC_RESET)) { + val |= UHSIC_RESET; + writel(val, base + USB_SUSP_CTRL); - val = readl(base + USB_SUSP_CTRL); - val |= ULPI_PHY_ENABLE; - writel(val, base + USB_SUSP_CTRL); + val = 0; + writel(val, base + ULPI_TIMING_CTRL_1); - val = readl(base + USB_SUSP_CTRL); - val |= USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + ulpi_set_trimmer(phy); - if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, - USB_PHY_CLK_VALID, 2500)) - pr_err("%s: timeout waiting for phy to stabilize\n", __func__); + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; + writel(val, base + ULPI_TIMING_CTRL_0); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, TEGRA_TRI_NORMAL); +#endif + val = readl(base + USB_SUSP_CTRL); + val |= ULPI_PHY_ENABLE; + writel(val, base + USB_SUSP_CTRL); - if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, USB_CLKEN, - USB_CLKEN, 2500)) - pr_err("%s: timeout waiting for AHB clock\n", __func__); + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_PHY_CLK_VALID, USB_PHY_CLK_VALID, 2500) < 0) + pr_err("%s: timeout waiting for phy" \ + "to stabilize\n", __func__); - val = readl(base + USB_SUSP_CTRL); - val &= ~USB_SUSP_CLR; - writel(val, base + USB_SUSP_CTRL); + val = readl(base + USB_TXFILLTUNING); + if ((val & USB_FIFO_TXFILL_MASK) != + USB_FIFO_TXFILL_THRES(0x10)) { + val = USB_FIFO_TXFILL_THRES(0x10); + writel(val, base + USB_TXFILLTUNING); + } + } else { + /* Case for auto resume*/ + val = readl(base + USB_SUSP_CTRL); + val |= USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); - val = 0; - writel(val, base + ULPI_TIMING_CTRL_1); + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_PHY_CLK_VALID, USB_PHY_CLK_VALID, 2500) < 0) + pr_err("%s: timeout waiting for phy" \ + "to stabilize\n", __func__); - ulpi_set_trimmer(phy); + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_CLKEN, USB_CLKEN, 2500) < 0) + pr_err("%s: timeout waiting for AHB clock\n", __func__); - /* Fix VbusInvalid due to floating VBUS */ - ret = otg_io_write(phy->ulpi_vp, 0x40, 0x08); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; + val = readl(base + USB_SUSP_CTRL); + val &= ~USB_SUSP_CLR; + writel(val, base + USB_SUSP_CTRL); } + if (phy->linkphy_init) { + /* To be done only incase of coldboot*/ + /* Fix VbusInvalid due to floating VBUS */ + ret = otg_io_write(phy->ulpi_vp, 0x40, 0x08); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } - ret = otg_io_write(phy->ulpi_vp, 0x80, 0x0B); - if (ret) { - pr_err("%s: ulpi write failed\n", __func__); - return ret; + ret = otg_io_write(phy->ulpi_vp, 0x80, 0x0B); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return ret; + } + phy->linkphy_init = false; } val = readl(base + USB_PORTSC); @@ -1510,6 +1542,7 @@ static inline void ulpi_link_phy_set_tristate(bool enable) #ifdef CONFIG_ARCH_TEGRA_2x_SOC int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; + tegra_pinmux_set_tristate(TEGRA_PINGROUP_CDEV2, tristate); tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate); tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); @@ -1535,6 +1568,7 @@ static void ulpi_link_phy_restore_end(struct tegra_usb_phy *phy) { unsigned long val; void __iomem *base = phy->regs; + int ret; DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst); @@ -1543,6 +1577,13 @@ static void ulpi_link_phy_restore_end(struct tegra_usb_phy *phy) writel(val, base + ULPI_TIMING_CTRL_0); ulpi_link_phy_set_tristate(false); + + udelay(10); + ret = otg_io_write(phy->ulpi_vp, 0x55, 0x04); + if (ret) { + pr_err("%s: ulpi write failed\n", __func__); + return; + } } static int ulpi_link_phy_resume(struct tegra_usb_phy *phy) @@ -1562,7 +1603,70 @@ static int ulpi_link_phy_resume(struct tegra_usb_phy *phy) return status; } -static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, bool enable) +static int ulpi_link_phy_pre_resume(struct tegra_usb_phy *phy, + bool remote_wakeup) +{ + int status = 0; + unsigned long val; + void __iomem *base = phy->regs; + DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst); + + val = readl(base + USB_PORTSC); + if (val & USB_PORTSC_RESUME) { + + val = readl(base + USB_USBCMD); + val &= ~USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + + /* detect remote wakeup */ + msleep(20); + + val = readl(base + USB_PORTSC); + + /* Poll until the controller clears RESUME and SUSPEND */ + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_PORTSC_RESUME, 0, 2500)) + pr_err("%s: timeout waiting for RESUME\n", __func__); + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_PORTSC_SUSP, 0, 2500)) + pr_err("%s: timeout waiting for SUSPEND\n", __func__); + + /* Since we skip remote wakeup event, + put controller in suspend again and + resume port later */ + val = readl(base + USB_PORTSC); + val |= USB_PORTSC_SUSP; + writel(val, base + USB_PORTSC); + mdelay(4); + /* Wait until port suspend completes */ + if (usb_phy_reg_status_wait(base + USB_SUSP_CTRL, + USB_PORTSC_SUSP, USB_PORTSC_SUSP, 2500)) + pr_err("%s: timeout waiting for" \ + "PORT_SUSPEND\n", __func__); + + /* Disable interrupts */ + writel(0, base + USB_USBINTR); + /* Clear the run bit to stop SOFs - 2LS WAR */ + val = readl(base + USB_USBCMD); + val &= ~USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + if (usb_phy_reg_status_wait(base + USB_USBSTS, + USB_USBSTS_HCH, USB_USBSTS_HCH, 2000)) { + pr_err("%s: timeout waiting for" \ + "USB_USBSTS_HCH\n", __func__); + } + usb_phy_wait_for_sof(phy); + + val = readl(base + USB_USBCMD); + val |= USB_USBCMD_RS; + writel(val, base + USB_USBCMD); + } + return status; +} + + +static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, + bool enable) { unsigned long val; void __iomem *base = phy->regs; @@ -1795,7 +1899,8 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy) val = readl(base + ULPIS2S_CTRL); val |= ULPIS2S_ENA; val |= ULPIS2S_SUPPORT_DISCONNECT; - val |= ULPIS2S_SPARE((phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) ? 3 : 1); + val |= ULPIS2S_SPARE((phy->pdata->op_mode == TEGRA_USB_OPMODE_HOST) + ? 3 : 1); val |= ULPIS2S_PLLU_MASTER_BLASTER60; writel(val, base + ULPIS2S_CTRL); @@ -1906,6 +2011,7 @@ static struct tegra_usb_phy_ops ulpi_link_phy_ops = { .power_on = ulpi_link_phy_power_on, .power_off = ulpi_link_phy_power_off, .resume = ulpi_link_phy_resume, + .pre_resume = ulpi_link_phy_pre_resume, }; static struct tegra_usb_phy_ops ulpi_null_phy_ops = { |