summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorRakesh Bodla <rbodla@nvidia.com>2012-04-03 12:20:09 +0530
committerRohan Somvanshi <rsomvanshi@nvidia.com>2012-04-08 22:55:18 -0700
commit56a89c4c35bcb582ab8f30fdd5e17b6f99f5a951 (patch)
tree9e59e556a27ec92b0c262693ba79ac717cbb2a6e /drivers
parentedfea48c58977109336ba88464531d86909b13fc (diff)
usb: ehci: tegra: Fix tegra utmip issues
Following tegra USB UTMIP issues are fixed: 1. Clear run bit directly in the command register instead of updating the shadow variable. 2. Reset EHCI while resuming from LP0 for tegra 2. 3. Wait for 25ms to ensure port is resumed. Bug 912880 Reviewed-on: http://git-master/r/92565 (cherry picked from commit 928ad32858af191fb9d90d736b910499121e10df) Change-Id: I676f7f23fd8833a179e1670e6aed28a01baaf15b Reviewed-on: http://git-master/r/94829 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Rakesh Bodla <rbodla@nvidia.com> Tested-by: Rakesh Bodla <rbodla@nvidia.com> Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/host/ehci-tegra.c64
1 files changed, 41 insertions, 23 deletions
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index c6ddcab46404..bb86dfa47e7d 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -237,7 +237,7 @@ static int tegra_ehci_hub_control(
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int ports = HCS_N_PORTS(ehci->hcs_params);
- u32 temp, status;
+ u32 temp, status, cmd_run;
u32 __iomem *status_reg;
u32 usbsts_reg;
@@ -246,7 +246,6 @@ 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;
mutex_lock(&tegra->tegra_ehci_hcd_mutex);
if (!tegra->host_resumed) {
@@ -273,27 +272,29 @@ static int tegra_ehci_hub_control(
goto done;
} 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])) {
- 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) {
+ /* check port is in resume state */
+ if (tegra->port_resuming) {
+ int delay = ehci->reset_done[wIndex-1] - jiffies;
+ /* Sometimes it seems we get called too soon... In that case, wait.*/
+ if (delay > 0) {
+ ehci_dbg(ehci, "GetPortStatus called too soon, waiting %dms...\n", delay);
+ mdelay(jiffies_to_msecs(delay));
+ }
+ /* Ensure the port PORT_SUSPEND and PORT_RESUME has cleared */
+ if (handshake(ehci, status_reg, (PORT_SUSPEND | PORT_RESUME), 0, 25000)) {
+ pr_err("%s: timeout waiting for SUSPEND to clear\n", __func__);
+ }
tegra->port_resuming = 0;
tegra_usb_phy_postresume(tegra->phy, false);
if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
ehci->command |= CMD_RUN;
+ cmd_run = ehci_readl(ehci, &ehci->regs->command);
+ cmd_run |= CMD_RUN;
/*
* ehci run bit is disabled to avoid SOF.
* 2LS WAR is executed by now enable the run bit.
*/
- ehci_writel(ehci, ehci->command,
- &ehci->regs->command);
+ ehci_writel(ehci, cmd_run, &ehci->regs->command);
/* Now we can safely re-enable irqs */
ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
}
@@ -322,14 +323,23 @@ static int tegra_ehci_hub_control(
pr_err("%s: timeout waiting for SUSPEND\n", __func__);
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
-
+ /*
+ * If RUN bit is disabled interrupt is not generated after suspend.
+ * This change on T20 will allow ASE interrupt generated after suspend
+ * which will unlink the qheads.
+ */
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
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);
+ cmd_run = ehci_readl(ehci, &ehci->regs->command);
+ cmd_run &= ~CMD_RUN;
+ ehci_writel(ehci, cmd_run, &ehci->regs->command);
+ if (handshake (ehci, &ehci->regs->status,
+ STS_HALT, STS_HALT, 16 * 125))
+ pr_err("%s() timeout waiting for STS_HALT\n", __func__);
}
-
+#endif
tegra_usb_phy_postsuspend(tegra->phy, false);
goto done;
@@ -368,8 +378,12 @@ static int tegra_ehci_hub_control(
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
/* Disable RUN bit. */
ehci->command &= ~CMD_RUN;
- ehci_writel(ehci, ehci->command,
- &ehci->regs->command);
+ cmd_run = ehci_readl(ehci, &ehci->regs->command);
+ cmd_run &= ~CMD_RUN;
+ ehci_writel(ehci, cmd_run, &ehci->regs->command);
+ if (handshake (ehci, &ehci->regs->status,
+ STS_HALT, STS_HALT, 16 * 125))
+ pr_err("%s() timeout waiting for STS_HALT\n", __func__);
}
/* Disable disconnect detection during port resume */
@@ -585,8 +599,7 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd)
/* Force the phy to keep data lines in suspend state */
tegra_ehci_phy_restore_start(tegra->phy, tegra->port_speed);
- if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) &&
- (tegra->ehci->has_hostpc)) {
+ if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
ehci_reset(ehci);
}
@@ -615,6 +628,11 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd)
/* Check if the phy resume from LP0. When the phy resume from LP0
* USB register will be reset. */
if (!readl(&hw->async_next)) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* Start the controller */
+ val = readl(&hw->command);
+ writel((val | CMD_RUN), &hw->command);
+#endif
/* Program the field PTC based on the saved speed mode */
val = readl(&hw->port_status[0]);
val &= ~PORT_TEST(~0);