Mathias Nyman wrote: > Hi > > On 2.2.2021 5.42, Thinh Nguyen wrote: >> The current xhci_create_usb3_bos_desc() uses a static bos structure and >> various magic numbers and offset making it difficult to extend support >> for USB 3.2. Let's rewrite this entire function to support dual-lane in >> USB 3.2. > Agree, it's time to get rid of the static u8 array used for bos. > >> The hub driver matches the port speed ID from the extended port status >> to the SSID of the sublink speed attributes to detect if the device >> supports SuperSpeed Plus. Currently we don't provide the default gen1x2 >> and gen2x2 sublink speed capability descriptor for USB 3.2 roothub. The >> USB stack depends on this to detect and match the correct speed. >> In addition, if the xHCI host provides Protocol Speed ID (PSI) >> capability, then make sure to convert Protocol Speed ID Mantissa and >> Exponent (PSIM & PSIE) to lane speed for gen1x2 and gen2x2. >> >> Signed-off-by: Thinh Nguyen <Thinh.Nguyen@xxxxxxxxxxxx> >> --- >> drivers/usb/host/xhci-hub.c | 279 +++++++++++++++++++++++++++++++++++- >> 1 file changed, 277 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c >> index 74c497fd3476..c095c30212e5 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.ch" >> #include "xhci-trace.h" >> @@ -52,7 +53,281 @@ static u8 usb_bos_descriptor [] = { >> 0xb5, 0x40, 0x0a, 0x00, /* 10Gbps, SSP, symmetric, tx, ID = 5 */ >> }; >> >> -static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, >> +static int xhci_fill_default_ssp_attr(struct xhci_hcd *xhci, >> + struct usb_ssp_cap_descriptor *ssp_cap) >> +{ >> + u32 attr; >> + u8 ssac; >> + int i; >> + >> + attr = le32_to_cpu(ssp_cap->bmAttributes); >> + ssac = FIELD_GET(USB_SSP_SUBLINK_SPEED_ATTRIBS, attr); >> + >> + /* Currently we only support USB 3.1 and 3.2 */ >> + if (ssac != 3 && ssac != 7) >> + return -EINVAL; >> + >> + /* >> + * Map default xHCI port speed ID to SSID: >> + * SSID 4 = Symmetric SSP Gen1x1 >> + * SSID 5 = Symmetric SSP Gen2x1 >> + * SSID 6 = Symmetric SSP Gen1x2 >> + * SSID 7 = Symmetric SSP Gen2x2 >> + */ >> + for (i = 0; i < ssac + 1; i++) { >> + u8 ssid; >> + u8 type; >> + u8 lp; >> + u16 mantissa; >> + >> + ssid = (i >> 1) + 4; >> + >> + if (ssid > 4) >> + lp = USB_SSP_SUBLINK_SPEED_LP_SSP; >> + else >> + lp = USB_SSP_SUBLINK_SPEED_LP_SS; >> + >> + if (ssid == 5 || ssid == 7) >> + mantissa = 10; >> + else >> + mantissa = 5; >> + >> + if (i % 2) >> + type = USB_SSP_SUBLINK_SPEED_ST_SYM_TX; >> + else >> + type = USB_SSP_SUBLINK_SPEED_ST_SYM_RX; >> + >> + ssp_cap->bmSublinkSpeedAttr[i] = >> + cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, >> + ssid) | >> + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, >> + USB_SSP_SUBLINK_SPEED_LSE_GBPS) | >> + FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, type) | >> + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, lp) | >> + FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, >> + mantissa)); >> + } >> + >> + return 0; >> +} > I don't think it makes sense to generates the default sublinkSpeedAttr[] entries like > the function above does. > The content is static and defined in xhci spec. > For these I'd just make a static u32 array. (minding endianness) > > static u32 ssp_cap_default_ssa[] = { > 0xaaaaaaaa, /* USB 3.0 SS 5Gbps Id:4, rx */ > 0xbbbbbbbb, /* USB 3.0 SS 5Gbps Id:4, tx */ > 0xcccccccc, /* USB 3.1 SSP 10Gbps Id:5 rx */ > 0xdddddddd, /* USB 3.1 SSP 10Gbps Id:5, tx */ > 0xeeeeeeee, /* USB 3.2 Gen1x2 10Gbps Id:6, rx */ > ... > } > > Then when creating the ssp cap descriptor set the attribute count (ssac) based on > bcdUSB as you do, and assign the default values if custom ones aren't given (!psi_count). > > for (i = 0; i < ssac + 1; i++) > ssp_cap->mbSublinkSpeedAttr[i] = ssp_capdefault_ssa[i]; That looks good. I'll revise it. >> + >> +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; > This is much better than the current way we fill the descriptor, much more readable. > >> + >> + /* 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) { >> + /* Two SSA entries for each unique PSI ID, RX and TX */ >> + if (port_cap->psi_count) >> + ssac = port_cap->psi_uid_count * 2 - 1; >> + else if (bcdUSB == 0x0320) > maybe (bcdUSB >= 0x320), allows old driver to handle future hardware. Ok. > >> + ssac = 7; >> + else >> + ssac = 3; >> + >> + if (port_cap->psi_count) >> + ssic = port_cap->psi_count - 1; >> + else >> + 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->mbSublinkSpeedAttr[i] = ssp_cap_default_ssa[i]; //mind endianness > >> + int ret; >> + >> + ret = xhci_fill_default_ssp_attr(xhci, ssp_cap); >> + if (ret) >> + return ret; >> + >> + min_ssid = 4; >> + goto out; >> + } >> + >> + offset = 0; >> + for (i = 0; i < port_cap->psi_count; i++) { > I need to take a better look at this sublinkSpeedAttr[] creation from custom PSIC table a bit later. > > Thanks > -Mathias Thanks for the review! Thinh