xHCI: Set link state support
authorAndiry Xu <andiry.xu@amd.com>
Wed, 27 Apr 2011 10:07:39 +0000 (18:07 +0800)
committerSarah Sharp <sarah.a.sharp@linux.intel.com>
Mon, 2 May 2011 23:42:52 +0000 (16:42 -0700)
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>
drivers/usb/host/xhci-hub.c

index f3bafba4b5c66e617dce262e135d84dec298d8d1..b87535442386466fba2282e94108ea8fa4b5c24d 100644 (file)
@@ -387,6 +387,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
        __le32 __iomem **port_array;
        int slot_id;
        struct xhci_bus_state *bus_state;
+       u16 link_state = 0;
 
        if (hcd->speed == HCD_USB3) {
                ports = xhci->num_usb3_ports;
@@ -497,6 +498,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                put_unaligned(cpu_to_le32(status), (__le32 *) buf);
                break;
        case SetPortFeature:
+               if (wValue == USB_PORT_FEAT_LINK_STATE)
+                       link_state = (wIndex & 0xff00) >> 3;
                wIndex &= 0xff;
                if (!wIndex || wIndex > ports)
                        goto error;
@@ -545,6 +548,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        temp = xhci_readl(xhci, port_array[wIndex]);
                        bus_state->suspended_ports |= 1 << wIndex;
                        break;
+               case USB_PORT_FEAT_LINK_STATE:
+                       temp = xhci_readl(xhci, port_array[wIndex]);
+                       /* Software should not attempt to set
+                        * port link state above '5' (Rx.Detect) and the port
+                        * must be enabled.
+                        */
+                       if ((temp & PORT_PE) == 0 ||
+                               (link_state > USB_SS_PORT_LS_RX_DETECT)) {
+                               xhci_warn(xhci, "Cannot set link state.\n");
+                               goto error;
+                       }
+
+                       if (link_state == USB_SS_PORT_LS_U3) {
+                               slot_id = xhci_find_slot_id_by_port(hcd, xhci,
+                                               wIndex + 1);
+                               if (slot_id) {
+                                       /* unlock to execute stop endpoint
+                                        * commands */
+                                       spin_unlock_irqrestore(&xhci->lock,
+                                                               flags);
+                                       xhci_stop_device(xhci, slot_id, 1);
+                                       spin_lock_irqsave(&xhci->lock, flags);
+                               }
+                       }
+
+                       temp = xhci_port_state_to_neutral(temp);
+                       temp &= ~PORT_PLS_MASK;
+                       temp |= PORT_LINK_STROBE | link_state;
+                       xhci_writel(xhci, temp, port_array[wIndex]);
+
+                       spin_unlock_irqrestore(&xhci->lock, flags);
+                       msleep(20); /* wait device to enter */
+                       spin_lock_irqsave(&xhci->lock, flags);
+
+                       temp = xhci_readl(xhci, port_array[wIndex]);
+                       if (link_state == USB_SS_PORT_LS_U3)
+                               bus_state->suspended_ports |= 1 << wIndex;
+                       break;
                case USB_PORT_FEAT_POWER:
                        /*
                         * Turn on ports, even if there isn't per-port switching.