usb: Check for genXxY on host
Subsystems (2)
v2 cover usb: Check for genXxY on host
This series add some missing support for USB 3.2 SuperSpeed Plus detection on the host side. A SuperSpeed Plus device can operate in gen2x2, gen2x1, or gen1x2. The current implementation can't detect whether the device is in Gen 1 or Gen 2 speed. We can do this by matching for the lane speed exponent and mantissa of the SSP sublink speed capability descriptor from the hub driver.
Also, the current xHCI driver is missing some reports for the default SSP Sublink Speed capability for USB 3.2 roothub. This series also add some support for xHCI driver detecting various SuperSpeed Plus GenXxY.
Changes in v2:
- Remove RFC tag
- Rebase on greg/usb-testing
- Make some updates on BOS descriptor creation base on Mathias suggestions
Thinh Nguyen (8):
usb: core: Track SuperSpeed Plus GenXxY
usb: core: hub: Remove port_speed_is_ssp()
usb: core: hub: Print speed name based on ssp rate
usb: core: sysfs: Check for SSP rate in speed attr
usb: xhci: Init root hub SSP rate
usb: xhci: Fix port minor revision
usb: xhci: Rewrite xhci_create_usb3_bos_desc()
usb: xhci: Remove unused function
drivers/usb/core/hcd.c | 6 +- drivers/usb/core/hub.c | 97 ++++++++--- drivers/usb/core/sysfs.c | 5 +- drivers/usb/host/xhci-hub.c | 320 +++++++++++++++++++++++------------- drivers/usb/host/xhci-mem.c | 9 + drivers/usb/host/xhci.c | 2 + include/linux/usb.h | 2 + 7 files changed, 300 insertions(+), 141 deletions(-)
base-commit: d021e0694d77ee3cdc5d3fca2c8d53ae7575499a
Signature · 1 line
2.28.0
PATCH v2 1/8 usb: core: Track SuperSpeed Plus GenXxY
Introduce ssp_rate field to usb_device structure to capture the connected SuperSpeed Plus signaling rate generation and lane count with the corresponding usb_ssp_rate enum.
---
Changes in v2:
- None
drivers/usb/core/hcd.c | 6 +++- drivers/usb/core/hub.c | 78 ++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 2 ++ 3 files changed, 85 insertions(+), 1 deletion(-)
drivers/usb/core/hcd.c +5 -1
@@ -2721,6 +2721,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->rx_lanes =1 ; rhdev->tx_lanes =1 ; + rhdev->ssp_rate =USB_SSP_GEN_UNKNOWN ;switch (hcd->speed) {case HCD_USB11 :
@@ -2738,8 +2739,11 @@ int usb_add_hcd(struct usb_hcd *hcd,case HCD_USB32 : rhdev->rx_lanes =2 ; rhdev->tx_lanes =2 ; - fallthrough; + rhdev->ssp_rate = USB_SSP_GEN_2x2; + rhdev->speed =USB_SPEED_SUPER_PLUS ; +break ;case HCD_USB31 : + rhdev->ssp_rate = USB_SSP_GEN_2x1; rhdev->speed =USB_SPEED_SUPER_PLUS ;break ;default :
drivers/usb/core/hub.c +78 -0
99 lines not on this page · raw message
include/linux/usb.h +2 -0
@@ -560,6 +560,7 @@ struct usb3_lpm_parameters {* @speed: device speed: high/full/low (or error) * @rx_lanes: number of rx lanes in use, USB 3.2 adds dual-lane support * @tx_lanes: number of tx lanes in use, USB 3.2 adds dual-lane support +* @ssp_rate: SuperSpeed Plus phy signaling rate and lane count * @tt: Transaction Translator info; used with low/full speed dev, highspeed hub * @ttport: device port on that tt hub * @toggle: one bit for each endpoint, with ([0] = IN, [1] = OUT) endpoints
@@ -636,6 +637,7 @@ struct usb_device {enum usb_device_speed speed;unsigned int rx_lanes;unsigned int tx_lanes; +enum usb_ssp_rate ssp_rate;struct usb_tt *tt;int ttport;
Signature · 1 line
2.28.0
PATCH v2 2/8 usb: core: hub: Remove port_speed_is_ssp()
In reply to Thinh NguyenThe get_port_ssp_rate() can replace port_speed_is_ssp(). If the port speed is detected to be in gen2x1, gen1x2, or gen2x2, then the port is operating at SuperSpeed Plus.
---
Changes in v2:
- None
drivers/usb/core/hub.c | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-)
drivers/usb/core/hub.c +1 -30
@@ -2744,33 +2744,6 @@ static enum usb_ssp_rate get_port_ssp_rate(struct usb_device *hdev,return USB_SSP_GEN_UNKNOWN ; } -/* -* Return 1 if port speed is SuperSpeedPlus, 0 otherwise -* check it from the link protocol field of the current speed ID attribute. -* current speed ID is got from ext port status request. Sublink speed attribute -* table is returned with the hub BOS SSP device capability descriptor -*/ -static int port_speed_is_ssp (struct usb_device *hdev,int speed_id) -{ -int ssa_count; -u32 ss_attr; -int i; -struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap; - -if (!ssp_cap) -return 0 ; - - ssa_count =le32_to_cpu (ssp_cap->bmAttributes) & -USB_SSP_SUBLINK_SPEED_ATTRIBS ; - -for (i =0 ; i <= ssa_count; i++) { - ss_attr =le32_to_cpu (ssp_cap->bmSublinkSpeedAttr[i]); -if (speed_id == (ss_attr &USB_SSP_SUBLINK_SPEED_SSID )) -return !!(ss_attr &USB_SSP_SUBLINK_SPEED_LP ); - } -return 0 ; -} -/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ static unsigned hub_is_wusb (struct usb_hub *hub) {
@@ -2934,9 +2907,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, }if (hub_is_wusb (hub)) udev->speed =USB_SPEED_WIRELESS ; -else if (hub_is_superspeedplus (hub->hdev) && -port_speed_is_ssp (hub->hdev, ext_portstatus & -USB_EXT_PORT_STAT_RX_SPEED_ID )) +else if (udev->ssp_rate !=USB_SSP_GEN_UNKNOWN ) udev->speed =USB_SPEED_SUPER_PLUS ;else if (hub_is_superspeed (hub->hdev)) udev->speed =USB_SPEED_SUPER ;
Signature · 1 line
2.28.0
PATCH v2 3/8 usb: core: hub: Print speed name based on ssp rate
In reply to Thinh NguyenCheck for usb_device->ssp_rate to print the SuperSpeed Plus signaling rate generation and lane count.
---
Changes in v2:
- None
drivers/usb/core/hub.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
drivers/usb/core/hub.c +7 -3
@@ -4830,9 +4830,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,"%s SuperSpeed%s%s USB device number %d using %s\n" , (udev->config) ?"reset" :"new" , (udev->speed ==USB_SPEED_SUPER_PLUS ) ? -"Plus Gen 2" :" Gen 1" , - (udev->rx_lanes ==2 && udev->tx_lanes ==2 ) ? -"x2" :"" , +" Plus" :"" , + (udev->ssp_rate == USB_SSP_GEN_2x2) ? +" gen2x2" : + (udev->ssp_rate == USB_SSP_GEN_2x1) ? +" gen2x1" : + (udev->ssp_rate == USB_SSP_GEN_1x2) ? +" gen1x2" :"" , devnum, driver_name); }
Signature · 1 line
2.28.0
Re: [PATCH v2 3/8] usb: core: hub: Print speed name based on ssp rate
Quoted 19 lines · Thinh Nguyen
Check for
usb_device->ssp_rateto print the SuperSpeed Plus signaling rate generation and lane count.Signed-off-by Thinh Nguyen <[email protected]>---
Changes in v2:
- Nonedrivers/usb/core/hub.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)drivers/usb/core/hub.c +0
-0@@ -4830,9 +4830,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,"%s SuperSpeed%s%s USB device number %d using %s\n" , (udev->config) ?"reset" :"new" , (udev->speed == USB_SPEED_SUPER_PLUS) ?
drivers/usb/core/hub.c hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, · line 4839
Quoted 10 lines
drivers/usb/core/hub.c +7
-3- "Plus Gen 2" : " Gen 1", - (udev->rx_lanes ==2 && udev->tx_lanes ==2 ) ? - "x2" : "", + " Plus" : "", + (udev->ssp_rate == USB_SSP_GEN_2x2) ? +" gen2x2" : + (udev->ssp_rate == USB_SSP_GEN_2x1) ? +" gen2x1" : + (udev->ssp_rate == USB_SSP_GEN_1x2) ? + " gen1x2" : "",
Minor nit:
Should we keep the "Gen XxY" format used in spec and driver, instead of this "genXxY"
Thanks
Mathias
Re: [PATCH v2 3/8] usb: core: hub: Print speed name based on ssp rate
Quoted 24 lines · Mathias Nyman
Quoted 23 lines · Thinh Nguyen
Check for
usb_device->ssp_rateto print the SuperSpeed Plus signaling rate generation and lane count.Signed-off-by Thinh Nguyen <[email protected]>---
Changes in v2:
- Nonedrivers/usb/core/hub.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)drivers/usb/core/hub.c +1
-3@@ -4830,9 +4830,13 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,"%s SuperSpeed%s%s USB device number %d using %s\n" , (udev->config) ?"reset" :"new" , (udev->speed ==USB_SPEED_SUPER_PLUS ) ? -"Plus Gen 2" :" Gen 1" , - (udev->rx_lanes ==2 && udev->tx_lanes ==2 ) ? - "x2" : "", + " Plus" : "",
Quoted 9 lines
drivers/usb/core/hub.c hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, · line 4839
Quoted 6 lines
drivers/usb/core/hub.c +6
-0+ (udev->ssp_rate == USB_SSP_GEN_2x2) ? +" gen2x2" : + (udev->ssp_rate == USB_SSP_GEN_2x1) ? +" gen2x1" : + (udev->ssp_rate == USB_SSP_GEN_1x2) ? + " gen1x2" : "",Minor nit:
Should we keep the "Gen XxY" format used in spec and driver, instead of this "genXxY"
Ok. I've made the fix.
Thanks,
Thinh
PATCH v2 4/8 usb: core: sysfs: Check for SSP rate in speed attr
In reply to Thinh NguyenCheck for usb_device->ssp_rate to output the signaling rate for genXxY.
---
Changes in v2:
- None
drivers/usb/core/sysfs.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
drivers/usb/core/sysfs.c +4 -1
@@ -167,7 +167,10 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, speed ="5000" ;break ;case USB_SPEED_SUPER_PLUS : - speed ="10000" ; +if (udev->ssp_rate == USB_SSP_GEN_2x2) + speed ="20000" ; +else + speed ="10000" ;break ;default : speed ="unknown" ;
Signature · 1 line
2.28.0
PATCH v2 5/8 usb: xhci: Init root hub SSP rate
In reply to Thinh NguyenInitialize USB 3.x root hub SuperSpeed Plus rate.
---
Changes in v2:
- None
drivers/usb/host/xhci.c | 2 ++ 1 file changed, 2 insertions(+)
drivers/usb/host/xhci.c +2 -0
@@ -5226,10 +5226,12 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) hcd->self.root_hub->speed =USB_SPEED_SUPER_PLUS ; hcd->self.root_hub->rx_lanes =2 ; hcd->self.root_hub->tx_lanes =2 ; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2;break ;case 1 : hcd->speed =HCD_USB31 ; hcd->self.root_hub->speed =USB_SPEED_SUPER_PLUS ; + hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1;break ; } xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n",
Signature · 1 line
2.28.0
PATCH v2 6/8 usb: xhci: Fix port minor revision
In reply to Thinh NguyenSome hosts incorrectly use sub-minor version for minor version (i.e. 0x02 instead of 0x20 for bcdUSB 0x320 and 0x01 for bcdUSB 0x310). Currently the xHCI driver works around this by just checking for minor revision > 0x01 for USB 3.1 everywhere. With the addition of USB 3.2, checking this gets a bit cumbersome. Since there is no USB release with bcdUSB 0x301 to 0x309, we can assume that sub-minor version 01 to 09 is incorrect. Let's try to fix this and use the minor revision that matches with the USB/xHCI spec to help with the version checking within the driver.
---
Changes in v2:
- Use <<= when shift and assign value instead
drivers/usb/host/xhci-mem.c | 9 +++++++++ 1 file changed, 9 insertions(+)
drivers/usb/host/xhci-mem.c +9 -0
@@ -2129,6 +2129,15 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports,if (major_revision ==0x03 ) { rhub = &xhci->usb3_rhub; +/* +* Some hosts incorrectly use sub-minor version for minor +* version (i.e. 0x02 instead of 0x20 for bcdUSB 0x320 and 0x01 +* for bcdUSB 0x310). Since there is no USB release with sub +* minor version 0x301 to 0x309, we can assume that they are +* incorrect and fix it here. +*/ +if (!(minor_revision &0xf0 ) && (minor_revision &0x0f )) + minor_revision <<=4 ; }else if (major_revision <=0x02 ) { rhub = &xhci->usb2_rhub; }else {
Signature · 1 line
2.28.0
PATCH v2 7/8 usb: xhci: Rewrite xhci_create_usb3_bos_desc()
In reply to Thinh NguyenThe current xhci_create_usb3_bos_desc() uses a static bos u8 array and various magic numbers and offsets making it difficult to extend support for USB 3.2. Let's rewrite this entire function to support dual-lane in USB 3.2.
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.
---
Changes in v2:
- Use static array for SSA per Mathias suggestion
- Check bcdUSB >= 0x320 instead of bcdUSB == 0x320 per Mathias suggestion
- When host uses custom PSI, SSIC is the psi_uid_count and not SSAC. I missed this when transferring the previous logic over. Previous implementation was incorrect. Let's fix it here.
drivers/usb/host/xhci-hub.c | 237 +++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-)
drivers/usb/host/xhci-hub.c +235 -2
@@ -11,6 +11,7 @@#include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/bitfield.h> #include "xhci.h" #include "xhci-trace.h"
@@ -52,7 +53,239 @@ 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, +/* 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_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); +} + +static __maybe_unused int xhci_create_usb3_bos_desc (struct xhci_hcd *xhci,char *buf,u16 wLength) {struct xhci_port_cap *port_cap =NULL ;
@@ -1137,7 +1370,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:
Signature · 1 line
2.28.0
Re: [PATCH v2 7/8] usb: xhci: Rewrite xhci_create_usb3_bos_desc()
Quoted 191 lines · Thinh Nguyen
The current
xhci_create_usb3_bos_desc()uses a static bos u8 array and various magic numbers and offsets making it difficult to extend support for USB 3.2. Let's rewrite this entire function to support dual-lane in USB 3.2.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 <[email protected]>---
Changes in v2:
- Use static array for SSA per Mathias suggestion
- Check bcdUSB >=0x320instead of bcdUSB ==0x320per Mathias suggestion
- When host uses custom PSI, SSIC is thepsi_uid_countand not SSAC. I missed this when transferring the previous logic over. Previous implementation was incorrect. Let's fix it here.drivers/usb/host/xhci-hub.c | 237 +++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-)drivers/usb/host/xhci-hub.c +149
-1@@ -11,6 +11,7 @@#include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/bitfield.h> #include "xhci.h" #include "xhci-trace.h" @@ -52,7 +53,239 @@ 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, +/* 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_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++)
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 213
Quoted 10 lines
drivers/usb/host/xhci-hub.c +10
-0+ 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 ;
Maybe we should limit the above fix to older than USB 3.2 hosts. xHCI supporting Gen 1x2 can in theory have psim==10 even if it only supports SS link protocol, not SSP.
I'd guess newer USB 3.2 hosts have have fixed the LP bit.
Quoted 17 lines
drivers/usb/host/xhci-hub.c +17
-0+ +/* +* 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.
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 240
Quoted 10 lines
drivers/usb/host/xhci-hub.c +10
-0+*/ +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 ;
This is a very specific workaround designed for one default case.
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 247
Quoted 7 lines
drivers/usb/host/xhci-hub.c +7
-0+ } + } + +if (psie ==3 && psim >10 ) { + lse =USB_SSP_SUBLINK_SPEED_LSE_GBPS ; + lane_mantissa =10 ; + }
So is this.
Maybe something more generic could be figured out.
If we assume that bcdUSB >= 0x0320 cases have the Link Protocol (LP) bit is set correctly, we could do something like:
if (bcdUSB >= 0x0320)
if (LP && psim > 10) || (!LP && (psim > 5))
psim = psim / 2 /* divide by lane count */
Not sure if above pseudocode works with SSIC cases
We are also forcing the speeds to Gbps (current code does this as well) This is rounding down non-standard speeds. (SSIC at 2915 Mbps for example)
Quoted 46 lines
drivers/usb/host/xhci-hub.c +41
-0+ } + + 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); +} + +static __maybe_unused int xhci_create_usb3_bos_desc (struct xhci_hcd *xhci,char *buf,u16 wLength) {struct xhci_port_cap *port_cap =NULL ;
drivers/usb/host/xhci-hub.c int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, · line 1376
Quoted 8 lines
drivers/usb/host/xhci-hub.c +1
-1@@ -1137,7 +1370,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:
This is anyways a lot better than what we currently have.
Would be nice to get those xHCI PSIM values nicely converted to USB 3.x bmSuperSpeedAttr Lane Speed Mantissa.
Otherwise everyting looks good
Thanks
-Mathias
Re: [PATCH v2 7/8] usb: xhci: Rewrite xhci_create_usb3_bos_desc()
Quoted 197 lines · Mathias Nyman
Quoted 196 lines · Thinh Nguyen
The current
xhci_create_usb3_bos_desc()uses a static bos u8 array and various magic numbers and offsets making it difficult to extend support for USB 3.2. Let's rewrite this entire function to support dual-lane in USB 3.2.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 <[email protected]>---
Changes in v2:
- Use static array for SSA per Mathias suggestion
- Check bcdUSB >=0x320instead of bcdUSB ==0x320per Mathias suggestion
- When host uses custom PSI, SSIC is thepsi_uid_countand not SSAC. I missed this when transferring the previous logic over. Previous implementation was incorrect. Let's fix it here.drivers/usb/host/xhci-hub.c | 237 +++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-)drivers/usb/host/xhci-hub.c +154
-1@@ -11,6 +11,7 @@#include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/bitfield.h> #include "xhci.h" #include "xhci-trace.h" @@ -52,7 +53,239 @@ 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, +/* 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_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;
Quoted 8 lines
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 213
Quoted 5 lines
drivers/usb/host/xhci-hub.c +5
-0+ } + +/* Some host controllers don't set the link protocol for SSP */ +if (psim >=10 ) + lp =USB_SSP_SUBLINK_SPEED_LP_SSP ;Maybe we should limit the above fix to older than USB 3.2 hosts. xHCI supporting Gen 1x2 can in theory have psim==10 even if it only supports SS link protocol, not SSP.
Gen 1x2 uses SuperSpeed Plus link protocol. See USB 3.2 spec section 3.2
Quoted 23 lines
I'd guess newer USB 3.2 hosts have have fixed the LP bit.
Quoted 20 lines
drivers/usb/host/xhci-hub.c +20
-0+ +/* +* 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 ];
Quoted 8 lines
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 240
Quoted 7 lines
drivers/usb/host/xhci-hub.c +7
-0+ +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 ;This is a very specific workaround designed for one default case.
Since we can't differentiate between gen1x2 or gen2x1 PSI dword, I want to the driver to assume by default that the PSI represents gen2x1 unless it falls into this very specific condition for gen1x2. It's not often that a device is connected in gen1x2.
Quoted 9 lines
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 247
Quoted 8 lines
drivers/usb/host/xhci-hub.c +7
-0+ } + } + +if (psie ==3 && psim >10 ) { + lse =USB_SSP_SUBLINK_SPEED_LSE_GBPS ; + lane_mantissa =10 ; + }So is this.
This case assumes psim > 10 is for gen2x2 and each lane is capable of up to 10Gbps. We may change this and set lane_mantissa = psim / 2. But it complicates the GenXxY description check a bit since it means that we're expecting some odd number between 5Gbps or 10Gbps.
Quoted 7 lines
Maybe something more generic could be figured out.
If we assume that bcdUSB >=0x0320cases have the Link Protocol (LP) bit is set correctly, we could do something like:if (bcdUSB >=
0x0320)
if (LP && psim > 10) || (!LP && (psim > 5))
psim = psim / 2 /* divide by lane count */
This won't work well because gen1x2 uses the same SSP link protocol, and we don't have lane count in PSI dword.
Quoted 5 lines
Not sure if above pseudocode works with SSIC cases
We are also forcing the speeds to Gbps (current code does this as well) This is rounding down non-standard speeds. (SSIC at 2915 Mbps for example)
We're only forcing the speed to Gbps for PSI dwords that we determined to be gen1x2 and gen2x2. We're not rounding down for others.
Quoted 59 lines
Quoted 47 lines
drivers/usb/host/xhci-hub.c +41
-0+ } + + 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); +} + +static __maybe_unused int xhci_create_usb3_bos_desc (struct xhci_hcd *xhci,char *buf,u16 wLength) {struct xhci_port_cap *port_cap =NULL ;drivers/usb/host/xhci-hub.c int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, · line 1376
Quoted 8 lines
drivers/usb/host/xhci-hub.c +1
-1@@ -1137,7 +1370,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:This is anyways a lot better than what we currently have.
Would be nice to get those xHCI PSIM values nicely converted to USB 3.x bmSuperSpeedAttr Lane Speed Mantissa.
Quoted 2 lines
Otherwise everyting looks good
Yes, it would be nice if the PSI dwords can just be converted to bmSublinkSpeedAttr. Thanks for the review.
BR,
Thinh
Re: [PATCH v2 7/8] usb: xhci: Rewrite xhci_create_usb3_bos_desc()
Quoted 207 lines · Thinh Nguyen
Quoted 197 lines · Mathias Nyman
Quoted 196 lines · Thinh Nguyen
The current
xhci_create_usb3_bos_desc()uses a static bos u8 array and various magic numbers and offsets making it difficult to extend support for USB 3.2. Let's rewrite this entire function to support dual-lane in USB 3.2.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 <[email protected]>---
Changes in v2:
- Use static array for SSA per Mathias suggestion
- Check bcdUSB >=0x320instead of bcdUSB ==0x320per Mathias suggestion
- When host uses custom PSI, SSIC is thepsi_uid_countand not SSAC. I missed this when transferring the previous logic over. Previous implementation was incorrect. Let's fix it here.drivers/usb/host/xhci-hub.c | 237 +++++++++++++++++++++++++++++++++++- 1 file changed, 235 insertions(+), 2 deletions(-)drivers/usb/host/xhci-hub.c +154
-1@@ -11,6 +11,7 @@#include <linux/slab.h> #include <asm/unaligned.h> +#include <linux/bitfield.h> #include "xhci.h" #include "xhci-trace.h" @@ -52,7 +53,239 @@ 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, +/* 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_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;Quoted 8 lines
drivers/usb/host/xhci-hub.c static u8 usb_bos_descriptor [] = { · line 213
Quoted 5 lines
drivers/usb/host/xhci-hub.c +5
-0+ } + +/* Some host controllers don't set the link protocol for SSP */ +if (psim >=10 ) + lp =USB_SSP_SUBLINK_SPEED_LP_SSP ;Maybe we should limit the above fix to older than USB 3.2 hosts. xHCI supporting Gen 1x2 can in theory have psim==10 even if it only supports SS link protocol, not SSP.
Quoted 2 lines
Gen 1x2 uses SuperSpeed Plus link protocol. See USB 3.2 spec section 3.2
Yes, you're right. I got so used to Gen 1 always being SS, and Gen 2 SSP in USB 3.1.
Then those specific workarounds you made also makes sense. From xhci POV this looks good.
Thanks
-Mathias
PATCH v2 8/8 usb: xhci: Remove unused function
In reply to Thinh NguyenNow that we replaced the xhci_create_usb3_bos_desc() function. We can remove it along with the static usb_bos_descriptor structure.
---
Changes in v2:
- None
drivers/usb/host/xhci-hub.c | 147 ------------------------------------ 1 file changed, 147 deletions(-)
drivers/usb/host/xhci-hub.c +0 -147
@@ -20,39 +20,6 @@#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 */
@@ -285,120 +252,6 @@ static int xhci_create_usb3x_bos_desc(struct xhci_hcd *xhci, char *buf,return le16_to_cpu (bos->wTotalLength); } -static __maybe_unused int xhci_create_usb3_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 */ -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; - port_cap = &xhci->port_caps[i]; -break ; - } - } - -if (usb3_1) { -/* does xhci provide a PSI table for SSA speed attributes? */ -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 */ - } - 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 ]); - } - -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 ; - -/* 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 ]); - } - -/* 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; - - ssp_cap_base =USB_DT_BOS_SIZE +USB_DT_USB_SS_CAP_SIZE ; - -if (wLength < desc_size) -return wLength; - buf[ssp_cap_base] = ssp_cap_size + ssa_size; - -/* 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 ]); - -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 -*/ - 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 ; - } -put_unaligned_le32 (psi, &buf[offset]); - offset +=4 ; -if (offset >= desc_size + ssa_size) -return desc_size + ssa_size; - } - } -/* ssa_size is 0 for other than usb 3.1 hosts */ -return desc_size + ssa_size; -} -static void xhci_common_hub_descriptor (struct xhci_hcd *xhci,struct usb_hub_descriptor *desc,int ports) {
Signature · 1 line
2.28.0