diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 2d773cbe191cde5c1d3c3f5d68a9f28ed27a1e3a..50411cd0ac48a0dde8bc32345ea23c1d09b3b568 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2421,11 +2421,26 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) * we don't explicitly enable it here. */ if (udev->do_remote_wakeup) { - status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, 0, - NULL, 0, - USB_CTRL_SET_TIMEOUT); + if (!hub_is_superspeed(hub->hdev)) { + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, 0, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } else { + /* Assume there's only one function on the USB 3.0 + * device and enable remote wake for the first + * interface. FIXME if the interface association + * descriptor shows there's more than one function. + */ + status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, + USB_RECIP_INTERFACE, + USB_INTRF_FUNC_SUSPEND, + USB_INTRF_FUNC_SUSPEND_RW, + NULL, 0, + USB_CTRL_SET_TIMEOUT); + } if (status) { dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", status); diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 36cbe2226a44e46b3acd6c8269ede7ab57fc03ad..6b70e7fb484c4c4ce33aace27b842f73dc097eab 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -2141,7 +2141,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) unsigned int val, val2; u64 val_64; struct xhci_segment *seg; - u32 page_size; + u32 page_size, temp; int i; page_size = xhci_readl(xhci, &xhci->op_regs->page_size); @@ -2324,6 +2324,15 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) INIT_LIST_HEAD(&xhci->lpm_failed_devs); + /* Enable USB 3.0 device notifications for function remote wake, which + * is necessary for allowing USB 3.0 devices to do remote wakeup from + * U3 (device suspend). + */ + temp = xhci_readl(xhci, &xhci->op_regs->dev_notification); + temp &= ~DEV_NOTE_MASK; + temp |= DEV_NOTE_FWAKE; + xhci_writel(xhci, temp, &xhci->op_regs->dev_notification); + return 0; fail: diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ca38483c9f56ef566518cf55d466965d89d6a5cb..ffe549338cec6ec4a9dcbb7ee92be08252d24c97 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -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) { @@ -2282,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))