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:39 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2011-05-02 19:42:52 -0400
commit2c44178032b046c4113c40d0d459a0d36e39b920 (patch)
tree998a4e5d9594b9d2da6f5cc35af4ac8be50c5171 /drivers/usb/host/xhci-hub.c
parent85387c0ea3e1cd85ad9d7215917ff5e71ca2aea3 (diff)
xHCI: Set link state support
This patch adds support for Set Port Feature(PORT_LINK_STATE) request. The most significant byte (bits 15..8) of the wIndex field specifies the U state the host software wants to put the link connected to the port into. This request is only valid when the PORT_ENABLE bit is set and the PORT_LINK_STATE should not be above value '5' (Rx.Detect). This request will be later used to replace the set/clear suspend USB3 protocol ports in hub driver. 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.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index f3bafba4b5c6..b87535442386 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -387,6 +387,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
387 __le32 __iomem **port_array; 387 __le32 __iomem **port_array;
388 int slot_id; 388 int slot_id;
389 struct xhci_bus_state *bus_state; 389 struct xhci_bus_state *bus_state;
390 u16 link_state = 0;
390 391
391 if (hcd->speed == HCD_USB3) { 392 if (hcd->speed == HCD_USB3) {
392 ports = xhci->num_usb3_ports; 393 ports = xhci->num_usb3_ports;
@@ -497,6 +498,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
497 put_unaligned(cpu_to_le32(status), (__le32 *) buf); 498 put_unaligned(cpu_to_le32(status), (__le32 *) buf);
498 break; 499 break;
499 case SetPortFeature: 500 case SetPortFeature:
501 if (wValue == USB_PORT_FEAT_LINK_STATE)
502 link_state = (wIndex & 0xff00) >> 3;
500 wIndex &= 0xff; 503 wIndex &= 0xff;
501 if (!wIndex || wIndex > ports) 504 if (!wIndex || wIndex > ports)
502 goto error; 505 goto error;
@@ -545,6 +548,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
545 temp = xhci_readl(xhci, port_array[wIndex]); 548 temp = xhci_readl(xhci, port_array[wIndex]);
546 bus_state->suspended_ports |= 1 << wIndex; 549 bus_state->suspended_ports |= 1 << wIndex;
547 break; 550 break;
551 case USB_PORT_FEAT_LINK_STATE:
552 temp = xhci_readl(xhci, port_array[wIndex]);
553 /* Software should not attempt to set
554 * port link state above '5' (Rx.Detect) and the port
555 * must be enabled.
556 */
557 if ((temp & PORT_PE) == 0 ||
558 (link_state > USB_SS_PORT_LS_RX_DETECT)) {
559 xhci_warn(xhci, "Cannot set link state.\n");
560 goto error;
561 }
562
563 if (link_state == USB_SS_PORT_LS_U3) {
564 slot_id = xhci_find_slot_id_by_port(hcd, xhci,
565 wIndex + 1);
566 if (slot_id) {
567 /* unlock to execute stop endpoint
568 * commands */
569 spin_unlock_irqrestore(&xhci->lock,
570 flags);
571 xhci_stop_device(xhci, slot_id, 1);
572 spin_lock_irqsave(&xhci->lock, flags);
573 }
574 }
575
576 temp = xhci_port_state_to_neutral(temp);
577 temp &= ~PORT_PLS_MASK;
578 temp |= PORT_LINK_STROBE | link_state;
579 xhci_writel(xhci, temp, port_array[wIndex]);
580
581 spin_unlock_irqrestore(&xhci->lock, flags);
582 msleep(20); /* wait device to enter */
583 spin_lock_irqsave(&xhci->lock, flags);
584
585 temp = xhci_readl(xhci, port_array[wIndex]);
586 if (link_state == USB_SS_PORT_LS_U3)
587 bus_state->suspended_ports |= 1 << wIndex;
588 break;
548 case USB_PORT_FEAT_POWER: 589 case USB_PORT_FEAT_POWER:
549 /* 590 /*
550 * Turn on ports, even if there isn't per-port switching. 591 * Turn on ports, even if there isn't per-port switching.