aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-hub.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-04-27 06:07:50 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-05-02 19:42:53 -0400
commita7114230f6bd925f1c734d8ca1c32c93bf956aed (patch)
treec7e08619e6b5815fe5634120c3954ecbdb8c50e2 /drivers/usb/host/xhci-hub.c
parent0ed9a57e052a3d20df052a2ff12a3b42380867aa (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.c49
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. */