|
@@ -1474,15 +1474,18 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|
|
unsigned long flags;
|
|
|
struct xhci_hub *rhub;
|
|
|
struct xhci_port **ports;
|
|
|
+ u32 portsc_buf[USB_MAXCHILDREN];
|
|
|
+ bool wake_enabled;
|
|
|
|
|
|
rhub = xhci_get_rhub(hcd);
|
|
|
ports = rhub->ports;
|
|
|
max_ports = rhub->num_ports;
|
|
|
bus_state = &xhci->bus_state[hcd_index(hcd)];
|
|
|
+ wake_enabled = hcd->self.root_hub->do_remote_wakeup;
|
|
|
|
|
|
spin_lock_irqsave(&xhci->lock, flags);
|
|
|
|
|
|
- if (hcd->self.root_hub->do_remote_wakeup) {
|
|
|
+ if (wake_enabled) {
|
|
|
if (bus_state->resuming_ports || /* USB2 */
|
|
|
bus_state->port_remote_wakeup) { /* USB3 */
|
|
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
|
@@ -1490,26 +1493,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- port_index = max_ports;
|
|
|
+ /*
|
|
|
+ * Prepare ports for suspend, but don't write anything before all ports
|
|
|
+ * are checked and we know bus suspend can proceed
|
|
|
+ */
|
|
|
bus_state->bus_suspended = 0;
|
|
|
+ port_index = max_ports;
|
|
|
while (port_index--) {
|
|
|
- /* suspend the port if the port is not suspended */
|
|
|
u32 t1, t2;
|
|
|
- int slot_id;
|
|
|
|
|
|
t1 = readl(ports[port_index]->addr);
|
|
|
t2 = xhci_port_state_to_neutral(t1);
|
|
|
+ portsc_buf[port_index] = 0;
|
|
|
|
|
|
- if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) {
|
|
|
- xhci_dbg(xhci, "port %d not suspended\n", port_index);
|
|
|
- slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
|
|
- port_index + 1);
|
|
|
- if (slot_id) {
|
|
|
+ /* Bail out if a USB3 port has a new device in link training */
|
|
|
+ if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) {
|
|
|
+ bus_state->bus_suspended = 0;
|
|
|
+ spin_unlock_irqrestore(&xhci->lock, flags);
|
|
|
+ xhci_dbg(xhci, "Bus suspend bailout, port in polling\n");
|
|
|
+ return -EBUSY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* suspend ports in U0, or bail out for new connect changes */
|
|
|
+ if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
|
|
|
+ if ((t1 & PORT_CSC) && wake_enabled) {
|
|
|
+ bus_state->bus_suspended = 0;
|
|
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
|
|
- xhci_stop_device(xhci, slot_id, 1);
|
|
|
- spin_lock_irqsave(&xhci->lock, flags);
|
|
|
+ xhci_dbg(xhci, "Bus suspend bailout, port connect change\n");
|
|
|
+ return -EBUSY;
|
|
|
}
|
|
|
+ xhci_dbg(xhci, "port %d not suspended\n", port_index);
|
|
|
t2 &= ~PORT_PLS_MASK;
|
|
|
t2 |= PORT_LINK_STROBE | XDEV_U3;
|
|
|
set_bit(port_index, &bus_state->bus_suspended);
|
|
@@ -1518,7 +1531,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|
|
* including the USB 3.0 roothub, but only if CONFIG_PM
|
|
|
* is enabled, so also enable remote wake here.
|
|
|
*/
|
|
|
- if (hcd->self.root_hub->do_remote_wakeup) {
|
|
|
+ if (wake_enabled) {
|
|
|
if (t1 & PORT_CONNECT) {
|
|
|
t2 |= PORT_WKOC_E | PORT_WKDISC_E;
|
|
|
t2 &= ~PORT_WKCONN_E;
|
|
@@ -1538,7 +1551,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
|
|
|
|
|
|
t1 = xhci_port_state_to_neutral(t1);
|
|
|
if (t1 != t2)
|
|
|
- writel(t2, ports[port_index]->addr);
|
|
|
+ portsc_buf[port_index] = t2;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* write port settings, stopping and suspending ports if needed */
|
|
|
+ port_index = max_ports;
|
|
|
+ while (port_index--) {
|
|
|
+ if (!portsc_buf[port_index])
|
|
|
+ continue;
|
|
|
+ if (test_bit(port_index, &bus_state->bus_suspended)) {
|
|
|
+ int slot_id;
|
|
|
+
|
|
|
+ slot_id = xhci_find_slot_id_by_port(hcd, xhci,
|
|
|
+ port_index + 1);
|
|
|
+ if (slot_id) {
|
|
|
+ spin_unlock_irqrestore(&xhci->lock, flags);
|
|
|
+ xhci_stop_device(xhci, slot_id, 1);
|
|
|
+ spin_lock_irqsave(&xhci->lock, flags);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ writel(portsc_buf[port_index], ports[port_index]->addr);
|
|
|
}
|
|
|
hcd->state = HC_STATE_SUSPENDED;
|
|
|
bus_state->next_statechange = jiffies + msecs_to_jiffies(10);
|