Commit a85c0f8d authored by Mathias Nyman's avatar Mathias Nyman Committed by Greg Kroah-Hartman

xhci: rework bus_resume and check ports are suspended before resuming them.

bus_resume() tried to resume the same ports the bus_suspend()
suspeded. This caused PLC timeouts in case a suspended device disconnected
and was not in a resumable state at bus_resume().

Add a check to make sure the link state is either U3 or resuming
before actually resuming the link.

At the same time do some other changes such as make sure we remove
wake on connect/disconnect/overcurrent also for the resuming ports,
and avoid extra portsc port register writes.

This improves resume time with 10ms in those PLC timeout cases where
devices disconnect at suspend/resume cycle.
Signed-off-by: default avatarMathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 74072bae
...@@ -1521,15 +1521,14 @@ static bool xhci_port_missing_cas_quirk(int port_index, ...@@ -1521,15 +1521,14 @@ static bool xhci_port_missing_cas_quirk(int port_index,
int xhci_bus_resume(struct usb_hcd *hcd) int xhci_bus_resume(struct usb_hcd *hcd)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports, port_index;
__le32 __iomem **port_array;
struct xhci_bus_state *bus_state; struct xhci_bus_state *bus_state;
u32 temp; __le32 __iomem **port_array;
unsigned long flags; unsigned long flags;
unsigned long port_was_suspended = 0; int max_ports, port_index;
bool need_usb2_u3_exit = false;
int slot_id; int slot_id;
int sret; int sret;
u32 next_state;
u32 temp, portsc;
max_ports = xhci_get_ports(hcd, &port_array); max_ports = xhci_get_ports(hcd, &port_array);
bus_state = &xhci->bus_state[hcd_index(hcd)]; bus_state = &xhci->bus_state[hcd_index(hcd)];
...@@ -1548,68 +1547,77 @@ int xhci_bus_resume(struct usb_hcd *hcd) ...@@ -1548,68 +1547,77 @@ int xhci_bus_resume(struct usb_hcd *hcd)
temp &= ~CMD_EIE; temp &= ~CMD_EIE;
writel(temp, &xhci->op_regs->command); writel(temp, &xhci->op_regs->command);
/* bus specific resume for ports we suspended at bus_suspend */
if (hcd->speed >= HCD_USB3)
next_state = XDEV_U0;
else
next_state = XDEV_RESUME;
port_index = max_ports; port_index = max_ports;
while (port_index--) { while (port_index--) {
/* Check whether need resume ports. If needed portsc = readl(port_array[port_index]);
resume port and disable remote wakeup */
u32 temp;
temp = readl(port_array[port_index]);
/* warm reset CAS limited ports stuck in polling/compliance */ /* warm reset CAS limited ports stuck in polling/compliance */
if ((xhci->quirks & XHCI_MISSING_CAS) && if ((xhci->quirks & XHCI_MISSING_CAS) &&
(hcd->speed >= HCD_USB3) && (hcd->speed >= HCD_USB3) &&
xhci_port_missing_cas_quirk(port_index, port_array)) { xhci_port_missing_cas_quirk(port_index, port_array)) {
xhci_dbg(xhci, "reset stuck port %d\n", port_index); xhci_dbg(xhci, "reset stuck port %d\n", port_index);
clear_bit(port_index, &bus_state->bus_suspended);
continue; continue;
} }
if (DEV_SUPERSPEED_ANY(temp)) /* resume if we suspended the link, and it is still suspended */
temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); if (test_bit(port_index, &bus_state->bus_suspended))
else switch (portsc & PORT_PLS_MASK) {
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); case XDEV_U3:
if (test_bit(port_index, &bus_state->bus_suspended) && portsc = xhci_port_state_to_neutral(portsc);
(temp & PORT_PLS_MASK)) { portsc &= ~PORT_PLS_MASK;
set_bit(port_index, &port_was_suspended); portsc |= PORT_LINK_STROBE | next_state;
if (!DEV_SUPERSPEED_ANY(temp)) { break;
xhci_set_link_state(xhci, port_array, case XDEV_RESUME:
port_index, XDEV_RESUME); /* resume already initiated */
need_usb2_u3_exit = true; break;
default:
/* not in a resumeable state, ignore it */
clear_bit(port_index,
&bus_state->bus_suspended);
break;
} }
} else /* disable wake for all ports, write new link state if needed */
writel(temp, port_array[port_index]); portsc &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS);
writel(portsc, port_array[port_index]);
} }
if (need_usb2_u3_exit) { /* USB2 specific resume signaling delay and U0 link state transition */
if (hcd->speed < HCD_USB3) {
if (bus_state->bus_suspended) {
spin_unlock_irqrestore(&xhci->lock, flags); spin_unlock_irqrestore(&xhci->lock, flags);
msleep(USB_RESUME_TIMEOUT); msleep(USB_RESUME_TIMEOUT);
spin_lock_irqsave(&xhci->lock, flags); spin_lock_irqsave(&xhci->lock, flags);
} }
for_each_set_bit(port_index, &bus_state->bus_suspended,
port_index = max_ports; BITS_PER_LONG) {
while (port_index--) { /* Clear PLC to poll it later for U0 transition */
if (!(port_was_suspended & BIT(port_index))) xhci_test_and_clear_bit(xhci, port_array, port_index,
continue; PORT_PLC);
/* Clear PLC to poll it later after XDEV_U0 */ xhci_set_link_state(xhci, port_array, port_index,
xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); XDEV_U0);
xhci_set_link_state(xhci, port_array, port_index, XDEV_U0); }
} }
port_index = max_ports; /* poll for U0 link state complete, both USB2 and USB3 */
while (port_index--) { for_each_set_bit(port_index, &bus_state->bus_suspended, BITS_PER_LONG) {
if (!(port_was_suspended & BIT(port_index)))
continue;
/* Poll and Clear PLC */
sret = xhci_handshake(port_array[port_index], PORT_PLC, sret = xhci_handshake(port_array[port_index], PORT_PLC,
PORT_PLC, 10 * 1000); PORT_PLC, 10 * 1000);
if (sret) if (sret) {
xhci_warn(xhci, "port %d resume PLC timeout\n", xhci_warn(xhci, "port %d resume PLC timeout\n",
port_index); port_index);
continue;
}
xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC);
slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1);
if (slot_id) if (slot_id)
xhci_ring_device(xhci, slot_id); xhci_ring_device(xhci, slot_id);
} }
(void) readl(&xhci->op_regs->command); (void) readl(&xhci->op_regs->command);
bus_state->next_statechange = jiffies + msecs_to_jiffies(5); bus_state->next_statechange = jiffies + msecs_to_jiffies(5);
......
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