diff options
-rw-r--r-- | arch/arm/mach-tegra/board-enterprise-baseband.c | 95 | ||||
-rw-r--r-- | arch/arm/mach-tegra/board-whistler-baseband.c | 95 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_usb_phy.c | 175 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_usb_phy.c | 192 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra_usb_phy.h | 3 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 2 | ||||
-rw-r--r-- | include/linux/platform_data/tegra_usb.h | 3 |
7 files changed, 354 insertions, 211 deletions
diff --git a/arch/arm/mach-tegra/board-enterprise-baseband.c b/arch/arm/mach-tegra/board-enterprise-baseband.c index 91f3d625dde1..3ad83ad4fe8a 100644 --- a/arch/arm/mach-tegra/board-enterprise-baseband.c +++ b/arch/arm/mach-tegra/board-enterprise-baseband.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/board-enterprise-baseband.c * - * Copyright (c) 2011, NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,10 +27,6 @@ #include <linux/err.h> #include <linux/wakelock.h> #include <linux/platform_data/tegra_usb.h> -#include <asm/mach-types.h> -#include <asm/mach/arch.h> -#include <mach/pinmux.h> -#include <mach/usb_phy.h> #include <mach/tegra_usb_modem_power.h> #include "devices.h" #include "gpio-names.h" @@ -46,38 +42,23 @@ #define AP2MDM_ACK2 TEGRA_GPIO_PE2 #define MDM2AP_ACK2 TEGRA_GPIO_PV0 -/* 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 - static struct wake_lock mdm_wake_lock; static struct gpio modem_gpios[] = { {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"}, {MODEM_RESET, GPIOF_IN, "MODEM RESET"}, {BB_RST_OUT, GPIOF_IN, "BB RST OUT"}, - {MDM2AP_ACK, GPIOF_IN, "MDM2AP_ACK"}, {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"}, {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"}, - {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 void baseband_phy_init(void); -static void baseband_phy_on(void); + +static void baseband_post_phy_on(void); static void baseband_pre_phy_off(void); -static void baseband_post_phy_off(void); -static bool ap2mdm_ack_gpio_off = false; static struct tegra_usb_phy_platform_ops ulpi_null_plat_ops = { - .init = baseband_phy_init, .pre_phy_off = baseband_pre_phy_off, - .post_phy_off = baseband_post_phy_off, - .post_phy_on = baseband_phy_on, + .post_phy_on = baseband_post_phy_on, }; static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { @@ -99,6 +80,7 @@ static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { .stpdirnxt_trimmer = 1, .dir_trimmer = 1, .clk = NULL, + .phy_restore_gpio = MDM2AP_ACK, }, .ops = &ulpi_null_plat_ops, }; @@ -124,77 +106,16 @@ static irqreturn_t mdm_start_thread(int irq, void *data) return IRQ_HANDLED; } -static void baseband_phy_init(void) +static void baseband_post_phy_on(void) { - static bool phy_init = false; - - if (!phy_init) { - /* set AP2MDM_ACK2 low */ - gpio_set_value(AP2MDM_ACK2, 0); - phy_init = true; - } - pr_info("%s\n", __func__); + /* set AP2MDM_ACK2 low */ + gpio_set_value(AP2MDM_ACK2, 0); } -static inline void null_phy_set_tristate(bool enable) -{ - 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); -} - - -static void baseband_post_phy_off(void) -{ - null_phy_set_tristate(true); -} - - static void baseband_pre_phy_off(void) { /* set AP2MDM_ACK2 high */ gpio_set_value(AP2MDM_ACK2, 1); - ap2mdm_ack_gpio_off = true; -} - -static void baseband_phy_on(void) -{ - if (ap2mdm_ack_gpio_off) { - - /* driving linestate using GPIO */ - gpio_set_value(ULPI_D0, 0); - gpio_set_value(ULPI_D1, 0); - - /* remove ULPI tristate */ - null_phy_set_tristate(false); - - gpio_set_value(AP2MDM_ACK2, 0); - - if (gpio_is_valid(MDM2AP_ACK2)) { - int retry = 20000; - while (retry) { - /* poll phy_restore_gpio high */ - if (gpio_get_value(MDM2AP_ACK2)) - break; - retry--; - } - - if (retry == 0) - pr_info("phy_restore_gpio timeout\n"); - } - ap2mdm_ack_gpio_off = false; - } } static void baseband_start(void) diff --git a/arch/arm/mach-tegra/board-whistler-baseband.c b/arch/arm/mach-tegra/board-whistler-baseband.c index b602cd3f44c1..ad3dbd316ded 100644 --- a/arch/arm/mach-tegra/board-whistler-baseband.c +++ b/arch/arm/mach-tegra/board-whistler-baseband.c @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/board-whistler-baseband.c * - * Copyright (C) 2011 NVIDIA Corporation + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -16,32 +16,18 @@ #include <linux/kernel.h> #include <linux/init.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/tegra_caif.h> #include <mach/tegra_usb_modem_power.h> - #include "board.h" #include "board-whistler-baseband.h" -static void baseband_phy_init(void); -static void baseband_phy_on(void); -static void baseband_pre_phy_off(void); -static void baseband_post_phy_off(void); -static bool ap2mdm_ack_gpio_off = false; static struct wake_lock mdm_wake_lock; static struct gpio modem_gpios[] = { {MODEM_PWR_ON, GPIOF_OUT_INIT_LOW, "MODEM PWR ON"}, {MODEM_RESET, GPIOF_IN, "MODEM RESET"}, {BB_RST_OUT, GPIOF_IN, "BB RST OUT"}, - {MDM2AP_ACK, GPIOF_IN, "MDM2AP_ACK"}, {AP2MDM_ACK2, GPIOF_OUT_INIT_HIGH, "AP2MDM ACK2"}, {AP2MDM_ACK, GPIOF_OUT_INIT_LOW, "AP2MDM ACK"}, - {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 __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = { @@ -57,11 +43,12 @@ static __initdata struct tegra_pingroup_config whistler_null_ulpi_pinmux[] = { TEGRA_TRI_NORMAL}, }; +static void baseband_post_phy_on(void); +static void baseband_pre_phy_off(void); + static struct tegra_usb_phy_platform_ops ulpi_null_plat_ops = { - .init = baseband_phy_init, .pre_phy_off = baseband_pre_phy_off, - .post_phy_off = baseband_post_phy_off, - .post_phy_on = baseband_phy_on, + .post_phy_on = baseband_post_phy_on, }; static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { @@ -83,6 +70,7 @@ static struct tegra_usb_platform_data tegra_ehci2_ulpi_null_pdata = { .stpdirnxt_trimmer = 1, .dir_trimmer = 1, .clk = NULL, + .phy_restore_gpio = MDM2AP_ACK, }, .ops = &ulpi_null_plat_ops, }; @@ -107,72 +95,18 @@ static irqreturn_t mdm_start_thread(int irq, void *data) return IRQ_HANDLED; } -static inline void null_phy_set_tristate(bool enable) +static void baseband_post_phy_on(void) { - 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); -} - -static void baseband_phy_init(void) -{ - static bool phy_init; - - if (!phy_init) { - /* set AP2MDM_ACK2 low */ - gpio_set_value(AP2MDM_ACK2, 0); - phy_init = true; - } - pr_info("%s\n", __func__); + /* set AP2MDM_ACK2 low */ + gpio_set_value(AP2MDM_ACK2, 0); } static void baseband_pre_phy_off(void) { /* set AP2MDM_ACK2 high */ gpio_set_value(AP2MDM_ACK2, 1); - ap2mdm_ack_gpio_off = true; -} - -static void baseband_post_phy_off(void) -{ - null_phy_set_tristate(true); } -static void baseband_phy_on(void) -{ - if (ap2mdm_ack_gpio_off) { - - /* driving linestate using 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 */ - null_phy_set_tristate(false); - - gpio_set_value(AP2MDM_ACK2, 0); - - if (gpio_is_valid(MDM2AP_ACK2)) { - int retry = 20000; - while (retry) { - /* poll phy_restore_gpio high */ - if (gpio_get_value(MDM2AP_ACK2)) - break; - retry--; - } - - if (retry == 0) - pr_info("phy_restore_gpio timeout\n"); - } - ap2mdm_ack_gpio_off = false; - } -} - - static void baseband_start(void) { /* @@ -205,17 +139,6 @@ static int baseband_init(void) tegra_pinmux_set_pullupdown(TEGRA_PINGROUP_UAC, TEGRA_PUPD_PULL_UP); - tegra_gpio_enable(MODEM_PWR_ON); - tegra_gpio_enable(MODEM_RESET); - tegra_gpio_enable(AP2MDM_ACK2); - tegra_gpio_enable(BB_RST_OUT); - tegra_gpio_enable(AP2MDM_ACK); - tegra_gpio_enable(MDM2AP_ACK); - tegra_gpio_enable(TEGRA_GPIO_PY3); - tegra_gpio_enable(TEGRA_GPIO_PY1); - tegra_gpio_enable(TEGRA_GPIO_PO1); - tegra_gpio_enable(TEGRA_GPIO_PO2); - /* export GPIO for user space access through sysfs */ gpio_export(MODEM_PWR_ON, false); 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; 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, }; diff --git a/arch/arm/mach-tegra/tegra_usb_phy.h b/arch/arm/mach-tegra/tegra_usb_phy.h index 0375b5aac812..c399c31b18ae 100644 --- a/arch/arm/mach-tegra/tegra_usb_phy.h +++ b/arch/arm/mach-tegra/tegra_usb_phy.h @@ -1,7 +1,7 @@ /* * arch/arm/mach-tegra/include/mach/tegra_usb_phy.h * - * Copyright (C) 2011 NVIDIA Corporation. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -93,6 +93,7 @@ struct tegra_usb_phy { bool phy_power_on; bool remote_wakeup; bool hw_accessible; + bool ulpi_clk_padout_ena; }; int usb_phy_reg_status_wait(void __iomem *reg, u32 mask, diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index 041a5434ca03..b072283da294 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -115,7 +115,7 @@ int usb_phy_reg_status_wait(void __iomem *reg, u32 mask, return -1; } -int tegra_usb_phy_init_ops(struct tegra_usb_phy *phy) +static int tegra_usb_phy_init_ops(struct tegra_usb_phy *phy) { int err = 0; diff --git a/include/linux/platform_data/tegra_usb.h b/include/linux/platform_data/tegra_usb.h index 97472714e8e6..81b27e89631c 100644 --- a/include/linux/platform_data/tegra_usb.h +++ b/include/linux/platform_data/tegra_usb.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2010-2011 NVIDIA Corporation + * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -61,6 +61,7 @@ struct tegra_ulpi_config { u8 stpdirnxt_trimmer; u8 dir_trimmer; const char *clk; + int phy_restore_gpio; }; /** |