| From: Ravi Chandra Sadineni <ravisadineni@chromium.org> |
| Date: Fri, 20 Apr 2018 11:08:21 -0700 |
| Subject: USB: Increment wakeup count on remote wakeup. |
| |
| commit 83a62c51ba7b3c0bf45150c4eac7aefc6c785e94 upstream. |
| |
| On chromebooks we depend on wakeup count to identify the wakeup source. |
| But currently USB devices do not increment the wakeup count when they |
| trigger the remote wake. This patch addresses the same. |
| |
| Resume condition is reported differently on USB 2.0 and USB 3.0 devices. |
| |
| On USB 2.0 devices, a wake capable device, if wake enabled, drives |
| resume signal to indicate a remote wake (USB 2.0 spec section 7.1.7.7). |
| The upstream facing port then sets C_PORT_SUSPEND bit and reports a |
| port change event (USB 2.0 spec section 11.24.2.7.2.3). Thus if a port |
| has resumed before driving the resume signal from the host and |
| C_PORT_SUSPEND is set, then the device attached to the given port might |
| be the reason for the last system wakeup. Increment the wakeup count for |
| the same. |
| |
| On USB 3.0 devices, a function may signal that it wants to exit from device |
| suspend by sending a Function Wake Device Notification to the host (USB3.0 |
| spec section 8.5.6.4) Thus on receiving the Function Wake, increment the |
| wakeup count. |
| |
| Signed-off-by: Ravi Chandra Sadineni <ravisadineni@chromium.org> |
| Acked-by: Alan Stern <stern@rowland.harvard.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| drivers/usb/core/hcd.c | 1 + |
| drivers/usb/core/hub.c | 10 +++++++++- |
| 2 files changed, 10 insertions(+), 1 deletion(-) |
| |
| --- a/drivers/usb/core/hcd.c |
| +++ b/drivers/usb/core/hcd.c |
| @@ -2293,6 +2293,7 @@ void usb_hcd_resume_root_hub (struct usb |
| |
| spin_lock_irqsave (&hcd_root_hub_lock, flags); |
| if (hcd->rh_registered) { |
| + pm_wakeup_event(&hcd->self.root_hub->dev, 0); |
| set_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags); |
| queue_work(pm_wq, &hcd->wakeup_work); |
| } |
| --- a/drivers/usb/core/hub.c |
| +++ b/drivers/usb/core/hub.c |
| @@ -626,12 +626,17 @@ void usb_wakeup_notification(struct usb_ |
| unsigned int portnum) |
| { |
| struct usb_hub *hub; |
| + struct usb_port *port_dev; |
| |
| if (!hdev) |
| return; |
| |
| hub = usb_hub_to_struct_hub(hdev); |
| if (hub) { |
| + port_dev = hub->ports[portnum - 1]; |
| + if (port_dev && port_dev->child) |
| + pm_wakeup_event(&port_dev->child->dev, 0); |
| + |
| set_bit(portnum, hub->wakeup_bits); |
| kick_khubd(hub); |
| } |
| @@ -3313,8 +3318,11 @@ int usb_port_resume(struct usb_device *u |
| |
| /* Skip the initial Clear-Suspend step for a remote wakeup */ |
| status = hub_port_status(hub, port1, &portstatus, &portchange); |
| - if (status == 0 && !port_is_suspended(hub, portstatus)) |
| + if (status == 0 && !port_is_suspended(hub, portstatus)) { |
| + if (portchange & USB_PORT_STAT_C_SUSPEND) |
| + pm_wakeup_event(&udev->dev, 0); |
| goto SuspendCleared; |
| + } |
| |
| /* see 7.1.7.7; affects power usage, but not budgeting */ |
| if (hub_is_superspeed(hub->hdev)) |