diff options
author | Raj Jayaraman <rjayaraman@nvidia.com> | 2011-05-18 15:38:26 -0700 |
---|---|---|
committer | Manish Tuteja <mtuteja@nvidia.com> | 2011-07-11 02:20:58 -0700 |
commit | d3ca81dcf9ddb8b3dbf687aed75225abc65f0b54 (patch) | |
tree | c3c81793dae83b7807b3febd93be5ccef0ed4c67 /arch | |
parent | 911a357fd862702f6a01bd714b2b009efc1be974 (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.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 341 |
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) |