usb: core: Track SuperSpeed Plus GenXxY
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Thu, 11 Mar 2021 03:42:44 +0000 (19:42 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 23 Mar 2021 12:12:37 +0000 (13:12 +0100)
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.

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/b7805d121e5ae4ad5ae144bd860b6ac04ee47436.1615432770.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
include/linux/usb.h

index 3f0381344221e20980dea3adad15a3d7c4933319..6119fb41d73653848c067952f6bbafe031356b6b 100644 (file)
@@ -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:
index 7f71218cc1e54b0d577eb9bee233ebc2ac75ba6c..e78b2dd7801a60e03f0ae9a4b0c7ccee45d33cc1 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/pm_qos.h>
 #include <linux/kobject.h>
 
+#include <linux/bitfield.h>
 #include <linux/uaccess.h>
 #include <asm/byteorder.h>
 
@@ -2668,6 +2669,81 @@ out_authorized:
        return result;
 }
 
+/**
+ * get_port_ssp_rate - Match the extended port status to SSP rate
+ * @hdev: The hub device
+ * @ext_portstatus: extended port status
+ *
+ * Match the extended port status speed id to the SuperSpeed Plus sublink speed
+ * capability attributes. Base on the number of connected lanes and speed,
+ * return the corresponding enum usb_ssp_rate.
+ */
+static enum usb_ssp_rate get_port_ssp_rate(struct usb_device *hdev,
+                                          u32 ext_portstatus)
+{
+       struct usb_ssp_cap_descriptor *ssp_cap = hdev->bos->ssp_cap;
+       u32 attr;
+       u8 speed_id;
+       u8 ssac;
+       u8 lanes;
+       int i;
+
+       if (!ssp_cap)
+               goto out;
+
+       speed_id = ext_portstatus & USB_EXT_PORT_STAT_RX_SPEED_ID;
+       lanes = USB_EXT_PORT_RX_LANES(ext_portstatus) + 1;
+
+       ssac = le32_to_cpu(ssp_cap->bmAttributes) &
+               USB_SSP_SUBLINK_SPEED_ATTRIBS;
+
+       for (i = 0; i <= ssac; i++) {
+               u8 ssid;
+
+               attr = le32_to_cpu(ssp_cap->bmSublinkSpeedAttr[i]);
+               ssid = FIELD_GET(USB_SSP_SUBLINK_SPEED_SSID, attr);
+               if (speed_id == ssid) {
+                       u16 mantissa;
+                       u8 lse;
+                       u8 type;
+
+                       /*
+                        * Note: currently asymmetric lane types are only
+                        * applicable for SSIC operate in SuperSpeed protocol
+                        */
+                       type = FIELD_GET(USB_SSP_SUBLINK_SPEED_ST, attr);
+                       if (type == USB_SSP_SUBLINK_SPEED_ST_ASYM_RX ||
+                           type == USB_SSP_SUBLINK_SPEED_ST_ASYM_TX)
+                               goto out;
+
+                       if (FIELD_GET(USB_SSP_SUBLINK_SPEED_LP, attr) !=
+                           USB_SSP_SUBLINK_SPEED_LP_SSP)
+                               goto out;
+
+                       lse = FIELD_GET(USB_SSP_SUBLINK_SPEED_LSE, attr);
+                       mantissa = FIELD_GET(USB_SSP_SUBLINK_SPEED_LSM, attr);
+
+                       /* Convert to Gbps */
+                       for (; lse < USB_SSP_SUBLINK_SPEED_LSE_GBPS; lse++)
+                               mantissa /= 1000;
+
+                       if (mantissa >= 10 && lanes == 1)
+                               return USB_SSP_GEN_2x1;
+
+                       if (mantissa >= 10 && lanes == 2)
+                               return USB_SSP_GEN_2x2;
+
+                       if (mantissa >= 5 && lanes == 2)
+                               return USB_SSP_GEN_1x2;
+
+                       goto out;
+               }
+       }
+
+out:
+       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.
@@ -2850,9 +2926,11 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
                /* extended portstatus Rx and Tx lane count are zero based */
                udev->rx_lanes = USB_EXT_PORT_RX_LANES(ext_portstatus) + 1;
                udev->tx_lanes = USB_EXT_PORT_TX_LANES(ext_portstatus) + 1;
+               udev->ssp_rate = get_port_ssp_rate(hub->hdev, ext_portstatus);
        } else {
                udev->rx_lanes = 1;
                udev->tx_lanes = 1;
+               udev->ssp_rate = USB_SSP_GEN_UNKNOWN;
        }
        if (hub_is_wusb(hub))
                udev->speed = USB_SPEED_WIRELESS;
index b07e90d07ab61d3116bbede5535fec69e24de2c1..ddd2f5b2a2827d8434d67af51a298ae3714d672a 100644 (file)
@@ -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;