diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:32:23 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:32:23 -0700 |
commit | ef1244124349fea36e4a7e260ecaf156b6b6b22a (patch) | |
tree | 92bf1dd4e9fc06708898a6e78de70f7ac36f5a72 /drivers/usb/host/xhci-hub.c | |
parent | d08410d8c9908058a2f69b55e24edfb0d19da7a1 (diff) | |
parent | caa93d9bd2d7ca7ffe5a23df9f003b81721c8e1b (diff) |
Merge tag 'usb-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB and Thunderbolt updates from Greg KH:
"Here is the big set of USB and Thunderbolt driver updates for
5.13-rc1.
Lots of little things in here, with loads of tiny fixes and cleanups
over these drivers, as well as these "larger" changes:
- thunderbolt updates and new features added
- xhci driver updates and split out of a mediatek-specific xhci
driver from the main xhci module to make it easier to work with
(something that I have been wanting for a while).
- loads of typec feature additions and updates
- dwc2 driver updates
- dwc3 driver updates
- gadget driver fixes and minor updates
- loads of usb-serial cleanups and fixes and updates
- usbip documentation updates and fixes
- lots of other tiny USB driver updates
All of these have been in linux-next for a while with no reported
issues"
* tag 'usb-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (371 commits)
usb: Fix up movement of USB core kerneldoc location
usb: dwc3: gadget: Handle DEV_TXF_FLUSH_BYPASS capability
usb: dwc3: Capture new capability register GHWPARAMS9
usb: gadget: prevent a ternary sign expansion bug
usb: dwc3: core: Do core softreset when switch mode
usb: dwc2: Get rid of useless error checks in suspend interrupt
usb: dwc2: Update dwc2_handle_usb_suspend_intr function.
usb: dwc2: Add exit hibernation mode before removing drive
usb: dwc2: Add hibernation exiting flow by system resume
usb: dwc2: Add hibernation entering flow by system suspend
usb: dwc2: Allow exit hibernation in urb enqueue
usb: dwc2: Move exit hibernation to dwc2_port_resume() function
usb: dwc2: Move enter hibernation to dwc2_port_suspend() function
usb: dwc2: Clear GINTSTS_RESTOREDONE bit after restore is generated.
usb: dwc2: Clear fifo_map when resetting core.
usb: dwc2: Allow exiting hibernation from gpwrdn rst detect
usb: dwc2: Fix hibernation between host and device modes.
usb: dwc2: Fix host mode hibernation exit with remote wakeup flow.
usb: dwc2: Reset DEVADDR after exiting gadget hibernation.
usb: dwc2: Update exit hibernation when port reset is asserted
...
Diffstat (limited to 'drivers/usb/host/xhci-hub.c')
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 320 |
1 files changed, 203 insertions, 117 deletions
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 74c497fd3476..e9b18fc17617 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -11,6 +11,7 @@ #include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/bitfield.h> #include "xhci.h" #include "xhci-trace.h" @@ -19,151 +20,236 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -/* USB 3 BOS descriptor and a capability descriptors, combined. - * Fields will be adjusted and added later in xhci_create_usb3_bos_desc() - */ -static u8 usb_bos_descriptor [] = { - USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ - USB_DT_BOS, /* __u8 bDescriptorType */ - 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ - 0x1, /* __u8 bNumDeviceCaps */ - /* First device capability, SuperSpeed */ - USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ - USB_DT_DEVICE_CAPABILITY, /* Device Capability */ - USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ - 0x00, /* bmAttributes, LTM off by default */ - USB_5GBPS_OPERATION, 0x00, /* wSpeedsSupported, 5Gbps only */ - 0x03, /* bFunctionalitySupport, - USB 3.0 speed only */ - 0x00, /* bU1DevExitLat, set later. */ - 0x00, 0x00, /* __le16 bU2DevExitLat, set later. */ - /* Second device capability, SuperSpeedPlus */ - 0x1c, /* bLength 28, will be adjusted later */ - USB_DT_DEVICE_CAPABILITY, /* Device Capability */ - USB_SSP_CAP_TYPE, /* bDevCapabilityType SUPERSPEED_PLUS */ - 0x00, /* bReserved 0 */ - 0x23, 0x00, 0x00, 0x00, /* bmAttributes, SSAC=3 SSIC=1 */ - 0x01, 0x00, /* wFunctionalitySupport */ - 0x00, 0x00, /* wReserved 0 */ - /* Default Sublink Speed Attributes, overwrite if custom PSI exists */ - 0x34, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, rx, ID = 4 */ - 0xb4, 0x00, 0x05, 0x00, /* 5Gbps, symmetric, tx, ID = 4 */ - 0x35, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, rx, ID = 5 */ - 0xb5, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, tx, ID = 5 */ +/* Default sublink speed attribute of each lane */ +static u32 ssp_cap_default_ssa[] = { + 0x00050034, /* USB 3.0 SS Gen1x1 id:4 symmetric rx 5Gbps */ + 0x000500b4, /* USB 3.0 SS Gen1x1 id:4 symmetric tx 5Gbps */ + 0x000a4035, /* USB 3.1 SSP Gen2x1 id:5 symmetric rx 10Gbps */ + 0x000a40b5, /* USB 3.1 SSP Gen2x1 id:5 symmetric tx 10Gbps */ + 0x00054036, /* USB 3.2 SSP Gen1x2 id:6 symmetric rx 5Gbps */ + 0x000540b6, /* USB 3.2 SSP Gen1x2 id:6 symmetric tx 5Gbps */ + 0x000a4037, /* USB 3.2 SSP Gen2x2 id:7 symmetric rx 10Gbps */ + 0x000a40b7, /* USB 3.2 SSP Gen2x2 id:7 symmetric tx 10Gbps */ }; -static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, - u16 wLength) +static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf, + u16 wLength) { - struct xhci_port_cap *port_cap = NULL; - int i, ssa_count; - u32 temp; - u16 desc_size, ssp_cap_size, ssa_size = 0; - bool usb3_1 = false; - - desc_size = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; - ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; - - /* does xhci support USB 3.1 Enhanced SuperSpeed */ + struct usb_bos_descriptor *bos; + struct usb_ss_cap_descriptor *ss_cap; + struct usb_ssp_cap_descriptor *ssp_cap; + struct xhci_port_cap *port_cap = NULL; + u16 bcdUSB; + u32 reg; + u32 min_rate = 0; + u8 min_ssid; + u8 ssac; + u8 ssic; + int offset; + int i; + + /* BOS descriptor */ + bos = (struct usb_bos_descriptor *)buf; + bos->bLength = USB_DT_BOS_SIZE; + bos->bDescriptorType = USB_DT_BOS; + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE); + bos->bNumDeviceCaps = 1; + + /* Create the descriptor for port with the highest revision */ for (i = 0; i < xhci->num_port_caps; i++) { - if (xhci->port_caps[i].maj_rev == 0x03 && - xhci->port_caps[i].min_rev >= 0x01) { - usb3_1 = true; + u8 major = xhci->port_caps[i].maj_rev; + u8 minor = xhci->port_caps[i].min_rev; + u16 rev = (major << 8) | minor; + + if (i == 0 || bcdUSB < rev) { + bcdUSB = rev; port_cap = &xhci->port_caps[i]; - break; } } - if (usb3_1) { - /* does xhci provide a PSI table for SSA speed attributes? */ + if (bcdUSB >= 0x0310) { if (port_cap->psi_count) { - /* two SSA entries for each unique PSI ID, RX and TX */ - ssa_count = port_cap->psi_uid_count * 2; - ssa_size = ssa_count * sizeof(u32); - ssp_cap_size -= 16; /* skip copying the default SSA */ + u8 num_sym_ssa = 0; + + for (i = 0; i < port_cap->psi_count; i++) { + if ((port_cap->psi[i] & PLT_MASK) == PLT_SYM) + num_sym_ssa++; + } + + ssac = port_cap->psi_count + num_sym_ssa - 1; + ssic = port_cap->psi_uid_count - 1; + } else { + if (bcdUSB >= 0x0320) + ssac = 7; + else + ssac = 3; + + ssic = (ssac + 1) / 2 - 1; } - desc_size += ssp_cap_size; - } - memcpy(buf, &usb_bos_descriptor, min(desc_size, wLength)); - if (usb3_1) { - /* modify bos descriptor bNumDeviceCaps and wTotalLength */ - buf[4] += 1; - put_unaligned_le16(desc_size + ssa_size, &buf[2]); + bos->bNumDeviceCaps++; + bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE + + USB_DT_USB_SSP_CAP_SIZE(ssac)); } if (wLength < USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE) return wLength; - /* Indicate whether the host has LTM support. */ - temp = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(temp)) - buf[8] |= USB_LTM_SUPPORT; + /* SuperSpeed USB Device Capability */ + ss_cap = (struct usb_ss_cap_descriptor *)&buf[USB_DT_BOS_SIZE]; + ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE; + ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE; + ss_cap->bmAttributes = 0; /* set later */ + ss_cap->wSpeedSupported = cpu_to_le16(USB_5GBPS_OPERATION); + ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION; + ss_cap->bU1devExitLat = 0; /* set later */ + ss_cap->bU2DevExitLat = 0; /* set later */ + + reg = readl(&xhci->cap_regs->hcc_params); + if (HCC_LTC(reg)) + ss_cap->bmAttributes |= USB_LTM_SUPPORT; - /* Set the U1 and U2 exit latencies. */ if ((xhci->quirks & XHCI_LPM_SUPPORT)) { - temp = readl(&xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + reg = readl(&xhci->cap_regs->hcs_params3); + ss_cap->bU1devExitLat = HCS_U1_LATENCY(reg); + ss_cap->bU2DevExitLat = cpu_to_le16(HCS_U2_LATENCY(reg)); } - /* If PSI table exists, add the custom speed attributes from it */ - if (usb3_1 && port_cap->psi_count) { - u32 ssp_cap_base, bm_attrib, psi, psi_mant, psi_exp; - int offset; + if (wLength < le16_to_cpu(bos->wTotalLength)) + return wLength; - ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + if (bcdUSB < 0x0310) + return le16_to_cpu(bos->wTotalLength); + + ssp_cap = (struct usb_ssp_cap_descriptor *)&buf[USB_DT_BOS_SIZE + + USB_DT_USB_SS_CAP_SIZE]; + ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(ssac); + ssp_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY; + ssp_cap->bDevCapabilityType = USB_SSP_CAP_TYPE; + ssp_cap->bReserved = 0; + ssp_cap->wReserved = 0; + ssp_cap->bmAttributes = + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic)); + + if (!port_cap->psi_count) { + for (i = 0; i < ssac + 1; i++) + ssp_cap->bmSublinkSpeedAttr[i] = + cpu_to_le32(ssp_cap_default_ssa[i]); + + min_ssid = 4; + goto out; + } - if (wLength < desc_size) - return wLength; - buf[ssp_cap_base] = ssp_cap_size + ssa_size; + offset = 0; + for (i = 0; i < port_cap->psi_count; i++) { + u32 psi; + u32 attr; + u8 ssid; + u8 lp; + u8 lse; + u8 psie; + u16 lane_mantissa; + u16 psim; + u16 plt; + + psi = port_cap->psi[i]; + ssid = XHCI_EXT_PORT_PSIV(psi); + lp = XHCI_EXT_PORT_LP(psi); + psie = XHCI_EXT_PORT_PSIE(psi); + psim = XHCI_EXT_PORT_PSIM(psi); + plt = psi & PLT_MASK; + + lse = psie; + lane_mantissa = psim; + + /* Shift to Gbps and set SSP Link Protocol if 10Gpbs */ + for (; psie < USB_SSP_SUBLINK_SPEED_LSE_GBPS; psie++) + psim /= 1000; + + if (!min_rate || psim < min_rate) { + min_ssid = ssid; + min_rate = psim; + } - /* attribute count SSAC bits 4:0 and ID count SSIC bits 8:5 */ - bm_attrib = (ssa_count - 1) & 0x1f; - bm_attrib |= (port_cap->psi_uid_count - 1) << 5; - put_unaligned_le32(bm_attrib, &buf[ssp_cap_base + 4]); + /* Some host controllers don't set the link protocol for SSP */ + if (psim >= 10) + lp = USB_SSP_SUBLINK_SPEED_LP_SSP; - if (wLength < desc_size + ssa_size) - return wLength; /* - * Create the Sublink Speed Attributes (SSA) array. - * The xhci PSI field and USB 3.1 SSA fields are very similar, - * but link type bits 7:6 differ for values 01b and 10b. - * xhci has also only one PSI entry for a symmetric link when - * USB 3.1 requires two SSA entries (RX and TX) for every link + * PSIM and PSIE represent the total speed of PSI. The BOS + * descriptor SSP sublink speed attribute lane mantissa + * describes the lane speed. E.g. PSIM and PSIE for gen2x2 + * is 20Gbps, but the BOS descriptor lane speed mantissa is + * 10Gbps. Check and modify the mantissa value to match the + * lane speed. */ - offset = desc_size; - for (i = 0; i < port_cap->psi_count; i++) { - psi = port_cap->psi[i]; - psi &= ~USB_SSP_SUBLINK_SPEED_RSVD; - psi_exp = XHCI_EXT_PORT_PSIE(psi); - psi_mant = XHCI_EXT_PORT_PSIM(psi); - - /* Shift to Gbps and set SSP Link BIT(14) if 10Gpbs */ - for (; psi_exp < 3; psi_exp++) - psi_mant /= 1000; - if (psi_mant >= 10) - psi |= BIT(14); - - if ((psi & PLT_MASK) == PLT_SYM) { - /* Symmetric, create SSA RX and TX from one PSI entry */ - put_unaligned_le32(psi, &buf[offset]); - psi |= 1 << 7; /* turn entry to TX */ - offset += 4; - if (offset >= desc_size + ssa_size) - return desc_size + ssa_size; - } else if ((psi & PLT_MASK) == PLT_ASYM_RX) { - /* Asymetric RX, flip bits 7:6 for SSA */ - psi ^= PLT_MASK; + if (bcdUSB == 0x0320 && plt == PLT_SYM) { + /* + * The PSI dword for gen1x2 and gen2x1 share the same + * values. But the lane speed for gen1x2 is 5Gbps while + * gen2x1 is 10Gbps. If the previous PSI dword SSID is + * 5 and the PSIE and PSIM match with SSID 6, let's + * assume that the controller follows the default speed + * id with SSID 6 for gen1x2. + */ + if (ssid == 6 && psie == 3 && psim == 10 && i) { + u32 prev = port_cap->psi[i - 1]; + + if ((prev & PLT_MASK) == PLT_SYM && + XHCI_EXT_PORT_PSIV(prev) == 5 && + XHCI_EXT_PORT_PSIE(prev) == 3 && + XHCI_EXT_PORT_PSIM(prev) == 10) { + lse = USB_SSP_SUBLINK_SPEED_LSE_GBPS; + lane_mantissa = 5; + } + } + + if (psie == 3 && psim > 10) { + lse = USB_SSP_SUBLINK_SPEED_LSE_GBPS; + lane_mantissa = 10; } - put_unaligned_le32(psi, &buf[offset]); - offset += 4; - if (offset >= desc_size + ssa_size) - return desc_size + ssa_size; + } + + attr = (FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ssid) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, lp) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, lse) | + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, lane_mantissa)); + + switch (plt) { + case PLT_SYM: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_SYM_RX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + + attr &= ~USB_SSP_SUBLINK_SPEED_ST; + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_SYM_TX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; + case PLT_ASYM_RX: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_ASYM_RX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; + case PLT_ASYM_TX: + attr |= FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, + USB_SSP_SUBLINK_SPEED_ST_ASYM_TX); + ssp_cap->bmSublinkSpeedAttr[offset++] = cpu_to_le32(attr); + break; } } - /* ssa_size is 0 for other than usb 3.1 hosts */ - return desc_size + ssa_size; +out: + ssp_cap->wFunctionalitySupport = + cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, + min_ssid) | + FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) | + FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1)); + + return le16_to_cpu(bos->wTotalLength); } static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, @@ -1137,7 +1223,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (hcd->speed < HCD_USB3) goto error; - retval = xhci_create_usb3_bos_desc(xhci, buf, wLength); + retval = xhci_create_usb3x_bos_desc(xhci, buf, wLength); spin_unlock_irqrestore(&xhci->lock, flags); return retval; case GetPortStatus: |