summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVenkat Moganty <vmoganty@nvidia.com>2012-01-19 11:34:22 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 01:14:53 -0700
commitdd783f40bc7670f5bfe82fbfe3ec23d739ca8f5f (patch)
treef3817a760eafa0e5d59028d7586ad4f8d500877c
parent6510858fb4412be6de067b047120c2c692bc035d (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.c3
-rw-r--r--drivers/usb/host/ehci-tegra.c62
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c134
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);