diff options
author | Venkat Moganty <vmoganty@nvidia.com> | 2012-01-19 11:34:22 +0530 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2013-09-14 01:14:53 -0700 |
commit | dd783f40bc7670f5bfe82fbfe3ec23d739ca8f5f (patch) | |
tree | f3817a760eafa0e5d59028d7586ad4f8d500877c | |
parent | 6510858fb4412be6de067b047120c2c692bc035d (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
Rebase-Id: Rdf14f3c5cc7db4c5282e560cc7a7ff75464eaf61
-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 | ||||
-rw-r--r-- | drivers/usb/phy/phy-tegra-usb.c | 134 |
4 files changed, 147 insertions, 53 deletions
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 72f51ed038bb..0c3edf6bfab3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -804,6 +804,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); usb_hcd_start_port_resume(&hcd->self, i); 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 ce24208356cc..508d3e555add 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -173,11 +173,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); @@ -202,7 +205,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( @@ -225,6 +242,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) @@ -241,14 +259,19 @@ static int tegra_ehci_hub_control( 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) { clear_bit(wIndex-1, &ehci->resuming_ports); + 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; /* @@ -257,9 +280,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) { @@ -284,6 +309,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; @@ -316,6 +348,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 @@ -503,6 +544,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) if (retval) return retval; + ehci->controller_remote_wakeup = false; tegra_ehci_post_reset(tegra->phy, false); if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) { @@ -816,6 +858,7 @@ static int controller_resume(struct device *dev) 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); @@ -854,8 +897,7 @@ static int controller_resume(struct device *dev) 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 @@ -918,6 +960,10 @@ static int controller_resume(struct device *dev) } tegra_ehci_phy_restore_end(tegra->phy); + if (utmip_remote_wakeup) { + ehci->command |= CMD_RUN; + ehci_writel(ehci, ehci->command, &ehci->regs->command); + } goto done; restart: diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index a7393a9c4a42..3b1cc47e3a0c 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -202,6 +202,7 @@ struct ehci_hcd { /* one per controller */ unsigned need_oc_pp_cycle:1; /* MPC834X port power */ #ifdef CONFIG_USB_EHCI_TEGRA unsigned controller_resets_phy:1; + unsigned controller_remote_wakeup:1; #endif /* required for usb32 quirk */ diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 4a9400b4c375..26f53d1f3e7b 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1415,27 +1415,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) { @@ -1459,11 +1477,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; } @@ -1477,7 +1549,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; @@ -1525,7 +1597,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; @@ -1539,34 +1610,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 } @@ -1598,10 +1642,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); @@ -2541,7 +2585,7 @@ err0: } EXPORT_SYMBOL_GPL(tegra_usb_phy_open); -void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) +void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup) { const tegra_phy_fp preresume[] = { utmi_phy_preresume, @@ -2551,7 +2595,7 @@ void tegra_usb_phy_preresume(struct tegra_usb_phy *phy) }; if (preresume[phy->usb_phy_type]) - preresume[phy->usb_phy_type](phy, is_dpd); + preresume[phy->usb_phy_type](phy, remote_wakeup); } EXPORT_SYMBOL_GPL(tegra_usb_phy_preresume); |