summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/board-enterprise-baseband.c95
-rw-r--r--arch/arm/mach-tegra/board-whistler-baseband.c95
-rw-r--r--arch/arm/mach-tegra/tegra2_usb_phy.c175
-rw-r--r--arch/arm/mach-tegra/tegra3_usb_phy.c192
-rw-r--r--arch/arm/mach-tegra/tegra_usb_phy.h3
-rw-r--r--arch/arm/mach-tegra/usb_phy.c2
-rw-r--r--include/linux/platform_data/tegra_usb.h3
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;
};
/**