summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjoyw <joyw@nvidia.com>2013-03-19 18:20:03 +0800
committerMrutyunjay Sawant <msawant@nvidia.com>2013-03-29 06:14:29 -0700
commit096b5feb74c11a2e81dce6768e94adb9c8b748aa (patch)
treeef8292103574d85a8d1d3805367c58482b1344c6
parentae6689e99a525a7d570f685bb734c9f1e7546228 (diff)
xhci: tegra: fix race between remote wake and host wake
A race condition has been discovered during stress testing remote wake from LP0. Occasionally, remote wake doesn't get served soon enough and later hub driver tries to do host initiate resume for the port. That causes the port staying PLS=RESUME with PLC cleared. This patch fix the race by letting remote wake being completed before hub driver can resume port. Bug 1235232 Change-Id: I0595cfca6bb97df96e8b8554ece18041787cb6b9 Signed-off-by: joyw <joyw@nvidia.com> Signed-off-by: JC Kuo <jckuo@nvidia.com> Reviewed-on: http://git-master/r/210752 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Ashutosh Jha <ajha@nvidia.com> Reviewed-by: Henry Lin <henryl@nvidia.com>
-rw-r--r--drivers/usb/host/xhci-tegra.c73
1 files changed, 73 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index a71ada6f1ad3..937148ec1188 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -228,6 +228,7 @@ struct tegra_xhci_hcd {
bool usb3_rh_suspend;
bool hc_in_elpg;
+ unsigned long usb3_rh_remote_wakeup_ports; /* one bit per port */
/* firmware loading related */
struct tegra_xhci_firmware firmware;
};
@@ -1476,6 +1477,73 @@ static void ss_partition_elpg_exit_work(struct work_struct *work)
mutex_unlock(&tegra->sync_lock);
}
+/* read pmc WAKE2_STATUS register to know if SS port caused remote wake */
+static void update_remote_wakeup_ports_pmc(struct tegra_xhci_hcd *tegra)
+{
+ struct xhci_hcd *xhci = tegra->xhci;
+ u32 wake2_status;
+
+#define PMC_WAKE2_STATUS 0x168
+#define PADCTL_WAKE (1 << (58 - 32)) /* PADCTL is WAKE#58 */
+
+ wake2_status = ioread32(tegra->pmc_base + PMC_WAKE2_STATUS);
+
+ if (wake2_status & PADCTL_WAKE) {
+ /* FIXME: This is customized for Dalmore, find a generic way */
+ set_bit(0, &tegra->usb3_rh_remote_wakeup_ports);
+ /* clear wake status */
+ iowrite32(PADCTL_WAKE, tegra->pmc_base + PMC_WAKE2_STATUS);
+ }
+
+ xhci_dbg(xhci, "%s: usb3 roothub remote_wakeup_ports 0x%lx\n",
+ __func__, tegra->usb3_rh_remote_wakeup_ports);
+}
+
+static void wait_remote_wakeup_ports(struct usb_hcd *hcd)
+{
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ struct tegra_xhci_hcd *tegra = hcd_to_tegra_xhci(hcd);
+ int port, num_ports;
+ unsigned long *remote_wakeup_ports;
+ u32 portsc;
+ __le32 __iomem **port_array;
+ unsigned char *rh;
+ unsigned int retry = 64;
+
+
+ if (hcd == xhci->shared_hcd) {
+ port_array = xhci->usb3_ports;
+ num_ports = xhci->num_usb3_ports;
+ remote_wakeup_ports = &tegra->usb3_rh_remote_wakeup_ports;
+ rh = "usb3 roothub";
+ } else
+ return;
+
+ while (*remote_wakeup_ports && retry--) {
+ for_each_set_bit(port, remote_wakeup_ports, num_ports) {
+ portsc = xhci_readl(xhci, port_array[port]);
+
+ if (!(portsc & PORT_CONNECT)) {
+ /* nothing to do if already disconnected */
+ clear_bit(port, remote_wakeup_ports);
+ continue;
+ }
+
+ if ((portsc & PORT_PLS_MASK) == XDEV_U0)
+ clear_bit(port, remote_wakeup_ports);
+ else
+ xhci_dbg(xhci, "%s: %s port %d status 0x%x\n",
+ __func__, rh, port, portsc);
+ }
+
+ if (*remote_wakeup_ports)
+ msleep(20); /* give some time, irq will direct U0 */
+ }
+
+ xhci_dbg(xhci, "%s: %s remote_wakeup_ports 0x%lx\n", __func__, rh,
+ *remote_wakeup_ports);
+}
+
/* Host ELPG Exit triggered by PADCTL irq */
/**
* tegra_xhci_host_partition_elpg_exit - bring XUSBC partition out from elpg
@@ -1597,6 +1665,8 @@ tegra_xhci_host_partition_elpg_exit(struct tegra_xhci_hcd *tegra)
goto out;
}
+ update_remote_wakeup_ports_pmc(tegra);
+
if (tegra->hs_wake_event)
tegra->hs_wake_event = false;
@@ -1985,6 +2055,9 @@ static int tegra_xhci_bus_resume(struct usb_hcd *hcd)
tegra_xhci_host_partition_elpg_exit(tegra);
}
+ /* handle remote wakeup before resuming bus */
+ wait_remote_wakeup_ports(hcd);
+
err = xhci_bus_resume(hcd);
if (err) {
xhci_err(xhci, "%s: xhci_bus_resume failed %d\n",