summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra2_usb_phy.c
diff options
context:
space:
mode:
authorSuresh Mangipudi <smangipudi@nvidia.com>2012-08-01 11:20:58 +0530
committerSimone Willett <swillett@nvidia.com>2012-08-02 09:25:07 -0700
commitd228a8472dfa7b12e8841bc340ce54b9a7c1db10 (patch)
treedfdbf9ffafb90b235383210ca7226d228d1eeeb8 /arch/arm/mach-tegra/tegra2_usb_phy.c
parentd9b961a08ca88964276824fba2916154b0e7a0ca (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.c186
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 = {