in host/xhci-hub.c [35:253]
static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,
u16 wLength)
{
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++) {
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];
}
}
if (bcdUSB >= 0x0310) {
if (port_cap->psi_count) {
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;
}
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;
/* 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;
if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
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 (wLength < le16_to_cpu(bos->wTotalLength))
return wLength;
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;
}
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;
}
/* Some host controllers don't set the link protocol for SSP */
if (psim >= 10)
lp = USB_SSP_SUBLINK_SPEED_LP_SSP;
/*
* 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.
*/
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;
}
}
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;
}
}
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);
}