diff options
author | Steve Lin <stlin@nvidia.com> | 2011-10-17 14:22:21 -0700 |
---|---|---|
committer | Ryan Wong <ryanw@nvidia.com> | 2011-10-19 17:10:51 -0700 |
commit | e694acfd92427450476c98905a8b87816edaec96 (patch) | |
tree | c2a1b3795bc70b1695b38a58b501a4476b3c4b6e | |
parent | 4711968d8d3864a0eedf383e31183b61519c59b1 (diff) |
arm: tegra: usb: Adding phy_restore functions for null ULPI
Adding phy_restore_start and phy_restore_end functions for null ULPI.
Forcing ehci_restart after LP0 for null ULPI.
Bug 863224
Reviewed-on: http://git-master/r/58474
(cherry picked from commit 5f5e7961c8a6a5be853fcdba4ae10d0bd6efc5f3)
Change-Id: If49f6acdcf15661f765c345431a52695bc045d29
Reviewed-on: http://git-master/r/59200
Reviewed-by: Ryan Wong <ryanw@nvidia.com>
Tested-by: Ryan Wong <ryanw@nvidia.com>
-rw-r--r-- | arch/arm/mach-tegra/include/mach/usb_phy.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 125 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 30 |
3 files changed, 125 insertions, 35 deletions
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/arch/arm/mach-tegra/include/mach/usb_phy.h index a6fa0944ce23..fa2a759230df 100644 --- a/arch/arm/mach-tegra/include/mach/usb_phy.h +++ b/arch/arm/mach-tegra/include/mach/usb_phy.h @@ -51,6 +51,8 @@ struct tegra_ulpi_config { int (*post_phy_on)(void); int (*pre_phy_off)(void); int (*post_phy_off)(void); + void (*phy_restore_start)(void); + void (*phy_restore_end)(void); }; struct tegra_uhsic_config { @@ -108,6 +110,9 @@ struct tegra_usb_phy { }; typedef int (*tegra_phy_fp)(struct tegra_usb_phy *phy, bool is_dpd); +typedef void (*tegra_phy_restore_start_fp)(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed); +typedef void (*tegra_phy_restore_end_fp)(struct tegra_usb_phy *phy); struct tegra_usb_phy *tegra_usb_phy_open(int instance, void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode, diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index e147b10d32fe..4d9765cb65ee 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -1605,29 +1605,16 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) #endif } +#ifdef CONFIG_ARCH_TEGRA_2x_SOC static void ulpi_set_tristate(bool enable) { int tristate = (enable)? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; -#ifdef CONFIG_ARCH_TEGRA_2x_SOC tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate); tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); -#else - 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_CLK, tristate); - tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate); - tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_STP, tristate); - tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_NXT, tristate); -#endif } +#endif static void ulpi_phy_reset(void __iomem *base) { @@ -1833,12 +1820,75 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) return 0; } +static void null_phy_set_tristate(bool enable) +{ + int tristate = (enable) ? TEGRA_TRI_TRISTATE : TEGRA_TRI_NORMAL; + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAA, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UAB, tristate); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_UDA, tristate); +#else + 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); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DATA0, tristate); + + if (enable) + tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, tristate); +#endif +} + +static void null_phy_restore_start(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + struct tegra_ulpi_config *config = phy->config; + + if (config->phy_restore_start) + config->phy_restore_start(); +#endif +} + +static void null_phy_restore_end(struct tegra_usb_phy *phy) +{ +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + struct tegra_ulpi_config *config = phy->config; + + if (config->phy_restore_end) + config->phy_restore_end(); + + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_CLK_PADOUT_ENA; + + udelay(500); + + /* remove ULPI tristate except DIR */ + null_phy_set_tristate(false); + udelay(20); + + /* enable ULPI CLK output pad */ + writel(val, base + ULPI_TIMING_CTRL_0); + + /* remove DIR tristate */ + tegra_pinmux_set_tristate(TEGRA_PINGROUP_ULPI_DIR, TEGRA_TRI_NORMAL); +#endif +} + static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) { const struct tegra_ulpi_trimmer default_trimmer = {0, 0, 4, 4}; unsigned long val; void __iomem *base = phy->regs; struct tegra_ulpi_config *config = phy->config; + static bool cold_boot = true; if (!config->trimmer) config->trimmer = &default_trimmer; @@ -1856,8 +1906,6 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) val |= ULPI_OUTPUT_PINMUX_BYP | ULPI_CLKOUT_PINMUX_BYP; writel(val, base + ULPI_TIMING_CTRL_0); - ulpi_set_tristate(false); - if (config->pre_phy_on && config->pre_phy_on()) return -EAGAIN; @@ -1926,9 +1974,12 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) val &= ~ULPI_PADS_RESET; writel(val, base + USB_SUSP_CTRL); #endif - val = readl(base + ULPI_TIMING_CTRL_0); - val |= ULPI_CLK_PADOUT_ENA; - writel(val, base + ULPI_TIMING_CTRL_0); + if (cold_boot) { + val = readl(base + ULPI_TIMING_CTRL_0); + val |= ULPI_CLK_PADOUT_ENA; + writel(val, base + ULPI_TIMING_CTRL_0); + cold_boot = false; + } udelay(10); @@ -1940,14 +1991,12 @@ static int null_phy_power_on(struct tegra_usb_phy *phy, bool is_dpd) static int null_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; if (config->pre_phy_off && config->pre_phy_off()) return -EAGAIN; - ulpi_set_tristate(true); + null_phy_set_tristate(true); if (config->post_phy_off && config->post_phy_off()) return -EAGAIN; @@ -1979,6 +2028,8 @@ static int null_phy_post_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd) unsigned long val; void __iomem *base = phy->regs; + ulpi_set_host(base); + /* remove slave0 reset */ val = readl(base + USB_SUSP_CTRL); val &= ~ULPIS2S_SLV0_RESET; @@ -1988,8 +2039,6 @@ static int null_phy_post_usbcmd_reset(struct tegra_usb_phy *phy, bool is_dpd) val &= ~ULPIS2S_SLV0_CLAMP_XMIT; writel(val, base + ULPIS2S_CTRL); udelay(10); - - ulpi_set_host(base); #endif return 0; } @@ -2434,18 +2483,28 @@ void tegra_ehci_post_reset(struct tegra_usb_phy *phy, bool is_dpd) void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy, enum tegra_usb_phy_port_speed port_speed) { - if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) - utmi_phy_restore_start(phy, port_speed); - else - ulpi_phy_restore_start(phy, port_speed); + const tegra_phy_restore_start_fp phy_restore_start[] = { + utmi_phy_restore_start, + ulpi_phy_restore_start, + null_phy_restore_start, + NULL, + }; + + if (phy_restore_start[phy->usb_phy_type]) + phy_restore_start[phy->usb_phy_type](phy, port_speed); } void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy) { - if (phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) - utmi_phy_restore_end(phy); - else - ulpi_phy_restore_end(phy); + const tegra_phy_restore_end_fp phy_restore_end[] = { + utmi_phy_restore_end, + ulpi_phy_restore_end, + null_phy_restore_end, + NULL, + }; + + if (phy_restore_end[phy->usb_phy_type]) + phy_restore_end[phy->usb_phy_type](phy); } void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy) diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index fcce552769ba..89b7771df901 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -437,14 +437,17 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_regs __iomem *hw = ehci->regs; unsigned long val; - int hsic = 0; + bool hsic; + bool null_ulpi; + null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI); hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); tegra_ehci_power_up(hcd, is_dpd); set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - if ((tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) || (hsic)) + if ((tegra->port_speed > TEGRA_USB_PHY_PORT_SPEED_HIGH) || (hsic) || + (null_ulpi)) goto restart; /* Force the phy to keep data lines in suspend state */ @@ -540,6 +543,29 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) return 0; restart: + if (null_ulpi) { + bool LP0 = !readl(&hw->async_next); + + if (LP0) { + static int cnt = 1; + + pr_info("LP0 restart %d\n", cnt++); + tegra_ehci_phy_restore_start(tegra->phy, + tegra->port_speed); + } + + val = readl(&hw->port_status[0]); + if (!((val & PORT_POWER) && (val & PORT_PE))) { + tegra_ehci_restart(hcd); + usb_set_device_state(udev, USB_STATE_CONFIGURED); + } + + if (LP0) + tegra_ehci_phy_restore_end(tegra->phy); + + return 0; + } + if ((tegra->port_speed <= TEGRA_USB_PHY_PORT_SPEED_HIGH) && (!hsic)) tegra_ehci_phy_restore_end(tegra->phy); if (hsic) { |