Commit eafe5b99 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: EHCI: fix remote-wakeup support for ARC/TDI core

This patch (as1147) fixes the remote-wakeup support for EHCI
controllers using the ARC/TDI "embedded-TT" core.  These controllers
turn off the RESUME bit by themselves when a port resume is complete;
hence we need to keep separate track of which ports are suspended or
in the process of resuming.

The patch also makes a couple of small improvements in ehci_irq(),
replacing reads of the command register with the value already stored
in a local variable.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Tested-by: default avatarThomas Reitmayr <treitmayr@devbase.at>
CC: David Brownell <david-b@pacbell.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 2da41d5f
...@@ -706,7 +706,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -706,7 +706,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
pcd_status = status; pcd_status = status;
/* resume root hub? */ /* resume root hub? */
if (!(ehci_readl(ehci, &ehci->regs->command) & CMD_RUN)) if (!(cmd & CMD_RUN))
usb_hcd_resume_root_hub(hcd); usb_hcd_resume_root_hub(hcd);
while (i--) { while (i--) {
...@@ -715,8 +715,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -715,8 +715,11 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
if (pstatus & PORT_OWNER) if (pstatus & PORT_OWNER)
continue; continue;
if (!(pstatus & PORT_RESUME) if (!(test_bit(i, &ehci->suspended_ports) &&
|| ehci->reset_done [i] != 0) ((pstatus & PORT_RESUME) ||
!(pstatus & PORT_SUSPEND)) &&
(pstatus & PORT_PE) &&
ehci->reset_done[i] == 0))
continue; continue;
/* start 20 msec resume signaling from this port, /* start 20 msec resume signaling from this port,
...@@ -731,9 +734,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) ...@@ -731,9 +734,8 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
/* PCI errors [4.15.2.4] */ /* PCI errors [4.15.2.4] */
if (unlikely ((status & STS_FATAL) != 0)) { if (unlikely ((status & STS_FATAL) != 0)) {
dbg_cmd (ehci, "fatal", ehci_readl(ehci, dbg_cmd(ehci, "fatal", cmd);
&ehci->regs->command)); dbg_status(ehci, "fatal", status);
dbg_status (ehci, "fatal", status);
if (status & STS_HALT) { if (status & STS_HALT) {
ehci_err (ehci, "fatal error\n"); ehci_err (ehci, "fatal error\n");
dead: dead:
......
...@@ -236,10 +236,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd) ...@@ -236,10 +236,8 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
temp = ehci_readl(ehci, &ehci->regs->port_status [i]); temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
if (test_bit(i, &ehci->bus_suspended) && if (test_bit(i, &ehci->bus_suspended) &&
(temp & PORT_SUSPEND)) { (temp & PORT_SUSPEND))
ehci->reset_done [i] = jiffies + msecs_to_jiffies (20);
temp |= PORT_RESUME; temp |= PORT_RESUME;
}
ehci_writel(ehci, temp, &ehci->regs->port_status [i]); ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
} }
i = HCS_N_PORTS (ehci->hcs_params); i = HCS_N_PORTS (ehci->hcs_params);
...@@ -482,10 +480,9 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf) ...@@ -482,10 +480,9 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
* controller by the user. * controller by the user.
*/ */
if ((temp & mask) != 0 if ((temp & mask) != 0 || test_bit(i, &ehci->port_c_suspend)
|| ((temp & PORT_RESUME) != 0 || (ehci->reset_done[i] && time_after_eq(
&& time_after_eq(jiffies, jiffies, ehci->reset_done[i]))) {
ehci->reset_done[i]))) {
if (i < 7) if (i < 7)
buf [0] |= 1 << (i + 1); buf [0] |= 1 << (i + 1);
else else
...@@ -688,6 +685,7 @@ static int ehci_hub_control ( ...@@ -688,6 +685,7 @@ static int ehci_hub_control (
/* resume completed? */ /* resume completed? */
else if (time_after_eq(jiffies, else if (time_after_eq(jiffies,
ehci->reset_done[wIndex])) { ehci->reset_done[wIndex])) {
clear_bit(wIndex, &ehci->suspended_ports);
set_bit(wIndex, &ehci->port_c_suspend); set_bit(wIndex, &ehci->port_c_suspend);
ehci->reset_done[wIndex] = 0; ehci->reset_done[wIndex] = 0;
...@@ -734,6 +732,9 @@ static int ehci_hub_control ( ...@@ -734,6 +732,9 @@ static int ehci_hub_control (
ehci_readl(ehci, status_reg)); ehci_readl(ehci, status_reg));
} }
if (!(temp & (PORT_RESUME|PORT_RESET)))
ehci->reset_done[wIndex] = 0;
/* transfer dedicated ports to the companion hc */ /* transfer dedicated ports to the companion hc */
if ((temp & PORT_CONNECT) && if ((temp & PORT_CONNECT) &&
test_bit(wIndex, &ehci->companion_ports)) { test_bit(wIndex, &ehci->companion_ports)) {
...@@ -757,8 +758,17 @@ static int ehci_hub_control ( ...@@ -757,8 +758,17 @@ static int ehci_hub_control (
} }
if (temp & PORT_PE) if (temp & PORT_PE)
status |= 1 << USB_PORT_FEAT_ENABLE; status |= 1 << USB_PORT_FEAT_ENABLE;
if (temp & (PORT_SUSPEND|PORT_RESUME))
/* maybe the port was unsuspended without our knowledge */
if (temp & (PORT_SUSPEND|PORT_RESUME)) {
status |= 1 << USB_PORT_FEAT_SUSPEND; status |= 1 << USB_PORT_FEAT_SUSPEND;
} else if (test_bit(wIndex, &ehci->suspended_ports)) {
clear_bit(wIndex, &ehci->suspended_ports);
ehci->reset_done[wIndex] = 0;
if (temp & PORT_PE)
set_bit(wIndex, &ehci->port_c_suspend);
}
if (temp & PORT_OC) if (temp & PORT_OC)
status |= 1 << USB_PORT_FEAT_OVER_CURRENT; status |= 1 << USB_PORT_FEAT_OVER_CURRENT;
if (temp & PORT_RESET) if (temp & PORT_RESET)
...@@ -803,6 +813,7 @@ static int ehci_hub_control ( ...@@ -803,6 +813,7 @@ static int ehci_hub_control (
|| (temp & PORT_RESET) != 0) || (temp & PORT_RESET) != 0)
goto error; goto error;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg); ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
set_bit(wIndex, &ehci->suspended_ports);
break; break;
case USB_PORT_FEAT_POWER: case USB_PORT_FEAT_POWER:
if (HCS_PPC (ehci->hcs_params)) if (HCS_PPC (ehci->hcs_params))
......
...@@ -99,6 +99,8 @@ struct ehci_hcd { /* one per controller */ ...@@ -99,6 +99,8 @@ struct ehci_hcd { /* one per controller */
owned by the companion during a bus suspend */ owned by the companion during a bus suspend */
unsigned long port_c_suspend; /* which ports have unsigned long port_c_suspend; /* which ports have
the change-suspend feature turned on */ the change-suspend feature turned on */
unsigned long suspended_ports; /* which ports are
suspended */
/* per-HC memory pools (could be per-bus, but ...) */ /* per-HC memory pools (could be per-bus, but ...) */
struct dma_pool *qh_pool; /* qh per active urb */ struct dma_pool *qh_pool; /* qh per active urb */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment