aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-05 18:49:47 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-07-05 18:49:47 -0400
commitd25ae36934e39fb902449e182a5ad7e47e9f5c10 (patch)
tree18b47fd7651368d8eb393dc15ba2fd95e31317a5 /drivers
parent6887a4131da3adaab011613776d865f4bcfb5678 (diff)
parent0d9f78a92ef5e97d9fe51d9215ebe22f6f0d289d (diff)
Merge tag 'for-usb-linus-2012-07-02' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus
xhci: Fix driver hang and resume error path. Hi Greg, Here's two bug fixes for 3.5. The first fixes an issue with port connections not being reported to the USB core after a system resume. The second fixes a driver hang when there are two back to back stalls on an endpoint. Sarah Sharp
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/core/hub.c18
-rw-r--r--drivers/usb/host/xhci-hub.c44
-rw-r--r--drivers/usb/host/xhci-ring.c11
-rw-r--r--drivers/usb/host/xhci.h6
4 files changed, 64 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 25a7422ee657..8fb484984c86 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2324,12 +2324,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
2324static int hub_port_reset(struct usb_hub *hub, int port1, 2324static int hub_port_reset(struct usb_hub *hub, int port1,
2325 struct usb_device *udev, unsigned int delay, bool warm); 2325 struct usb_device *udev, unsigned int delay, bool warm);
2326 2326
2327/* Is a USB 3.0 port in the Inactive state? */ 2327/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
2328static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) 2328 * Port worm reset is required to recover
2329 */
2330static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
2329{ 2331{
2330 return hub_is_superspeed(hub->hdev) && 2332 return hub_is_superspeed(hub->hdev) &&
2331 (portstatus & USB_PORT_STAT_LINK_STATE) == 2333 (((portstatus & USB_PORT_STAT_LINK_STATE) ==
2332 USB_SS_PORT_LS_SS_INACTIVE; 2334 USB_SS_PORT_LS_SS_INACTIVE) ||
2335 ((portstatus & USB_PORT_STAT_LINK_STATE) ==
2336 USB_SS_PORT_LS_COMP_MOD)) ;
2333} 2337}
2334 2338
2335static int hub_port_wait_reset(struct usb_hub *hub, int port1, 2339static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2365,7 +2369,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2365 * 2369 *
2366 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 2370 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
2367 */ 2371 */
2368 if (hub_port_inactive(hub, portstatus)) { 2372 if (hub_port_warm_reset_required(hub, portstatus)) {
2369 int ret; 2373 int ret;
2370 2374
2371 if ((portchange & USB_PORT_STAT_C_CONNECTION)) 2375 if ((portchange & USB_PORT_STAT_C_CONNECTION))
@@ -4408,9 +4412,7 @@ static void hub_events(void)
4408 /* Warm reset a USB3 protocol port if it's in 4412 /* Warm reset a USB3 protocol port if it's in
4409 * SS.Inactive state. 4413 * SS.Inactive state.
4410 */ 4414 */
4411 if (hub_is_superspeed(hub->hdev) && 4415 if (hub_port_warm_reset_required(hub, portstatus)) {
4412 (portstatus & USB_PORT_STAT_LINK_STATE)
4413 == USB_SS_PORT_LS_SS_INACTIVE) {
4414 dev_dbg(hub_dev, "warm reset port %d\n", i); 4416 dev_dbg(hub_dev, "warm reset port %d\n", i);
4415 hub_port_reset(hub, i, NULL, 4417 hub_port_reset(hub, i, NULL,
4416 HUB_BH_RESET_TIME, true); 4418 HUB_BH_RESET_TIME, true);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 2732ef660c5c..7b01094d7993 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -462,6 +462,42 @@ void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
462 } 462 }
463} 463}
464 464
465/* Updates Link Status for super Speed port */
466static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
467{
468 u32 pls = status_reg & PORT_PLS_MASK;
469
470 /* resume state is a xHCI internal state.
471 * Do not report it to usb core.
472 */
473 if (pls == XDEV_RESUME)
474 return;
475
476 /* When the CAS bit is set then warm reset
477 * should be performed on port
478 */
479 if (status_reg & PORT_CAS) {
480 /* The CAS bit can be set while the port is
481 * in any link state.
482 * Only roothubs have CAS bit, so we
483 * pretend to be in compliance mode
484 * unless we're already in compliance
485 * or the inactive state.
486 */
487 if (pls != USB_SS_PORT_LS_COMP_MOD &&
488 pls != USB_SS_PORT_LS_SS_INACTIVE) {
489 pls = USB_SS_PORT_LS_COMP_MOD;
490 }
491 /* Return also connection bit -
492 * hub state machine resets port
493 * when this bit is set.
494 */
495 pls |= USB_PORT_STAT_CONNECTION;
496 }
497 /* update status field */
498 *status |= pls;
499}
500
465int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 501int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
466 u16 wIndex, char *buf, u16 wLength) 502 u16 wIndex, char *buf, u16 wLength)
467{ 503{
@@ -606,13 +642,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
606 else 642 else
607 status |= USB_PORT_STAT_POWER; 643 status |= USB_PORT_STAT_POWER;
608 } 644 }
609 /* Port Link State */ 645 /* Update Port Link State for super speed ports*/
610 if (hcd->speed == HCD_USB3) { 646 if (hcd->speed == HCD_USB3) {
611 /* resume state is a xHCI internal state. 647 xhci_hub_report_link_state(&status, temp);
612 * Do not report it to usb core.
613 */
614 if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
615 status |= (temp & PORT_PLS_MASK);
616 } 648 }
617 if (bus_state->port_c_suspend & (1 << wIndex)) 649 if (bus_state->port_c_suspend & (1 << wIndex))
618 status |= 1 << USB_PORT_FEAT_C_SUSPEND; 650 status |= 1 << USB_PORT_FEAT_C_SUSPEND;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 23b4aefd1036..8275645889da 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -885,6 +885,17 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
885 num_trbs_free_temp = ep_ring->num_trbs_free; 885 num_trbs_free_temp = ep_ring->num_trbs_free;
886 dequeue_temp = ep_ring->dequeue; 886 dequeue_temp = ep_ring->dequeue;
887 887
888 /* If we get two back-to-back stalls, and the first stalled transfer
889 * ends just before a link TRB, the dequeue pointer will be left on
890 * the link TRB by the code in the while loop. So we have to update
891 * the dequeue pointer one segment further, or we'll jump off
892 * the segment into la-la-land.
893 */
894 if (last_trb(xhci, ep_ring, ep_ring->deq_seg, ep_ring->dequeue)) {
895 ep_ring->deq_seg = ep_ring->deq_seg->next;
896 ep_ring->dequeue = ep_ring->deq_seg->trbs;
897 }
898
888 while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) { 899 while (ep_ring->dequeue != dev->eps[ep_index].queued_deq_ptr) {
889 /* We have more usable TRBs */ 900 /* We have more usable TRBs */
890 ep_ring->num_trbs_free++; 901 ep_ring->num_trbs_free++;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index de3d6e3e57be..55c0785810c9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -341,7 +341,11 @@ struct xhci_op_regs {
341#define PORT_PLC (1 << 22) 341#define PORT_PLC (1 << 22)
342/* port configure error change - port failed to configure its link partner */ 342/* port configure error change - port failed to configure its link partner */
343#define PORT_CEC (1 << 23) 343#define PORT_CEC (1 << 23)
344/* bit 24 reserved */ 344/* Cold Attach Status - xHC can set this bit to report device attached during
345 * Sx state. Warm port reset should be perfomed to clear this bit and move port
346 * to connected state.
347 */
348#define PORT_CAS (1 << 24)
345/* wake on connect (enable) */ 349/* wake on connect (enable) */
346#define PORT_WKCONN_E (1 << 25) 350#define PORT_WKCONN_E (1 << 25)
347/* wake on disconnect (enable) */ 351/* wake on disconnect (enable) */