USB/xhci: Enable remote wakeup for USB3 devices.
[firefly-linux-kernel-4.4.55.git] / drivers / usb / host / xhci-ring.c
index b62037bff688c07c38f0ad06e22ab8cb964bf62f..ffe549338cec6ec4a9dcbb7ee92be08252d24c97 100644 (file)
@@ -1237,6 +1237,24 @@ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd,
        return num_similar_speed_ports;
 }
 
+static void handle_device_notification(struct xhci_hcd *xhci,
+               union xhci_trb *event)
+{
+       u32 slot_id;
+
+       slot_id = TRB_TO_SLOT_ID(event->generic.field[3]);
+       if (!xhci->devs[slot_id])
+               xhci_warn(xhci, "Device Notification event for "
+                               "unused slot %u\n", slot_id);
+       else
+               xhci_dbg(xhci, "Device Notification event for slot ID %u\n",
+                               slot_id);
+       /* XXX should we kick khubd for the parent hub?  It should have send an
+        * interrupt transfer when the port started signaling resume, so there's
+        * probably no need to do so.
+        */
+}
+
 static void handle_port_status(struct xhci_hcd *xhci,
                union xhci_trb *event)
 {
@@ -1321,20 +1339,16 @@ static void handle_port_status(struct xhci_hcd *xhci,
                }
 
                if (DEV_SUPERSPEED(temp)) {
-                       xhci_dbg(xhci, "resume SS port %d\n", port_id);
+                       xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
+                       xhci_test_and_clear_bit(xhci, port_array,
+                                       faked_port_index, PORT_PLC);
                        xhci_set_link_state(xhci, port_array, faked_port_index,
                                                XDEV_U0);
-                       slot_id = xhci_find_slot_id_by_port(hcd, xhci,
-                                       faked_port_index + 1);
-                       if (!slot_id) {
-                               xhci_dbg(xhci, "slot_id is zero\n");
-                               goto cleanup;
-                       }
-                       xhci_ring_device(xhci, slot_id);
-                       xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
-                       /* Clear PORT_PLC */
-                       xhci_test_and_clear_bit(xhci, port_array,
-                                               faked_port_index, PORT_PLC);
+                       /* Need to wait until the next link state change
+                        * indicates the device is actually in U0.
+                        */
+                       bogus_port_status = true;
+                       goto cleanup;
                } else {
                        xhci_dbg(xhci, "resume HS port %d\n", port_id);
                        bus_state->resume_done[faked_port_index] = jiffies +
@@ -1345,6 +1359,15 @@ static void handle_port_status(struct xhci_hcd *xhci,
                }
        }
 
+       if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
+                       DEV_SUPERSPEED(temp)) {
+               xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
+               slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+                               faked_port_index + 1);
+               if (slot_id && xhci->devs[slot_id])
+                       xhci_ring_device(xhci, slot_id);
+       }
+
        if (hcd->speed != HCD_USB3)
                xhci_test_and_clear_bit(xhci, port_array, faked_port_index,
                                        PORT_PLC);
@@ -2277,6 +2300,9 @@ static int xhci_handle_event(struct xhci_hcd *xhci)
                else
                        update_ptrs = 0;
                break;
+       case TRB_TYPE(TRB_DEV_NOTE):
+               handle_device_notification(xhci, event);
+               break;
        default:
                if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >=
                    TRB_TYPE(48))