arm: tegra: usb: restore the AP/modem handshaking
Steve Lin [Wed, 20 Jun 2012 19:59:43 +0000 (12:59 -0700)]
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>

arch/arm/mach-tegra/board-enterprise-baseband.c
arch/arm/mach-tegra/board-whistler-baseband.c
arch/arm/mach-tegra/tegra2_usb_phy.c
arch/arm/mach-tegra/tegra3_usb_phy.c
arch/arm/mach-tegra/tegra_usb_phy.h
arch/arm/mach-tegra/usb_phy.c
include/linux/platform_data/tegra_usb.h

index 91f3d62..3ad83ad 100644 (file)
@@ -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
 #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"
 #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)
index b602cd3..ad3dbd3 100644 (file)
@@ -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
 
 #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);
 
index f0d5ebd..a3440f4 100644 (file)
@@ -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
 #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"
 
 
 #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
 #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;
index 4af292e..85fb3db 100644 (file)
 #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
 #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
 #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,
 };
 
index 0375b5a..c399c31 100644 (file)
@@ -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,
index 041a543..b072283 100644 (file)
@@ -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;
 
index 9747271..81b27e8 100644 (file)
@@ -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;
 };
 
 /**