summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra3_usb_phy.c
diff options
context:
space:
mode:
authorSteve Lin <stlin@nvidia.com>2012-06-20 12:59:43 -0700
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-06-22 07:40:12 -0700
commit89da52e28b4fbbe9af0310f3fd82adafc7fa1d23 (patch)
treead76d7ce0df1462006ec48acc3acbe4faf00d80c /arch/arm/mach-tegra/tegra3_usb_phy.c
parent1638783677893d0ba4050e67831e3f0c7d1aaaa1 (diff)
arm: tegra: usb: restore the AP/modem handshaking
Restore the AP/modem handshaking functions and clean up the null phy driver. Bug 996035 Signed-off-by: Steve Lin <stlin@nvidia.com> Reviewed-on: http://git-master/r/109044 (cherry picked from commit ff27e8a48a53fe70949d95915f62dd2e03c73df7) Change-Id: I12a2401a7fcc540a657ab15378d440ef85561001 Reviewed-on: http://git-master/r/110145 Reviewed-by: Automatic_Commit_Validation_User Tested-by: Steve Lin <stlin@nvidia.com> Reviewed-by: Steve Lin <stlin@nvidia.com>
Diffstat (limited to 'arch/arm/mach-tegra/tegra3_usb_phy.c')
-rw-r--r--arch/arm/mach-tegra/tegra3_usb_phy.c192
1 files changed, 172 insertions, 20 deletions
diff --git a/arch/arm/mach-tegra/tegra3_usb_phy.c b/arch/arm/mach-tegra/tegra3_usb_phy.c
index 4af292e86324..85fb3db316d0 100644
--- a/arch/arm/mach-tegra/tegra3_usb_phy.c
+++ b/arch/arm/mach-tegra/tegra3_usb_phy.c
@@ -26,9 +26,11 @@
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_data/tegra_usb.h>
+#include <mach/clk.h>
#include <mach/iomap.h>
#include <mach/pinmux.h>
#include "tegra_usb_phy.h"
+#include "gpio-names.h"
#include "fuse.h"
#define USB_USBCMD 0x130
@@ -446,6 +448,15 @@
#define XCVR_SETUP_MSB_MASK 0x70
#define XCVR_SETUP_LSB_MAX_VAL 0xF
+#define APB_MISC_GP_OBSCTRL_0 0x818
+#define APB_MISC_GP_OBSDATA_0 0x81c
+
+/* ULPI GPIO */
+#define ULPI_STP TEGRA_GPIO_PY3
+#define ULPI_DIR TEGRA_GPIO_PY1
+#define ULPI_D0 TEGRA_GPIO_PO1
+#define ULPI_D1 TEGRA_GPIO_PO2
+
/* These values (in milli second) are taken from the battery charging spec */
#define TDP_SRC_ON_MS 100
#define TDPSRC_CON_MS 40
@@ -462,6 +473,7 @@
#define PHY_DBG(stuff...) do {} while (0)
#endif
+
static u32 utmip_rctrl_val, utmip_tctrl_val;
static DEFINE_SPINLOCK(utmip_pad_lock);
static int utmip_pad_count;
@@ -2395,6 +2407,7 @@ static void ulpi_set_host(void __iomem *base)
unsigned long val;
val = readl(base + USB_USBMODE);
+ val &= ~USB_USBMODE_MASK;
val |= USB_USBMODE_HOST;
writel(val, base + USB_USBMODE);
@@ -2403,6 +2416,106 @@ static void ulpi_set_host(void __iomem *base)
writel(val, base + HOSTPC1_DEVLC);
}
+static inline void ulpi_pinmux_bypass(struct tegra_usb_phy *phy, bool enable)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+
+ if (enable)
+ val |= ULPI_OUTPUT_PINMUX_BYP;
+ else
+ val &= ~ULPI_OUTPUT_PINMUX_BYP;
+
+ writel(val, base + ULPI_TIMING_CTRL_0);
+}
+
+static inline void ulpi_null_phy_set_tristate(bool enable)
+{
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL;
+
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA0, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA1, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA2, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA3, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA4, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA5, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA6, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA7, tristate);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_NXT, tristate);
+
+ if (enable)
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate);
+#endif
+}
+
+static void ulpi_null_phy_obs_read(void)
+{
+ static void __iomem *apb_misc;
+ unsigned slv0_obs, s2s_obs;
+
+ if (!apb_misc)
+ apb_misc = ioremap(TEGRA_APB_MISC_BASE, TEGRA_APB_MISC_SIZE);
+
+ writel(0x80d1003c, apb_misc + APB_MISC_GP_OBSCTRL_0);
+ slv0_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
+
+ writel(0x80d10040, apb_misc + APB_MISC_GP_OBSCTRL_0);
+ s2s_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0);
+
+ pr_debug("slv0 obs: %08x\ns2s obs: %08x\n", slv0_obs, s2s_obs);
+}
+
+static const struct gpio ulpi_gpios[] = {
+ {ULPI_STP, GPIOF_IN, "ULPI_STP"},
+ {ULPI_DIR, GPIOF_OUT_INIT_LOW, "ULPI_DIR"},
+ {ULPI_D0, GPIOF_OUT_INIT_LOW, "ULPI_D0"},
+ {ULPI_D1, GPIOF_OUT_INIT_LOW, "ULPI_D1"},
+};
+
+static int ulpi_null_phy_open(struct tegra_usb_phy *phy)
+{
+ struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+ int ret;
+
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+ ret = gpio_request_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+ if (ret)
+ return ret;
+
+ if (gpio_is_valid(config->phy_restore_gpio)) {
+ ret = gpio_request(config->phy_restore_gpio, "phy_restore");
+ if (ret)
+ goto err_gpio_free;
+
+ gpio_direction_input(config->phy_restore_gpio);
+ }
+
+ tegra_periph_reset_assert(phy->ctrlr_clk);
+ udelay(10);
+ tegra_periph_reset_deassert(phy->ctrlr_clk);
+
+ return 0;
+
+err_gpio_free:
+ gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+ return ret;
+}
+
+static void ulpi_null_phy_close(struct tegra_usb_phy *phy)
+{
+ struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+
+ if (gpio_is_valid(config->phy_restore_gpio))
+ gpio_free(config->phy_restore_gpio);
+
+ gpio_free_array(ulpi_gpios, ARRAY_SIZE(ulpi_gpios));
+}
static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
{
@@ -2416,10 +2529,11 @@ static int ulpi_null_phy_power_off(struct tegra_usb_phy *phy)
phy->phy_clk_on = false;
phy->hw_accessible = false;
-
+ ulpi_null_phy_set_tristate(true);
return 0;
}
+/* NOTE: this function must be called before ehci reset */
static int ulpi_null_phy_init(struct tegra_usb_phy *phy)
{
unsigned long val;
@@ -2444,6 +2558,7 @@ static int ulpi_null_phy_irq(struct tegra_usb_phy *phy)
return IRQ_HANDLED;
}
+/* NOTE: this function must be called after ehci reset */
static int ulpi_null_phy_cmd_reset(struct tegra_usb_phy *phy)
{
unsigned long val;
@@ -2465,11 +2580,47 @@ static int ulpi_null_phy_cmd_reset(struct tegra_usb_phy *phy)
return 0;
}
+static int ulpi_null_phy_restore(struct tegra_usb_phy *phy)
+{
+ struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
+ unsigned long timeout;
+ int ulpi_stp = ULPI_STP;
+
+ if (gpio_is_valid(config->phy_restore_gpio))
+ ulpi_stp = config->phy_restore_gpio;
+
+ /* disable ULPI pinmux bypass */
+ ulpi_pinmux_bypass(phy, false);
+
+ /* driving linstate by GPIO */
+ gpio_set_value(ULPI_D0, 0);
+ gpio_set_value(ULPI_D1, 0);
+
+ /* driving DIR high */
+ gpio_set_value(ULPI_DIR, 1);
+
+ /* remove ULPI tristate */
+ ulpi_null_phy_set_tristate(false);
+
+ /* wait for STP high */
+ timeout = jiffies + msecs_to_jiffies(25);
+
+ while (!gpio_get_value(ulpi_stp)) {
+ if (time_after(jiffies, timeout)) {
+ pr_warn("phy restore timeout\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
ulpi_null_phy_init(phy);
val = readl(base + USB_USBCMD);
@@ -2481,11 +2632,6 @@ static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
pr_err("%s: timeout waiting for reset\n", __func__);
}
- val = readl(base + USB_USBMODE);
- val &= ~USB_USBMODE_MASK;
- val |= USB_USBMODE_HOST;
- writel(val, base + USB_USBMODE);
-
ulpi_null_phy_cmd_reset(phy);
val = readl(base + USB_USBCMD);
@@ -2503,10 +2649,7 @@ static int ulpi_null_phy_lp0_resume(struct tegra_usb_phy *phy)
writel(val, base + USB_PORTSC);
udelay(10);
- /* disable ULPI pinmux bypass */
- val = readl(base + ULPI_TIMING_CTRL_0);
- val &= ~ULPI_OUTPUT_PINMUX_BYP;
- writel(val, base + ULPI_TIMING_CTRL_0);
+ ulpi_null_phy_restore(phy);
return 0;
}
@@ -2516,7 +2659,6 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_ulpi_config *config = &phy->pdata->u_cfg.ulpi;
- static bool cold_boot = true;
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
if (phy->phy_clk_on) {
@@ -2599,11 +2741,11 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
val &= ~ULPI_PADS_RESET;
writel(val, base + USB_SUSP_CTRL);
- if (cold_boot) {
+ if (!phy->ulpi_clk_padout_ena) {
val = readl(base + ULPI_TIMING_CTRL_0);
val |= ULPI_CLK_PADOUT_ENA;
writel(val, base + ULPI_TIMING_CTRL_0);
- cold_boot = false;
+ phy->ulpi_clk_padout_ena = true;
} else {
if (!readl(base + USB_ASYNCLISTADDR))
ulpi_null_phy_lp0_resume(phy);
@@ -2616,12 +2758,20 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy)
return 0;
}
-
-int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy, bool remote_wakeup)
+static int ulpi_null_phy_pre_resume(struct tegra_usb_phy *phy,
+ bool remote_wakeup)
{
DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
-
+ ulpi_null_phy_obs_read();
usb_phy_wait_for_sof(phy);
+ ulpi_null_phy_obs_read();
+ return 0;
+}
+
+static int ulpi_null_phy_post_resume(struct tegra_usb_phy *phy)
+{
+ DBG("%s(%d) inst:[%d]\n", __func__, __LINE__, phy->inst);
+ ulpi_null_phy_obs_read();
return 0;
}
@@ -2637,13 +2787,12 @@ static int ulpi_null_phy_resume(struct tegra_usb_phy *phy)
writel(val, base + ULPI_TIMING_CTRL_0);
/* enable ULPI pinmux bypass */
- val = readl(base + ULPI_TIMING_CTRL_0);
- val |= ULPI_OUTPUT_PINMUX_BYP;
- writel(val, base + ULPI_TIMING_CTRL_0);
+ ulpi_pinmux_bypass(phy, true);
udelay(5);
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
/* remove DIR tristate */
- tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR,
+ TEGRA_TRI_NORMAL);
#endif
}
return 0;
@@ -2676,12 +2825,15 @@ static struct tegra_usb_phy_ops uhsic_phy_ops = {
};
static struct tegra_usb_phy_ops ulpi_null_phy_ops = {
+ .open = ulpi_null_phy_open,
+ .close = ulpi_null_phy_close,
.init = ulpi_null_phy_init,
.irq = ulpi_null_phy_irq,
.power_on = ulpi_null_phy_power_on,
.power_off = ulpi_null_phy_power_off,
.pre_resume = ulpi_null_phy_pre_resume,
.resume = ulpi_null_phy_resume,
+ .post_resume = ulpi_null_phy_post_resume,
.reset = ulpi_null_phy_cmd_reset,
};