From 395f5409ca4336ee6ad461d3e0436bcc2d7c6309 Mon Sep 17 00:00:00 2001 From: Mathias Nyman Date: Thu, 1 Oct 2015 18:40:39 +0300 Subject: [PATCH] xhci: support new USB 3.1 hub request to get extended port status 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/xhci-hub.c | 29 +++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index a4d429ce02a2..5d2d7e954bd4 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -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) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index e2c76e210817..51093df15938 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -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)