xhci: support new USB 3.1 hub request to get extended port status
authorMathias Nyman <mathias.nyman@linux.intel.com>
Thu, 1 Oct 2015 15:40:39 +0000 (18:40 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Oct 2015 09:34:17 +0000 (10:34 +0100)
USB 3.1 adds different types of Get Port Status request.
The Get Extended Port Status request returns 4 additional bytes
after the normal portstatus and portchange words containing
link speed and lane information about a connected enhanced super
speed device

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci.h

index a4d429ce02a221422500b67b3ade616cf8bac9cb..5d2d7e954bd4cb8dbaa51ec1121e236e8f472dce 100644 (file)
@@ -659,6 +659,22 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
        }
 }
 
+static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li)
+{
+       u32 ext_stat = 0;
+       int speed_id;
+
+       /* only support rx and tx lane counts of 1 in usb3.1 spec */
+       speed_id = DEV_PORT_SPEED(raw_port_status);
+       ext_stat |= speed_id;           /* bits 3:0, RX speed id */
+       ext_stat |= speed_id << 4;      /* bits 7:4, TX speed id */
+
+       ext_stat |= PORT_RX_LANES(port_li) << 8;  /* bits 11:8 Rx lane count */
+       ext_stat |= PORT_TX_LANES(port_li) << 12; /* bits 15:12 Tx lane count */
+
+       return ext_stat;
+}
+
 /*
  * Converts a raw xHCI port status into the format that external USB 2.0 or USB
  * 3.0 hubs use.
@@ -874,6 +890,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
 
                put_unaligned(cpu_to_le32(status), (__le32 *) buf);
+               /* if USB 3.1 extended port status return additional 4 bytes */
+               if (wValue == 0x02) {
+                       u32 port_li;
+
+                       if (hcd->speed < HCD_USB31 || wLength != 8) {
+                               xhci_err(xhci, "get ext port status invalid parameter\n");
+                               retval = -EINVAL;
+                               break;
+                       }
+                       port_li = readl(port_array[wIndex] + PORTLI);
+                       status = xhci_get_ext_port_status(temp, port_li);
+                       put_unaligned_le32(cpu_to_le32(status), &buf[4]);
+               }
                break;
        case SetPortFeature:
                if (wValue == USB_PORT_FEAT_LINK_STATE)
index e2c76e2108176850a049d31998e19fd7bdde8f01..51093df159388a6c6b6a48e47d36e329c38c9f4e 100644 (file)
@@ -335,6 +335,7 @@ struct xhci_op_regs {
 #define DEV_SUPERSPEED(p)      (((p) & DEV_SPEED_MASK) == XDEV_SS)
 #define DEV_SUPERSPEEDPLUS(p)  (((p) & DEV_SPEED_MASK) == XDEV_SSP)
 #define DEV_SUPERSPEED_ANY(p)  (((p) & DEV_SPEED_MASK) >= XDEV_SS)
+#define DEV_PORT_SPEED(p)      (((p) >> 10) & 0x0f)
 
 /* Bits 20:23 in the Slot Context are the speed for the device */
 #define        SLOT_SPEED_FS           (XDEV_FS << 10)
@@ -419,6 +420,9 @@ struct xhci_op_regs {
 #define        PORT_L1DS(p)            (((p) & 0xff) << 8)
 #define        PORT_HLE                (1 << 16)
 
+/* USB3 Protocol PORTLI  Port Link Information */
+#define PORT_RX_LANES(p)       (((p) >> 16) & 0xf)
+#define PORT_TX_LANES(p)       (((p) >> 20) & 0xf)
 
 /* USB2 Protocol PORTHLPMC */
 #define PORT_HIRDM(p)((p) & 3)