diff options
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 41 |
1 files changed, 30 insertions, 11 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0be788cc2fd..ce9f974dac0 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -392,6 +392,20 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) | |||
392 | return max_ports; | 392 | return max_ports; |
393 | } | 393 | } |
394 | 394 | ||
395 | /* Test and clear port RWC bit */ | ||
396 | void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array, | ||
397 | int port_id, u32 port_bit) | ||
398 | { | ||
399 | u32 temp; | ||
400 | |||
401 | temp = xhci_readl(xhci, port_array[port_id]); | ||
402 | if (temp & port_bit) { | ||
403 | temp = xhci_port_state_to_neutral(temp); | ||
404 | temp |= port_bit; | ||
405 | xhci_writel(xhci, temp, port_array[port_id]); | ||
406 | } | ||
407 | } | ||
408 | |||
395 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | 409 | int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, |
396 | u16 wIndex, char *buf, u16 wLength) | 410 | u16 wIndex, char *buf, u16 wLength) |
397 | { | 411 | { |
@@ -463,11 +477,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
463 | && (temp & PORT_POWER)) | 477 | && (temp & PORT_POWER)) |
464 | status |= USB_PORT_STAT_SUSPEND; | 478 | status |= USB_PORT_STAT_SUSPEND; |
465 | } | 479 | } |
466 | if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { | 480 | if ((temp & PORT_PLS_MASK) == XDEV_RESUME && |
481 | !DEV_SUPERSPEED(temp)) { | ||
467 | if ((temp & PORT_RESET) || !(temp & PORT_PE)) | 482 | if ((temp & PORT_RESET) || !(temp & PORT_PE)) |
468 | goto error; | 483 | goto error; |
469 | if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies, | 484 | if (time_after_eq(jiffies, |
470 | bus_state->resume_done[wIndex])) { | 485 | bus_state->resume_done[wIndex])) { |
471 | xhci_dbg(xhci, "Resume USB2 port %d\n", | 486 | xhci_dbg(xhci, "Resume USB2 port %d\n", |
472 | wIndex + 1); | 487 | wIndex + 1); |
473 | bus_state->resume_done[wIndex] = 0; | 488 | bus_state->resume_done[wIndex] = 0; |
@@ -487,6 +502,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
487 | xhci_ring_device(xhci, slot_id); | 502 | xhci_ring_device(xhci, slot_id); |
488 | bus_state->port_c_suspend |= 1 << wIndex; | 503 | bus_state->port_c_suspend |= 1 << wIndex; |
489 | bus_state->suspended_ports &= ~(1 << wIndex); | 504 | bus_state->suspended_ports &= ~(1 << wIndex); |
505 | } else { | ||
506 | /* | ||
507 | * The resume has been signaling for less than | ||
508 | * 20ms. Report the port status as SUSPEND, | ||
509 | * let the usbcore check port status again | ||
510 | * and clear resume signaling later. | ||
511 | */ | ||
512 | status |= USB_PORT_STAT_SUSPEND; | ||
490 | } | 513 | } |
491 | } | 514 | } |
492 | if ((temp & PORT_PLS_MASK) == XDEV_U0 | 515 | if ((temp & PORT_PLS_MASK) == XDEV_U0 |
@@ -664,7 +687,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
664 | xhci_dbg(xhci, "PORTSC %04x\n", temp); | 687 | xhci_dbg(xhci, "PORTSC %04x\n", temp); |
665 | if (temp & PORT_RESET) | 688 | if (temp & PORT_RESET) |
666 | goto error; | 689 | goto error; |
667 | if (temp & XDEV_U3) { | 690 | if ((temp & PORT_PLS_MASK) == XDEV_U3) { |
668 | if ((temp & PORT_PE) == 0) | 691 | if ((temp & PORT_PE) == 0) |
669 | goto error; | 692 | goto error; |
670 | 693 | ||
@@ -752,7 +775,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
752 | memset(buf, 0, retval); | 775 | memset(buf, 0, retval); |
753 | status = 0; | 776 | status = 0; |
754 | 777 | ||
755 | mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC; | 778 | mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC | PORT_WRC; |
756 | 779 | ||
757 | spin_lock_irqsave(&xhci->lock, flags); | 780 | spin_lock_irqsave(&xhci->lock, flags); |
758 | /* For each port, did anything change? If so, set that bit in buf. */ | 781 | /* For each port, did anything change? If so, set that bit in buf. */ |
@@ -929,12 +952,8 @@ int xhci_bus_resume(struct usb_hcd *hcd) | |||
929 | spin_lock_irqsave(&xhci->lock, flags); | 952 | spin_lock_irqsave(&xhci->lock, flags); |
930 | 953 | ||
931 | /* Clear PLC */ | 954 | /* Clear PLC */ |
932 | temp = xhci_readl(xhci, port_array[port_index]); | 955 | xhci_test_and_clear_bit(xhci, port_array, port_index, |
933 | if (temp & PORT_PLC) { | 956 | PORT_PLC); |
934 | temp = xhci_port_state_to_neutral(temp); | ||
935 | temp |= PORT_PLC; | ||
936 | xhci_writel(xhci, temp, port_array[port_index]); | ||
937 | } | ||
938 | 957 | ||
939 | slot_id = xhci_find_slot_id_by_port(hcd, | 958 | slot_id = xhci_find_slot_id_by_port(hcd, |
940 | xhci, port_index + 1); | 959 | xhci, port_index + 1); |