aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorSarah Sharp <sarah.a.sharp@linux.intel.com>2011-11-14 21:00:01 -0500
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-02-14 15:12:26 -0500
commit4ee823b83bc9851743fab756c76b27d6a1e2472b (patch)
treea36c50b18f8b9cc7ebca78173171ab5b7de5e064 /drivers/usb/core
parent714b07be3bbf94d2dc9838723d63fc827fdbef12 (diff)
USB/xHCI: Support device-initiated USB 3.0 resume.
USB 3.0 hubs don't have a port suspend change bit (that bit is now reserved). Instead, when a host-initiated resume finishes, the hub sets the port link state change bit. When a USB 3.0 device initiates remote wakeup, the parent hubs with their upstream links in U3 will pass the LFPS up the chain. The first hub that has an upstream link in U0 (which may be the roothub) will reflect that LFPS back down the path to the device. However, the parent hubs in the resumed path will not set their link state change bit. Instead, the device that initiated the resume has to send an asynchronous "Function Wake" Device Notification up to the host controller. Therefore, we need a way to notify the USB core of a device resume without going through the normal hub URB completion method. First, make the xHCI roothub act like an external USB 3.0 hub and not pass up the port link state change bit when a device-initiated resume finishes. Introduce a new xHCI bit field, port_remote_wakeup, so that we can tell the difference between a port coming out of the U3Exit state (host-initiated resume) and the RExit state (ending state of device-initiated resume). Since the USB core can't tell whether a port on a hub has resumed by looking at the Hub Status buffer, we need to introduce a bitfield, wakeup_bits, that indicates which ports have resumed. When the xHCI driver notices a port finishing a device-initiated resume, we call into a new USB core function, usb_wakeup_notification(), that will set the right bit in wakeup_bits, and kick khubd for that hub. We also call usb_wakeup_notification() when the Function Wake Device Notification is received by the xHCI driver. This covers the case where the link between the roothub and the first-tier hub is in U0, and the hub reflects the resume signaling back to the device without giving any indication it has done so until the device sends the Function Wake notification. Change the code in khubd that handles the remote wakeup to look at the state the USB core thinks the device is in, and handle the remote wakeup if the port's wakeup bit is set. This patch only takes care of the case where the device is attached directly to the roothub, or the USB 3.0 hub that is attached to the root hub is the device sending the Function Wake Device Notification (e.g. because a new USB device was attached). The other cases will be covered in a second patch. Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/hub.c51
1 files changed, 39 insertions, 12 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index ba9509454ed5..1c32bbac9862 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -62,6 +62,8 @@ struct usb_hub {
62 resumed */ 62 resumed */
63 unsigned long removed_bits[1]; /* ports with a "removed" 63 unsigned long removed_bits[1]; /* ports with a "removed"
64 device present */ 64 device present */
65 unsigned long wakeup_bits[1]; /* ports that have signaled
66 remote wakeup */
65#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */ 67#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
66#error event_bits[] is too short! 68#error event_bits[] is too short!
67#endif 69#endif
@@ -411,6 +413,29 @@ void usb_kick_khubd(struct usb_device *hdev)
411 kick_khubd(hub); 413 kick_khubd(hub);
412} 414}
413 415
416/*
417 * Let the USB core know that a USB 3.0 device has sent a Function Wake Device
418 * Notification, which indicates it had initiated remote wakeup.
419 *
420 * USB 3.0 hubs do not report the port link state change from U3 to U0 when the
421 * device initiates resume, so the USB core will not receive notice of the
422 * resume through the normal hub interrupt URB.
423 */
424void usb_wakeup_notification(struct usb_device *hdev,
425 unsigned int portnum)
426{
427 struct usb_hub *hub;
428
429 if (!hdev)
430 return;
431
432 hub = hdev_to_hub(hdev);
433 if (hub) {
434 set_bit(portnum, hub->wakeup_bits);
435 kick_khubd(hub);
436 }
437}
438EXPORT_SYMBOL_GPL(usb_wakeup_notification);
414 439
415/* completion function, fires on port status changes and various faults */ 440/* completion function, fires on port status changes and various faults */
416static void hub_irq(struct urb *urb) 441static void hub_irq(struct urb *urb)
@@ -807,12 +832,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
807 clear_port_feature(hub->hdev, port1, 832 clear_port_feature(hub->hdev, port1,
808 USB_PORT_FEAT_C_ENABLE); 833 USB_PORT_FEAT_C_ENABLE);
809 } 834 }
810 if (portchange & USB_PORT_STAT_C_LINK_STATE) {
811 need_debounce_delay = true;
812 clear_port_feature(hub->hdev, port1,
813 USB_PORT_FEAT_C_PORT_LINK_STATE);
814 }
815
816 if ((portchange & USB_PORT_STAT_C_BH_RESET) && 835 if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
817 hub_is_superspeed(hub->hdev)) { 836 hub_is_superspeed(hub->hdev)) {
818 need_debounce_delay = true; 837 need_debounce_delay = true;
@@ -3498,11 +3517,18 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
3498 int ret; 3517 int ret;
3499 3518
3500 hdev = hub->hdev; 3519 hdev = hub->hdev;
3501 if (!(portchange & USB_PORT_STAT_C_SUSPEND))
3502 return 0;
3503 clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
3504
3505 udev = hdev->children[port-1]; 3520 udev = hdev->children[port-1];
3521 if (!hub_is_superspeed(hdev)) {
3522 if (!(portchange & USB_PORT_STAT_C_SUSPEND))
3523 return 0;
3524 clear_port_feature(hdev, port, USB_PORT_FEAT_C_SUSPEND);
3525 } else {
3526 if (!udev || udev->state != USB_STATE_SUSPENDED ||
3527 !test_and_clear_bit(udev->portnum,
3528 hub->wakeup_bits))
3529 return 0;
3530 }
3531
3506 if (udev) { 3532 if (udev) {
3507 /* TRSMRCY = 10 msec */ 3533 /* TRSMRCY = 10 msec */
3508 msleep(10); 3534 msleep(10);
@@ -3533,7 +3559,7 @@ static void hub_events(void)
3533 u16 portstatus; 3559 u16 portstatus;
3534 u16 portchange; 3560 u16 portchange;
3535 int i, ret; 3561 int i, ret;
3536 int connect_change; 3562 int connect_change, wakeup_change;
3537 3563
3538 /* 3564 /*
3539 * We restart the list every time to avoid a deadlock with 3565 * We restart the list every time to avoid a deadlock with
@@ -3612,8 +3638,9 @@ static void hub_events(void)
3612 if (test_bit(i, hub->busy_bits)) 3638 if (test_bit(i, hub->busy_bits))
3613 continue; 3639 continue;
3614 connect_change = test_bit(i, hub->change_bits); 3640 connect_change = test_bit(i, hub->change_bits);
3641 wakeup_change = test_bit(i, hub->wakeup_bits);
3615 if (!test_and_clear_bit(i, hub->event_bits) && 3642 if (!test_and_clear_bit(i, hub->event_bits) &&
3616 !connect_change) 3643 !connect_change && !wakeup_change)
3617 continue; 3644 continue;
3618 3645
3619 ret = hub_port_status(hub, i, 3646 ret = hub_port_status(hub, i,