diff options
author | Venkat Moganty <vmoganty@nvidia.com> | 2012-01-19 11:34:22 +0530 |
---|---|---|
committer | Rohan Somvanshi <rsomvanshi@nvidia.com> | 2012-01-24 10:59:32 -0800 |
commit | 005f572bd229c4072cc618b74847edeba65faf33 (patch) | |
tree | fe352aa8bffa315d51d388bd3299a911c06d5644 | |
parent | 65ab9647d93da8dce3ea14922a6095bbcf64efcd (diff) |
tegra: usb: host: Fix remote wakeup issues on UTMI
Add WAR to fix 2LS voilation during usb remote resume.
Bug 880538
Reviewed-on: http://git-master/r/75845
Change-Id: I552c9e657776f67c263ef750a7786c796dc785cb
Signed-off-by: Venkat Moganty <vmoganty@nvidia.com>
Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Reviewed-on: http://git-master/r/76822
Reviewed-by: Automatic_Commit_Validation_User
-rw-r--r-- | arch/arm/mach-tegra/usb_phy.c | 134 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 3 | ||||
-rw-r--r-- | drivers/usb/host/ehci-tegra.c | 62 | ||||
-rw-r--r-- | drivers/usb/host/ehci.h | 1 |
4 files changed, 147 insertions, 53 deletions
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c index 5eab7ccd3dc8..8820a2626282 100644 --- a/arch/arm/mach-tegra/usb_phy.c +++ b/arch/arm/mach-tegra/usb_phy.c @@ -1414,27 +1414,45 @@ static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy) #endif } -static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +static void utmi_phy_enable_obs_bus(struct tegra_usb_phy *phy, + enum tegra_usb_phy_port_speed port_speed) { -#ifdef CONFIG_ARCH_TEGRA_2x_SOC unsigned long val; void __iomem *base = phy->regs; - val = readl(base + UTMIP_TX_CFG0); - val |= UTMIP_HS_DISCON_DISABLE; - writel(val, base + UTMIP_TX_CFG0); -#else - utmip_phy_disable_pmc_bus_ctrl(phy); -#endif - return 0; + /* (2LS WAR)is not required for LS and FS devices and is only for HS */ + if (port_speed != TEGRA_USB_PHY_PORT_SPEED_HIGH) { + /* do not enable the OBS bus */ + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + writel(val, base + UTMIP_MISC_CFG0); + return; + } + /* Force DP/DM pulldown active for Host mode */ + val = readl(base + UTMIP_MISC_CFG0); + val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP | + COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS; + writel(val, base + UTMIP_MISC_CFG0); + val = readl(base + UTMIP_MISC_CFG0); + val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); + if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) + val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; + else + val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; + writel(val, base + UTMIP_MISC_CFG0); + udelay(1); + + val = readl(base + UTMIP_MISC_CFG0); + val |= UTMIP_DPDM_OBSERVE; + writel(val, base + UTMIP_MISC_CFG0); + udelay(10); } -static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) +static void utmi_phy_disable_obs_bus(struct tegra_usb_phy *phy) { unsigned long val; void __iomem *base = phy->regs; -#ifndef CONFIG_ARCH_TEGRA_2x_SOC /* check if OBS bus is already enabled */ val = readl(base + UTMIP_MISC_CFG0); if (val & UTMIP_DPDM_OBSERVE) { @@ -1458,11 +1476,65 @@ static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS); writel(val, base + UTMIP_MISC_CFG0); } +} + + +static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup) +{ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + unsigned long val; + void __iomem *base = phy->regs; + enum tegra_usb_phy_port_speed port_speed; + + val = readl(base + UTMIP_TX_CFG0); + val |= UTMIP_HS_DISCON_DISABLE; + writel(val, base + UTMIP_TX_CFG0); + + port_speed = (readl(base + USB_PORTSC1) >> 26) & 0x3; + utmi_phy_enable_obs_bus(phy, port_speed); + +#else + unsigned long val; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + unsigned int inst = phy->instance; + void __iomem *base = phy->regs; + enum tegra_usb_phy_port_speed port_speed; + + val = readl(pmc_base + PMC_SLEEP_CFG); + if (val & UTMIP_MASTER_ENABLE(inst)) { + if (!remote_wakeup) + utmip_phy_disable_pmc_bus_ctrl(phy); + } else { + port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) & + HOSTPC1_DEVLC_PSPD_MASK; + utmi_phy_enable_obs_bus(phy, port_speed); + } +#endif + + return 0; +} + +static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd) +{ + unsigned long val; + void __iomem *base = phy->regs; + void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + unsigned int inst = phy->instance; + +#ifndef CONFIG_ARCH_TEGRA_2x_SOC + val = readl(pmc_base + PMC_SLEEP_CFG); + /* if PMC is not disabled by now then disable it */ + if (val & UTMIP_MASTER_ENABLE(inst)) { + utmip_phy_disable_pmc_bus_ctrl(phy); + } #else val = readl(base + UTMIP_TX_CFG0); val &= ~UTMIP_HS_DISCON_DISABLE; writel(val, base + UTMIP_TX_CFG0); #endif + + utmi_phy_disable_obs_bus(phy); + return 0; } @@ -1476,7 +1548,7 @@ static int uhsic_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd) return 0; } -static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup) { struct tegra_uhsic_config *uhsic_config = phy->config; @@ -1524,7 +1596,6 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, udelay(10); #else unsigned long val; - void __iomem *base = phy->regs; void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); int inst = phy->instance; @@ -1538,34 +1609,7 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy, utmip_phy_disable_pmc_bus_ctrl(phy); } } - - /* (2LS WAR)is not required for LS and FS devices and is only for HS */ - if ((port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) || - (port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)) { - /* do not enable the OBS bus */ - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); - writel(val, base + UTMIP_MISC_CFG0); - return; - } - /* Force DP/DM pulldown active for Host mode */ - val = readl(base + UTMIP_MISC_CFG0); - val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP | - COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS; - writel(val, base + UTMIP_MISC_CFG0); - val = readl(base + UTMIP_MISC_CFG0); - val &= ~UTMIP_DPDM_OBSERVE_SEL(~0); - if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) - val |= UTMIP_DPDM_OBSERVE_SEL_FS_J; - else - val |= UTMIP_DPDM_OBSERVE_SEL_FS_K; - writel(val, base + UTMIP_MISC_CFG0); - udelay(1); - - val = readl(base + UTMIP_MISC_CFG0); - val |= UTMIP_DPDM_OBSERVE; - writel(val, base + UTMIP_MISC_CFG0); - udelay(10); + utmi_phy_enable_obs_bus(phy, port_speed); #endif } @@ -1597,10 +1641,10 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy) } wait_time_us--; } while (!(val & USB_PORTSC1_RESUME)); - /* disable PMC master control */ - utmip_phy_disable_pmc_bus_ctrl(phy); /* wait for 25 ms to port resume complete */ msleep(25); + /* disable PMC master control */ + utmip_phy_disable_pmc_bus_ctrl(phy); /* Clear PCI and SRI bits to avoid an interrupt upon resume */ val = readl(base + USB_USBSTS); @@ -2481,7 +2525,7 @@ void tegra_usb_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd) phy->power_on = false; } -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup) { const tegra_phy_fp preresume[] = { utmi_phy_preresume, @@ -2491,7 +2535,7 @@ void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd) }; if (preresume[phy->usb_phy_type]) - preresume[phy->usb_phy_type](phy, is_dpd); + preresume[phy->usb_phy_type](phy, remote_wakeup); } void tegra_usb_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index ab94b512e0ea..1a534c70ea6e 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -906,6 +906,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci->reset_done[i] = jiffies + msecs_to_jiffies(25); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); mod_timer(&hcd->rh_timer, ehci->reset_done[i]); +#ifdef CONFIG_USB_EHCI_TEGRA + ehci->controller_remote_wakeup = true; +#endif } } diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index 917796c20d8a..1dbfc4aa1981 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -167,11 +167,14 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) struct ehci_regs __iomem *hw = ehci->regs; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); u32 val; + irqreturn_t irq_status; + bool pmc_remote_wakeup = false; if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && (tegra->ehci->has_hostpc)) { /* check if there is any remote wake event */ if (tegra_usb_phy_is_remotewake_detected(tegra->phy)) { + pmc_remote_wakeup = true; spin_lock (&ehci->lock); usb_hcd_resume_root_hub(hcd); spin_unlock (&ehci->lock); @@ -196,7 +199,21 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd) } spin_unlock(&ehci->lock); } - return ehci_irq(hcd); + + irq_status = ehci_irq(hcd); + + if (pmc_remote_wakeup) { + ehci->controller_remote_wakeup = false; + } + + if (ehci->controller_remote_wakeup) { + ehci->controller_remote_wakeup = false; + /* disable interrupts */ + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + tegra_usb_phy_preresume(tegra->phy, true); + tegra->port_resuming = 1; + } + return irq_status; } static int tegra_ehci_hub_control( @@ -219,6 +236,7 @@ static int tegra_ehci_hub_control( unsigned selector; struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller); bool hsic = false; + bool do_post_resume = false; if (!tegra->host_resumed) { if (buf) @@ -244,13 +262,18 @@ static int tegra_ehci_hub_control( } else if (typeReq == GetPortStatus) { temp = ehci_readl(ehci, status_reg); if (tegra->port_resuming && !(temp & PORT_SUSPEND) && - time_after_eq(jiffies, ehci->reset_done[wIndex-1])) { - /* Resume completed, re-enable disconnect detection */ - tegra->port_resuming = 0; + time_after_eq(jiffies, ehci->reset_done[wIndex-1])) { clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); ehci->reset_done[wIndex-1] = 0; + do_post_resume = true; + } else if (tegra->port_resuming && (temp & PORT_RESUME) && + time_after_eq(jiffies, ehci->reset_done[wIndex-1]) ) { + do_post_resume = true; + } + + if (do_post_resume) { + tegra->port_resuming = 0; tegra_usb_phy_postresume(tegra->phy, false); -#ifndef CONFIG_ARCH_TEGRA_2x_SOC if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { ehci->command |= CMD_RUN; /* @@ -259,9 +282,11 @@ static int tegra_ehci_hub_control( */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + /* Now we can safely re-enable irqs */ + ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable); } -#endif } + } else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) { temp = ehci_readl(ehci, status_reg); if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) { @@ -286,6 +311,13 @@ static int tegra_ehci_hub_control( set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports); + if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { + /* Disable RUN bit. */ + ehci->command &= ~CMD_RUN; + ehci_writel(ehci, ehci->command, + &ehci->regs->command); + } + tegra_usb_phy_postsuspend(tegra->phy, false); goto done; @@ -318,6 +350,15 @@ static int tegra_ehci_hub_control( tegra->port_resuming = 1; + if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) { + /* disable interrupts */ + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + /* Disable RUN bit. */ + ehci->command &= ~CMD_RUN; + ehci_writel(ehci, ehci->command, + &ehci->regs->command); + } + /* Disable disconnect detection during port resume */ tegra_usb_phy_preresume(tegra->phy, false); #ifndef CONFIG_ARCH_TEGRA_2x_SOC @@ -509,6 +550,7 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) unsigned long val; bool hsic; bool null_ulpi; + bool utmip_remote_wakeup = false; null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI); hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC); @@ -547,8 +589,7 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) && (tegra->ehci->has_hostpc) && (tegra->phy->remote_wakeup)) { - ehci->command |= CMD_RUN; - ehci_writel(ehci, ehci->command, &ehci->regs->command); + utmip_remote_wakeup = true; } /* Check if the phy resume from LP0. When the phy resume from LP0 @@ -611,6 +652,10 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd) } tegra_ehci_phy_restore_end(tegra->phy); + if (utmip_remote_wakeup) { + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + } return 0; restart: @@ -709,6 +754,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) return retval; ehci->sbrn = 0x20; + ehci->controller_remote_wakeup = false; if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { tegra_ehci_pre_reset(tegra->phy, false); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 7f0e828631ed..e897262c6a60 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -142,6 +142,7 @@ struct ehci_hcd { /* one per controller */ unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */ #ifdef CONFIG_USB_EHCI_TEGRA unsigned controller_resets_phy:1; + unsigned controller_remote_wakeup:1; #endif /* required for usb32 quirk */ |