summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra2_usb_phy.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_usb_phy.c')
-rw-r--r--arch/arm/mach-tegra/tegra2_usb_phy.c175
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;