diff options
author | Andiry Xu <andiry.xu@amd.com> | 2011-04-27 06:07:50 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-05-02 19:42:53 -0400 |
commit | a7114230f6bd925f1c734d8ca1c32c93bf956aed (patch) | |
tree | c7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers/usb/host/xhci-hub.c | |
parent | 0ed9a57e052a3d20df052a2ff12a3b42380867aa (diff) |
usbcore: Refine USB3.0 device suspend and resume
In the past, we use USB2.0 request to suspend and resume a USB3.0 device.
Actually, USB3.0 hub does not support Set/Clear PORT_SUSPEND request,
instead, it uses Set PORT_LINK_STATE request. This patch makes USB3.0 device
suspend/resume comply with USB3.0 specification.
This patch fixes the issue that USB3.0 device can not be suspended when
connected to a USB3.0 external hub.
Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 49 |
1 files changed, 21 insertions, 28 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 4a3ca99fc64e..e3ddc6a95afe 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c | |||
@@ -483,7 +483,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
483 | && (temp & PORT_POWER) | 483 | && (temp & PORT_POWER) |
484 | && (bus_state->suspended_ports & (1 << wIndex))) { | 484 | && (bus_state->suspended_ports & (1 << wIndex))) { |
485 | bus_state->suspended_ports &= ~(1 << wIndex); | 485 | bus_state->suspended_ports &= ~(1 << wIndex); |
486 | bus_state->port_c_suspend |= 1 << wIndex; | 486 | if (hcd->speed != HCD_USB3) |
487 | bus_state->port_c_suspend |= 1 << wIndex; | ||
487 | } | 488 | } |
488 | if (temp & PORT_CONNECT) { | 489 | if (temp & PORT_CONNECT) { |
489 | status |= USB_PORT_STAT_CONNECTION; | 490 | status |= USB_PORT_STAT_CONNECTION; |
@@ -656,35 +657,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, | |||
656 | if (temp & XDEV_U3) { | 657 | if (temp & XDEV_U3) { |
657 | if ((temp & PORT_PE) == 0) | 658 | if ((temp & PORT_PE) == 0) |
658 | goto error; | 659 | goto error; |
659 | if (DEV_SUPERSPEED(temp)) { | ||
660 | temp = xhci_port_state_to_neutral(temp); | ||
661 | temp &= ~PORT_PLS_MASK; | ||
662 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
663 | xhci_writel(xhci, temp, | ||
664 | port_array[wIndex]); | ||
665 | xhci_readl(xhci, port_array[wIndex]); | ||
666 | } else { | ||
667 | temp = xhci_port_state_to_neutral(temp); | ||
668 | temp &= ~PORT_PLS_MASK; | ||
669 | temp |= PORT_LINK_STROBE | XDEV_RESUME; | ||
670 | xhci_writel(xhci, temp, | ||
671 | port_array[wIndex]); | ||
672 | 660 | ||
673 | spin_unlock_irqrestore(&xhci->lock, | 661 | temp = xhci_port_state_to_neutral(temp); |
674 | flags); | 662 | temp &= ~PORT_PLS_MASK; |
675 | msleep(20); | 663 | temp |= PORT_LINK_STROBE | XDEV_RESUME; |
676 | spin_lock_irqsave(&xhci->lock, flags); | 664 | xhci_writel(xhci, temp, |
665 | port_array[wIndex]); | ||
677 | 666 | ||
678 | temp = xhci_readl(xhci, | 667 | spin_unlock_irqrestore(&xhci->lock, |
679 | port_array[wIndex]); | 668 | flags); |
680 | temp = xhci_port_state_to_neutral(temp); | 669 | msleep(20); |
681 | temp &= ~PORT_PLS_MASK; | 670 | spin_lock_irqsave(&xhci->lock, flags); |
682 | temp |= PORT_LINK_STROBE | XDEV_U0; | 671 | |
683 | xhci_writel(xhci, temp, | 672 | temp = xhci_readl(xhci, |
684 | port_array[wIndex]); | 673 | port_array[wIndex]); |
685 | } | 674 | temp = xhci_port_state_to_neutral(temp); |
686 | bus_state->port_c_suspend |= 1 << wIndex; | 675 | temp &= ~PORT_PLS_MASK; |
676 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
677 | xhci_writel(xhci, temp, | ||
678 | port_array[wIndex]); | ||
687 | } | 679 | } |
680 | bus_state->port_c_suspend |= 1 << wIndex; | ||
688 | 681 | ||
689 | slot_id = xhci_find_slot_id_by_port(hcd, xhci, | 682 | slot_id = xhci_find_slot_id_by_port(hcd, xhci, |
690 | wIndex + 1); | 683 | wIndex + 1); |
@@ -755,7 +748,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) | |||
755 | memset(buf, 0, retval); | 748 | memset(buf, 0, retval); |
756 | status = 0; | 749 | status = 0; |
757 | 750 | ||
758 | mask = PORT_CSC | PORT_PEC | PORT_OCC; | 751 | mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC; |
759 | 752 | ||
760 | spin_lock_irqsave(&xhci->lock, flags); | 753 | spin_lock_irqsave(&xhci->lock, flags); |
761 | /* For each port, did anything change? If so, set that bit in buf. */ | 754 | /* For each port, did anything change? If so, set that bit in buf. */ |