aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/usb/core/hub.c51
-rw-r--r--drivers/usb/host/xhci-ring.c40
-rw-r--r--drivers/usb/host/xhci.h1
-rw-r--r--include/linux/usb/hcd.h2
4 files changed, 74 insertions, 20 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,
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index ffe549338cec..3a033240ec64 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1241,18 +1241,20 @@ static void handle_device_notification(struct xhci_hcd *xhci,
1241 union xhci_trb *event) 1241 union xhci_trb *event)
1242{ 1242{
1243 u32 slot_id; 1243 u32 slot_id;
1244 struct usb_device *udev;
1244 1245
1245 slot_id = TRB_TO_SLOT_ID(event->generic.field[3]); 1246 slot_id = TRB_TO_SLOT_ID(event->generic.field[3]);
1246 if (!xhci->devs[slot_id]) 1247 if (!xhci->devs[slot_id]) {
1247 xhci_warn(xhci, "Device Notification event for " 1248 xhci_warn(xhci, "Device Notification event for "
1248 "unused slot %u\n", slot_id); 1249 "unused slot %u\n", slot_id);
1249 else 1250 return;
1250 xhci_dbg(xhci, "Device Notification event for slot ID %u\n", 1251 }
1251 slot_id); 1252
1252 /* XXX should we kick khubd for the parent hub? It should have send an 1253 xhci_dbg(xhci, "Device Wake Notification event for slot ID %u\n",
1253 * interrupt transfer when the port started signaling resume, so there's 1254 slot_id);
1254 * probably no need to do so. 1255 udev = xhci->devs[slot_id]->udev;
1255 */ 1256 if (udev && udev->parent)
1257 usb_wakeup_notification(udev->parent, udev->portnum);
1256} 1258}
1257 1259
1258static void handle_port_status(struct xhci_hcd *xhci, 1260static void handle_port_status(struct xhci_hcd *xhci,
@@ -1340,6 +1342,11 @@ static void handle_port_status(struct xhci_hcd *xhci,
1340 1342
1341 if (DEV_SUPERSPEED(temp)) { 1343 if (DEV_SUPERSPEED(temp)) {
1342 xhci_dbg(xhci, "remote wake SS port %d\n", port_id); 1344 xhci_dbg(xhci, "remote wake SS port %d\n", port_id);
1345 /* Set a flag to say the port signaled remote wakeup,
1346 * so we can tell the difference between the end of
1347 * device and host initiated resume.
1348 */
1349 bus_state->port_remote_wakeup |= 1 << faked_port_index;
1343 xhci_test_and_clear_bit(xhci, port_array, 1350 xhci_test_and_clear_bit(xhci, port_array,
1344 faked_port_index, PORT_PLC); 1351 faked_port_index, PORT_PLC);
1345 xhci_set_link_state(xhci, port_array, faked_port_index, 1352 xhci_set_link_state(xhci, port_array, faked_port_index,
@@ -1362,10 +1369,27 @@ static void handle_port_status(struct xhci_hcd *xhci,
1362 if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && 1369 if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 &&
1363 DEV_SUPERSPEED(temp)) { 1370 DEV_SUPERSPEED(temp)) {
1364 xhci_dbg(xhci, "resume SS port %d finished\n", port_id); 1371 xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
1372 /* We've just brought the device into U0 through either the
1373 * Resume state after a device remote wakeup, or through the
1374 * U3Exit state after a host-initiated resume. If it's a device
1375 * initiated remote wake, don't pass up the link state change,
1376 * so the roothub behavior is consistent with external
1377 * USB 3.0 hub behavior.
1378 */
1365 slot_id = xhci_find_slot_id_by_port(hcd, xhci, 1379 slot_id = xhci_find_slot_id_by_port(hcd, xhci,
1366 faked_port_index + 1); 1380 faked_port_index + 1);
1367 if (slot_id && xhci->devs[slot_id]) 1381 if (slot_id && xhci->devs[slot_id])
1368 xhci_ring_device(xhci, slot_id); 1382 xhci_ring_device(xhci, slot_id);
1383 if (bus_state->port_remote_wakeup && (1 << faked_port_index)) {
1384 bus_state->port_remote_wakeup &=
1385 ~(1 << faked_port_index);
1386 xhci_test_and_clear_bit(xhci, port_array,
1387 faked_port_index, PORT_PLC);
1388 usb_wakeup_notification(hcd->self.root_hub,
1389 faked_port_index + 1);
1390 bogus_port_status = true;
1391 goto cleanup;
1392 }
1369 } 1393 }
1370 1394
1371 if (hcd->speed != HCD_USB3) 1395 if (hcd->speed != HCD_USB3)
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index fb99c8379142..0f4936956103 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1344,6 +1344,7 @@ struct xhci_bus_state {
1344 /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */ 1344 /* ports suspend status arrays - max 31 ports for USB2, 15 for USB3 */
1345 u32 port_c_suspend; 1345 u32 port_c_suspend;
1346 u32 suspended_ports; 1346 u32 suspended_ports;
1347 u32 port_remote_wakeup;
1347 unsigned long resume_done[USB_MAXCHILDREN]; 1348 unsigned long resume_done[USB_MAXCHILDREN];
1348}; 1349};
1349 1350
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index b2f62f3a32af..2e6071efbfb7 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -412,6 +412,8 @@ extern irqreturn_t usb_hcd_irq(int irq, void *__hcd);
412 412
413extern void usb_hc_died(struct usb_hcd *hcd); 413extern void usb_hc_died(struct usb_hcd *hcd);
414extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd); 414extern void usb_hcd_poll_rh_status(struct usb_hcd *hcd);
415extern void usb_wakeup_notification(struct usb_device *hdev,
416 unsigned int portnum);
415 417
416/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */ 418/* The D0/D1 toggle bits ... USE WITH CAUTION (they're almost hcd-internal) */
417#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1) 419#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> (ep)) & 1)