]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - drivers/usb/host/xhci-hub.c
Merge tag 'arm-defconfig-5.13' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[mirror_ubuntu-jammy-kernel.git] / drivers / usb / host / xhci-hub.c
index 74c497fd34762066bc7057f4a64366d01cfefb42..e9b18fc176172312f512ac7b110564185261eeeb 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/slab.h>
 #include <asm/unaligned.h>
+#include <linux/bitfield.h>
 
 #include "xhci.h"
 #include "xhci-trace.h"
 #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: