diff options
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_usb_phy.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_usb_phy.c | 175 |
1 files changed, 160 insertions, 15 deletions
diff --git a/arch/arm/mach-tegra/tegra2_usb_phy.c b/arch/arm/mach-tegra/tegra2_usb_phy.c index f0d5ebda2a37..a3440f41bf3c 100644 --- a/arch/arm/mach-tegra/tegra2_usb_phy.c +++ b/arch/arm/mach-tegra/tegra2_usb_phy.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/tegra2_usb_phy.c * - * Copyright (C) 2011 NVIDIA Corporation + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. * * * This software is licensed under the terms of the GNU General Public @@ -28,11 +28,13 @@ #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 <asm/mach-types.h> #include <mach/usb_phy.h> #include "tegra_usb_phy.h" +#include "gpio-names.h" #include "fuse.h" @@ -256,6 +258,15 @@ #define FUSE_USB_CALIB_0 0x1F0 #define FUSE_USB_CALIB_XCVR_SETUP(x) (((x) & 0x7F) << 0) +#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 @@ -266,6 +277,7 @@ #define DBG(stuff...) do {} while (0) #endif + static DEFINE_SPINLOCK(utmip_pad_lock); static int utmip_pad_count; static int utmip_pad_state_on; @@ -1548,6 +1560,97 @@ 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) +{ + 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) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; + + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, 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(0x80b10034, apb_misc + APB_MISC_GP_OBSCTRL_0); + slv0_obs = readl(apb_misc + APB_MISC_GP_OBSDATA_0); + + writel(0x80b10038, 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) { @@ -1561,7 +1664,7 @@ 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; } @@ -1571,11 +1674,48 @@ static int ulpi_null_phy_irq(struct tegra_usb_phy *phy) return IRQ_HANDLED; } +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); + val = readl(base + USB_USBCMD); val |= USB_USBCMD_RESET; writel(val, base + USB_USBCMD); @@ -1605,10 +1745,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; } @@ -1618,7 +1755,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) { @@ -1682,11 +1818,11 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy) /* set ULPI trimmers */ ulpi_set_trimmer(phy); - 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); @@ -1699,12 +1835,20 @@ static int ulpi_null_phy_power_on(struct tegra_usb_phy *phy) return 0; } - -static 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; } @@ -1720,9 +1864,7 @@ 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); } @@ -1765,11 +1907,14 @@ static struct tegra_usb_phy_ops ulpi_link_phy_ops = { }; static struct tegra_usb_phy_ops ulpi_null_phy_ops = { + .open = ulpi_null_phy_open, + .close = ulpi_null_phy_close, .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, }; static struct tegra_usb_phy_ops icusb_phy_ops; |