summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorRaj Jayaraman <rjayaraman@nvidia.com>2011-05-18 15:38:26 -0700
committerManish Tuteja <mtuteja@nvidia.com>2011-07-11 02:20:58 -0700
commitd3ca81dcf9ddb8b3dbf687aed75225abc65f0b54 (patch)
treec3c81793dae83b7807b3febd93be5ccef0ed4c67 /arch
parent911a357fd862702f6a01bd714b2b009efc1be974 (diff)
arm: tegra: ventana: Revised resume sequence for link ulpi.
Revised version of lp0 resume code for link ulpi. Bug 786136 Change-Id: I6b644a596148ec975d2e74113499358fc115066e Reviewed-on: http://git-master/r/39113 Reviewed-by: Manish Tuteja <mtuteja@nvidia.com> Tested-by: Manish Tuteja <mtuteja@nvidia.com>
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-tegra/include/mach/usb_phy.h1
-rw-r--r--arch/arm/mach-tegra/usb_phy.c341
2 files changed, 294 insertions, 48 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h
index 525573c9d297..efbcd78f45f7 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/arch/arm/mach-tegra/include/mach/usb_phy.h
@@ -84,6 +84,7 @@ struct tegra_xtal_freq;
struct tegra_usb_phy {
int instance;
+ bool initialized;
const struct tegra_xtal_freq *freq;
void __iomem *regs;
void __iomem *pad_regs;
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 5a352afb279b..d313e4213799 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -34,6 +34,7 @@
#include <mach/iomap.h>
#include <mach/pinmux.h>
#include "fuse.h"
+#include "gpio-names.h"
#define USB_USBCMD 0x140
#define USB_USBCMD_RS (1 << 0)
@@ -66,6 +67,7 @@
#define USB_WAKE_ON_CNNT_EN_DEV (1 << 3)
#define USB_WAKE_ON_DISCON_EN_DEV (1 << 4)
#define USB_SUSP_CLR (1 << 5)
+#define USB_CLKEN (1 << 6)
#define USB_PHY_CLK_VALID (1 << 7)
#define USB_PHY_CLK_VALID_INT_ENB (1 << 9)
#define UTMIP_RESET (1 << 11)
@@ -85,6 +87,8 @@
#define USB1_VBUS_SENSE_CTL_AB_SESS_VLD (2 << 1)
#define USB1_VBUS_SENSE_CTL_A_SESS_VLD (3 << 1)
+#define USB2_IF_ULPI_TIMING_CTRL_0_0_RESET_VAL 0x4000000
+
#define ULPIS2S_CTRL 0x418
#define ULPIS2S_ENA (1 << 0)
#define ULPIS2S_SUPPORT_DISCONNECT (1 << 2)
@@ -116,6 +120,9 @@
#define ULPI_DIR_TRIMMER_LOAD (1 << 24)
#define ULPI_DIR_TRIMMER_SEL(x) (((x) & 0x7) << 25)
+#define TEGRA_LVL2_CLK_GATE_OVRB 0xfc
+#define TEGRA_USB2_CLK_OVR_ON (1 << 10)
+
#define UTMIP_PLL_CFG1 0x804
#define UTMIP_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0)
#define UTMIP_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 27)
@@ -497,7 +504,7 @@ static void vbus_enable(int gpio)
if (gpio == -1)
return;
- gpio_status = gpio_request(gpio,"VBUS_USB");
+ gpio_status = gpio_request(gpio, "VBUS_USB");
if (gpio_status < 0) {
printk("VBUS_USB request GPIO FAILED\n");
WARN_ON(1);
@@ -584,7 +591,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
val = readl(base + UTMIP_XCVR_CFG0);
val &= ~(UTMIP_XCVR_LSBIAS_SEL | UTMIP_FORCE_PD_POWERDOWN |
- UTMIP_FORCE_PD2_POWERDOWN |UTMIP_FORCE_PDZI_POWERDOWN |
+ UTMIP_FORCE_PD2_POWERDOWN | UTMIP_FORCE_PDZI_POWERDOWN |
UTMIP_XCVR_SETUP(~0) | UTMIP_XCVR_LSFSLEW(~0) |
UTMIP_XCVR_LSRSLEW(~0) | UTMIP_XCVR_HSSLEW_MSB(~0));
val |= UTMIP_XCVR_SETUP(config->xcvr_setup);
@@ -757,71 +764,161 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
udelay(10);
}
+static void ulpi_pads_tristate(bool enable)
+{
+ if (enable) {
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_CDEV2,
+ TEGRA_TRI_TRISTATE);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA,
+ TEGRA_TRI_TRISTATE);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB,
+ TEGRA_TRI_TRISTATE);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA,
+ TEGRA_TRI_TRISTATE);
+ } else {
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_CDEV2,
+ TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA,
+ TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB,
+ TEGRA_TRI_NORMAL);
+ tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA,
+ TEGRA_TRI_NORMAL);
+ }
+ udelay(100);
+}
+
+static void ulpi_phy_suspend(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ /* Suspend the PHY, and wait for the clock to stop. */
+ val = readl(base + USB_PORTSC1);
+ val |= USB_PORTSC1_PHCD;
+ writel(val, base + USB_PORTSC1);
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) < 0)
+ pr_err("%s: timeout waiting for phy to stop\n", __func__);
+}
+
+static void ulpi_phy_resume(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+
+ /* Trigger a resume. */
+ val = readl(base + USB_SUSP_CTRL);
+ val |= USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(100);
+
+ /* Wait for PHY clock to start. */
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID)) {
+ pr_err("%s: timeout waiting for phy clock to \
+ start\n", __func__);
+ }
+
+ /* Wait for AHB clock to start. */
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_CLKEN,
+ USB_CLKEN)) {
+ pr_err("%s: timeout waiting for AHB clock to \
+ start\n", __func__);
+ }
+
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~USB_SUSP_CLR;
+ writel(val, base + USB_SUSP_CTRL);
+}
+
static int ulpi_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
{
int ret;
unsigned long val;
void __iomem *base = phy->regs;
struct tegra_ulpi_config *config = phy->config;
+ bool coldboot, lp0resume;
- gpio_direction_output(config->reset_gpio, 0);
- msleep(5);
- gpio_direction_output(config->reset_gpio, 1);
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val &= ULPI_OUTPUT_PINMUX_BYP;
+ coldboot = (!phy->initialized);
+ lp0resume = (!val && phy->initialized);
+ /* If resuming from LP0, configure PHY later.
+ Check TimingControl0 (PINMUX_BYP) / SuspControl0 (HSIC_RESET). */
+ if (lp0resume)
+ return 0;
+
+ /* Enable PHY reference clock. */
clk_enable(phy->clk);
msleep(1);
- val = readl(base + USB_SUSP_CTRL);
- val |= UHSIC_RESET;
- writel(val, base + USB_SUSP_CTRL);
+ /* Initialize controller if coldboot */
+ if (coldboot) {
+ /* Reset HSIC mode. */
+ val = readl(base + USB_SUSP_CTRL);
+ val |= UHSIC_RESET;
+ writel(val, base + USB_SUSP_CTRL);
- val = readl(base + ULPI_TIMING_CTRL_0);
- val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP;
- writel(val, base + ULPI_TIMING_CTRL_0);
+ /* Setup trimmer values. */
+ val = 0;
+ writel(val, base + ULPI_TIMING_CTRL_1);
- val = readl(base + USB_SUSP_CTRL);
- val |= ULPI_PHY_ENABLE;
- writel(val, base + USB_SUSP_CTRL);
+ val |= ULPI_DATA_TRIMMER_SEL(4);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+ val |= ULPI_DIR_TRIMMER_SEL(4);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
- val = 0;
- writel(val, base + ULPI_TIMING_CTRL_1);
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
- val |= ULPI_DATA_TRIMMER_SEL(4);
- val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
- val |= ULPI_DIR_TRIMMER_SEL(4);
- writel(val, base + ULPI_TIMING_CTRL_1);
- udelay(10);
+ /* Enable bypass for ULPI pads. */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= (ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP);
+ writel(val, base + ULPI_TIMING_CTRL_0);
- val |= ULPI_DATA_TRIMMER_LOAD;
- val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
- val |= ULPI_DIR_TRIMMER_LOAD;
- writel(val, base + ULPI_TIMING_CTRL_1);
+ /* Enable link mode. */
+ val = readl(base + USB_SUSP_CTRL);
+ val |= ULPI_PHY_ENABLE;
+ writel(val, base + USB_SUSP_CTRL);
- /* Fix VbusInvalid due to floating VBUS */
- ret = otg_io_write(phy->ulpi, 0x40, 0x08);
- if (ret) {
- pr_err("%s: ulpi write failed\n", __func__);
- return ret;
- }
+ /* Reset the PHY, when we are ready. */
+ gpio_direction_output(config->reset_gpio, 0);
+ msleep(5);
+ gpio_direction_output(config->reset_gpio, 1);
- ret = otg_io_write(phy->ulpi, 0x80, 0x0B);
- if (ret) {
- pr_err("%s: ulpi write failed\n", __func__);
- return ret;
- }
+ /* Wait for PHY clock. */
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID)) {
+ pr_err("%s: timeout waiting for phy clock to \
+ start\n", __func__);
+ }
- val = readl(base + USB_PORTSC1);
- val |= USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN;
- writel(val, base + USB_PORTSC1);
+ /* Fix VbusInvalid due to floating VBUS */
+ ret = otg_io_write(phy->ulpi, 0x40, 0x08);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
+ }
- val = readl(base + USB_SUSP_CTRL);
- val |= USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
- udelay(100);
+ ret = otg_io_write(phy->ulpi, 0x80, 0x0B);
+ if (ret) {
+ pr_err("%s: ulpi write failed\n", __func__);
+ return ret;
+ }
- val = readl(base + USB_SUSP_CTRL);
- val &= ~USB_SUSP_CLR;
- writel(val, base + USB_SUSP_CTRL);
+ /* PHY is initialized now. */
+ phy->initialized = 1;
+ } else {
+ /* Untristate ULPI output pads. */
+ ulpi_pads_tristate(0);
+
+ /* Resume the PHY. */
+ ulpi_phy_resume(phy);
+ }
return 0;
}
@@ -830,7 +927,6 @@ static void ulpi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
{
unsigned long val;
void __iomem *base = phy->regs;
- struct tegra_ulpi_config *config = phy->config;
/* Clear WKCN/WKDS/WKOC wake-on events that can cause the USB
* Controller to immediately bring the ULPI PHY out of low power
@@ -839,8 +935,152 @@ static void ulpi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
writel(val, base + USB_PORTSC1);
- gpio_direction_output(config->reset_gpio, 0);
+ /* Suspend the PHY. */
+ ulpi_phy_suspend(phy);
+
+ /* Disable PHY reference clock. */
clk_disable(phy->clk);
+
+ /* Tristate ULPI output pads. */
+ ulpi_pads_tristate(1);
+}
+
+static void ulpi_phy_restore_start(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *ulpi_config = phy->config;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) {
+
+ /* This function is only for LP0. So, the bypass should
+ be already disabled with POR. Just retain it anyway.*/
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val &= ~ULPI_OUTPUT_PINMUX_BYP;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL,
+ USB_PHY_CLK_VALID, 0) < 0)
+ pr_err("%s: timeout checking for invalid phy clock \
+ on resume\n", __func__);
+
+ /* Enable Null PHY mode. */
+ val = readl(base + USB_SUSP_CTRL);
+ val |= (UHSIC_RESET | ULPI_PHY_ENABLE);
+ writel(val, base + USB_SUSP_CTRL);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_SHADOW_CLK_LOOPBACK_EN;
+ val |= ULPI_SHADOW_CLK_SEL;
+ val |= ULPI_LBK_PAD_EN;
+ val |= ULPI_LBK_PAD_E_INPUT_OR;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = 0;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ val = ULPIS2S_ENA;
+ val |= ULPIS2S_PLLU_MASTER_BLASTER60;
+ val |= ULPIS2S_SPARE(1);
+ writel(val, base + ULPIS2S_CTRL);
+
+ /* select ULPI_CORE_CLK_SEL to SHADOW_CLK */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CORE_CLK_SEL;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ /* enable ULPI null clocks - can't set the trimmers
+ before this */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_OUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ val = ULPI_DATA_TRIMMER_SEL(4);
+ val |= ULPI_STPDIRNXT_TRIMMER_SEL(4);
+ val |= ULPI_DIR_TRIMMER_SEL(4);
+ writel(val, base + ULPI_TIMING_CTRL_1);
+ udelay(10);
+
+ val |= ULPI_DATA_TRIMMER_LOAD;
+ val |= ULPI_STPDIRNXT_TRIMMER_LOAD;
+ val |= ULPI_DIR_TRIMMER_LOAD;
+ writel(val, base + ULPI_TIMING_CTRL_1);
+
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= ULPI_CLK_PADOUT_ENA;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+ udelay(10);
+
+ if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
+ USB_PHY_CLK_VALID))
+ pr_err("%s: timeout waiting for null phy clk \
+ to start\n", __func__);
+ };
+}
+
+static void ulpi_phy_restore_end(struct tegra_usb_phy *phy)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ struct tegra_ulpi_config *ulpi_config = phy->config;
+
+ if (ulpi_config->inf_type == TEGRA_USB_LINK_ULPI) {
+
+ val = readl((IO_ADDRESS(TEGRA_CLK_RESET_BASE) +
+ TEGRA_LVL2_CLK_GATE_OVRB));
+ val |= TEGRA_USB2_CLK_OVR_ON;
+ writel(val, (IO_ADDRESS(TEGRA_CLK_RESET_BASE) +
+ TEGRA_LVL2_CLK_GATE_OVRB));
+
+ /* Suspend Null PHY, and wait for the clock to stop. */
+ ulpi_phy_suspend(phy);
+
+ /* Disable Null PHY. */
+ val = readl(base + ULPIS2S_CTRL);
+ val &= ~(ULPIS2S_ENA | ULPIS2S_SPARE(1));
+ writel(val, base + ULPIS2S_CTRL);
+
+ val = USB2_IF_ULPI_TIMING_CTRL_0_0_RESET_VAL;
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ val = readl(base + ULPIS2S_CTRL);
+ val &= ~(ULPIS2S_PLLU_MASTER_BLASTER60);
+ writel(val, base + ULPIS2S_CTRL);
+
+ /* Disable ULPI controller. */
+ val = readl(base + USB_SUSP_CTRL);
+ val &= ~(ULPI_PHY_ENABLE);
+ writel(val, base + USB_SUSP_CTRL);
+ udelay(5);
+
+ val = readl((IO_ADDRESS(TEGRA_CLK_RESET_BASE) +
+ TEGRA_LVL2_CLK_GATE_OVRB));
+ val &= ~TEGRA_USB2_CLK_OVR_ON;
+ writel(val, (IO_ADDRESS(TEGRA_CLK_RESET_BASE) +
+ TEGRA_LVL2_CLK_GATE_OVRB));
+
+ /* Enable bypass for ULPI pads. */
+ val = readl(base + ULPI_TIMING_CTRL_0);
+ val |= (ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP);
+ writel(val, base + ULPI_TIMING_CTRL_0);
+
+ /* Enable PHY reference clock. */
+ clk_enable(phy->clk);
+ msleep(1);
+
+ /* Untristate ULPI output pads. */
+ ulpi_pads_tristate(0);
+
+ val = readl(base + USB_SUSP_CTRL);
+ val |= (ULPI_PHY_ENABLE);
+ writel(val, base + USB_SUSP_CTRL);
+
+ /* Resume PHY. */
+ ulpi_phy_resume(phy);
+ }
}
static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd)
@@ -1091,6 +1331,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs,
return ERR_PTR(-ENOMEM);
phy->instance = instance;
+ phy->initialized = 0;
phy->regs = regs;
phy->config = config;
phy->mode = phy_mode;
@@ -1255,12 +1496,16 @@ void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
{
if (!phy_is_ulpi(phy))
utmi_phy_restore_start(phy, port_speed);
+ else
+ ulpi_phy_restore_start(phy);
}
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy)
{
if (!phy_is_ulpi(phy))
utmi_phy_restore_end(phy);
+ else
+ ulpi_phy_restore_end(phy);
}
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy)