diff options
author | Steve Lin <stlin@nvidia.com> | 2012-06-20 12:59:43 -0700 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-06-22 07:40:12 -0700 |
commit | 89da52e28b4fbbe9af0310f3fd82adafc7fa1d23 (patch) | |
tree | ad76d7ce0df1462006ec48acc3acbe4faf00d80c /arch/arm/mach-tegra/tegra3_usb_phy.c | |
parent | 1638783677893d0ba4050e67831e3f0c7d1aaaa1 (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.c | 192 |
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, }; |