Skip to contentSkip to conversation

usb: Handle different sublink speeds

PATCH Superseded Thinh Nguyen 2 participants 15 messages Jul 23, 2020 – Jul 24, 2020

Subsystems (3)

v2 of 5 Superseded Latest is v5 →

lore
Share & tools
lore

Thinh Nguyen

author maintainer
#0 lore raw

v2 cover usb: Handle different sublink speeds

A USB super-speed-plus device may operate at different sublink speed and lane count (e.g. gen2x2, gen1x2, or gen2x1). The usb gadget stack needs to be able to handle a couple things:

1) Report the sublink speed attributes the device support
2) Select the sublink speed attribute

This series introduces sublink speed attribute structure to ch9.h to capture the device capability of the gadget. It also introduces a new gadget ops udc_set_num_lanes_and_speed to select a specific sublink speed.

DWC3 needs this support for DWC_usb32 IP. Implement the new changes for DWC3.

Changes in v2:
- Move usb_sublink_speed attribute struct and enum to include/linux/usb/ch9.h
- Use "num-lanes" and "lane-speed-mantissa-gbps" as common properties instead
- Add common functions to get num-lanes and lsm properties
- Fix missing gen1x2 sublink speed attribute check report in dwc3

Thinh Nguyen (12):
usb: ch9: Add sublink speed struct
usb: gadget: composite: Avoid using magic numbers
usb: gadget: Expose sublink speed attributes
usb: gadget: Set max speed for SSP devices
usb: composite: Properly report sublink speed
usb: devicetree: Introduce num-lanes and lsm
usb: common: Add functions to get lanes and lsm
usb: dwc3: Initialize lane count and sublink speed
usb: dwc3: gadget: Report sublink speed capability
usb: dwc3: gadget: Implement setting of sublink speed
usb: dwc3: gadget: Track connected lane and sublink speed
usb: dwc3: gadget: Set speed only up to the max supported

 Documentation/devicetree/bindings/usb/generic.txt |  11 ++
 drivers/usb/common/common.c                       |  26 ++++
 drivers/usb/dwc3/core.c                           |  62 ++++++++++
 drivers/usb/dwc3/core.h                           |  18 +++
 drivers/usb/dwc3/gadget.c                         | 143 +++++++++++++++++++++-
 drivers/usb/gadget/composite.c                    |  81 ++++++++----
 drivers/usb/gadget/udc/core.c                     |  24 +++-
 include/linux/usb/ch9.h                           |  62 ++++++++++
 include/linux/usb/gadget.h                        |  23 ++++
 9 files changed, 414 insertions(+), 36 deletions(-)

base-commit: 5db5ea26a2469d8899f995e82e8b718dc9e9d168

Signature · 1 line
2.11.0
author maintainer
#1 lore raw

PATCH v2 1/12 usb: ch9: Add sublink speed struct

USB 3.2 specification supports dual-lane for super-speed-plus. USB devices may operate at different sublink speeds. To avoid using magic numbers and capture the sublink speed better, introduce the usb_sublink_speed structure and various sublink speed attribute enum.

See SSP BOS descriptor in USB 3.2 specification section 9.6.2.5

---
Changes in v2:
- Move to include/linux/usb/ch9.h instead of under uapi

 include/linux/usb/ch9.h | 43 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 43 insertions(+)
include/linux/usb/ch9.h +43 -0
index 58b83066bea4..f87ef9a2f859 100644
@@ -36,6 +36,49 @@
 #include <linux/device.h>
 #include <uapi/linux/usb/ch9.h>
 
+/* USB 3.2 sublink speed attributes */
+
+enum usb_lane_speed_exponent {
+	USB_LSE_BPS = 0,
+	USB_LSE_KBPS = 1,
+	USB_LSE_MBPS = 2,
+	USB_LSE_GBPS = 3,
+};
+
+enum usb_sublink_type {
+	USB_ST_SYMMETRIC_RX = 0,
+	USB_ST_ASYMMETRIC_RX = 1,
+	USB_ST_SYMMETRIC_TX = 2,
+	USB_ST_ASYMMETRIC_TX = 3,
+};
+
+enum usb_link_protocol {
+	USB_LP_SS = 0,
+	USB_LP_SSP = 1,
+};
+
+/**
+ * struct usb_sublink_speed - sublink speed attribute
+ * @id: sublink speed attribute ID (SSID)
+ * @mantissa: lane speed mantissa
+ * @exponent: lane speed exponent
+ * @type: sublink type
+ * @protocol: sublink protocol
+ *
+ * Super-speed-plus supports multiple lanes. Use the sublink speed attributes to
+ * describe the sublink speed.
+ *
+ * See USB 3.2 spec section 9.6.2.6 for super-speed-plus capability for more
+ * information.
+ */
+struct usb_sublink_speed {
+	u8				id;
+	u16				mantissa;
+	enum usb_lane_speed_exponent	exponent;
+	enum usb_sublink_type		type;
+	enum usb_link_protocol		protocol;
+};
+
 /**
  * usb_ep_type_string() - Returns human readable-name of the endpoint type.
  * @ep_type: The endpoint type to return human-readable name for.  If it's not
Signature · 1 line
2.11.0
author maintainer
#2 lore raw

PATCH v2 2/12 usb: gadget: composite: Avoid using magic numbers

In reply to Thinh Nguyen

Magic numbers are difficult to read. Use macros for super-speed-plus BOS descriptor attributes in the composite driver. They're self-documented. So there's no need to provide comments as we did previously for the magic numbers.

---
Changes in v2:
- None

 drivers/usb/gadget/composite.c | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)
drivers/usb/gadget/composite.c +20 -21
index 5c1eb96a5c57..a231ae382ac3 100644
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/utsname.h>
+#include <linux/bitfield.h>
 
 #include <linux/usb/composite.h>
 #include <linux/usb/otg.h>
@@ -743,32 +744,30 @@ static int bos_desc(struct usb_composite_dev *cdev)
 		ssp_cap->bReserved = 0;
 		ssp_cap->wReserved = 0;
 
-		/* SSAC = 1 (2 attributes) */
-		ssp_cap->bmAttributes = cpu_to_le32(1);
+		ssp_cap->bmAttributes =
+			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, 1) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, 0));
 
-		/* Min RX/TX Lane Count = 1 */
 		ssp_cap->wFunctionalitySupport =
-			cpu_to_le16((1 << 8) | (1 << 12));
+			cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) |
+				    FIELD_PREP(USB_SSP_MIN_RX_LANE_COUNT, 1) |
+				    FIELD_PREP(USB_SSP_MIN_TX_LANE_COUNT, 1));
 
-		/*
-		 * bmSublinkSpeedAttr[0]:
-		 *   ST  = Symmetric, RX
-		 *   LSE =  3 (Gbps)
-		 *   LP  =  1 (SuperSpeedPlus)
-		 *   LSM = 10 (10 Gbps)
-		 */
 		ssp_cap->bmSublinkSpeedAttr[0] =
-			cpu_to_le32((3 << 4) | (1 << 14) | (0xa << 16));
-		/*
-		 * bmSublinkSpeedAttr[1] =
-		 *   ST  = Symmetric, TX
-		 *   LSE =  3 (Gbps)
-		 *   LP  =  1 (SuperSpeedPlus)
-		 *   LSM = 10 (10 Gbps)
-		 */
+			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, USB_LSE_GBPS) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
+					       USB_ST_SYMMETRIC_RX) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, USB_LP_SSP) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
+
 		ssp_cap->bmSublinkSpeedAttr[1] =
-			cpu_to_le32((3 << 4) | (1 << 14) |
-				    (0xa << 16) | (1 << 7));
+			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, USB_LSE_GBPS) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
+					       USB_ST_SYMMETRIC_TX) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, USB_LP_SSP) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
 	}
 
 	return le16_to_cpu(bos->wTotalLength);
Signature · 1 line
2.11.0
author maintainer
#3 lore raw

PATCH v2 3/12 usb: gadget: Expose sublink speed attributes

In reply to Thinh Nguyen

The USB 3.2 specification supports dual-lane and different transfer rates for super-speed-plus. Devices operating in super-speed-plus can be gen2x1, gen1x2, or gen2x2.

A gadget driver may need to know the gadget's sublink speeds to properly setup its transfer requests and describe its capability in its descriptors. To describe the transfer rate in super-speed-plus fully, let's expose the lane count and sublink speed attributes when operating in super-speed-plus.

---
Changes in v2:
- None

 include/linux/usb/gadget.h | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)
include/linux/usb/gadget.h +20 -0
index 6a178177e4c9..2ab929eed928 100644
@@ -338,6 +338,15 @@ struct usb_gadget_ops {
  * @speed: Speed of current connection to USB host.
  * @max_speed: Maximal speed the UDC can handle.  UDC must support this
  *      and all slower speeds.
+ * @num_lanes: Number of lanes in use.
+ * @max_num_lanes: Maximum number of lanes the UDC supports.
+ * @ssac: Sublink speed attribute count. The number of sublink speed
+ *	attributes is ssac + 1.
+ * @sublink_speed: Array of sublink speed attributes the UDC supports. Sublink
+ *	speed attributes are paired, and an RX followed by a TX attribute.
+ * @speed_ssid: Current sublink speed attribute ID in use.
+ * @min_speed_ssid: Sublink speed attribute ID with the minimum speed.
+ * @max_speed_ssid: Sublink speed attribute ID with the maximum speed.
  * @state: the state we are now (attached, suspended, configured, etc)
  * @name: Identifies the controller hardware type.  Used in diagnostics
  *	and sometimes configuration.
@@ -405,6 +414,17 @@ struct usb_gadget {
 	struct list_head		ep_list;	/* of usb_ep */
 	enum usb_device_speed		speed;
 	enum usb_device_speed		max_speed;
+
+	/* SSP only */
+	unsigned			num_lanes;
+	unsigned			max_num_lanes;
+	unsigned			ssac;
+#define USB_GADGET_MAX_SSAC 3
+	struct usb_sublink_speed	sublink_speed[USB_GADGET_MAX_SSAC + 1];
+	unsigned			speed_ssid;
+	unsigned			min_speed_ssid;
+	unsigned			max_speed_ssid;
+
 	enum usb_device_state		state;
 	const char			*name;
 	struct device			dev;
Signature · 1 line
2.11.0
author maintainer
#4 lore raw

PATCH v2 4/12 usb: gadget: Set max speed for SSP devices

In reply to Thinh Nguyen

A super-speed-plus device may operate at different sublink speeds (e.g. gen2x2, gen1x2, or gen2x1). If the USB device supports different sublink speeds at super-speed-plus, set the device to operate at the maximum number of lanes and sublink speed possible. Introduce gadget ops udc_set_num_lanes_and_speed to set the lane count and sublink speed for super-speed-plus capable devices.

---
Changes in v2:
- None

 drivers/usb/gadget/udc/core.c | 24 +++++++++++++++++++-----
 include/linux/usb/gadget.h    |  3 +++
 2 files changed, 22 insertions(+), 5 deletions(-)
drivers/usb/gadget/udc/core.c +19 -5
index 5241603bd008..5fea14cc9dbd 100644
@@ -1112,12 +1112,26 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc)
 static inline void usb_gadget_udc_set_speed(struct usb_udc *udc,
 					    enum usb_device_speed speed)
 {
-	if (udc->gadget->ops->udc_set_speed) {
-		enum usb_device_speed s;
+	struct usb_gadget *gadget = udc->gadget;
+	enum usb_device_speed s;
 
-		s = min(speed, udc->gadget->max_speed);
-		udc->gadget->ops->udc_set_speed(udc->gadget, s);
-	}
+	if (speed == USB_SPEED_UNKNOWN)
+		s = gadget->max_speed;
+	else
+		s = min(speed, gadget->max_speed);
+
+	/*
+	 * If the UDC supports super-speed-plus and different sublink speeds,
+	 * then set the gadget to the max possible sublink speed for
+	 * super-speed-plus symmetric lanes.
+	 */
+	if (s == USB_SPEED_SUPER_PLUS &&
+	    gadget->ops->udc_set_num_lanes_and_speed)
+		gadget->ops->udc_set_num_lanes_and_speed(gadget,
+							 gadget->max_num_lanes,
+							 gadget->max_speed_ssid);
+	else if (gadget->ops->udc_set_speed)
+		gadget->ops->udc_set_speed(gadget, s);
 }
 
 /**
include/linux/usb/gadget.h +3 -0
index 2ab929eed928..ec4ed17e7bf5 100644
@@ -322,6 +322,9 @@ struct usb_gadget_ops {
 			struct usb_gadget_driver *);
 	int	(*udc_stop)(struct usb_gadget *);
 	void	(*udc_set_speed)(struct usb_gadget *, enum usb_device_speed);
+	void	(*udc_set_num_lanes_and_speed)(struct usb_gadget *,
+					       unsigned int num_lanes,
+					       unsigned int ssid);
 	struct usb_ep *(*match_ep)(struct usb_gadget *,
 			struct usb_endpoint_descriptor *,
 			struct usb_ss_ep_comp_descriptor *);
Signature · 1 line
2.11.0
author maintainer
#5 lore raw

PATCH v2 5/12 usb: composite: Properly report sublink speed

In reply to Thinh Nguyen

Use the max sublink speed attributes reported in the gadget structure to write to the super-speed-plus BOS descriptor if available.

---
Changes in v2:
- None

 drivers/usb/gadget/composite.c | 76 +++++++++++++++++++++++++++++-------------
 1 file changed, 52 insertions(+), 24 deletions(-)
drivers/usb/gadget/composite.c +52 -24
index a231ae382ac3..d9fb29a72f94 100644
@@ -729,45 +729,73 @@ static int bos_desc(struct usb_composite_dev *cdev)
 	/* The SuperSpeedPlus USB Device Capability descriptor */
 	if (gadget_is_superspeed_plus(cdev->gadget)) {
 		struct usb_ssp_cap_descriptor *ssp_cap;
+		unsigned int ssac = 1;
+		unsigned int ssic = 0;
+		unsigned int min_ssid = 0;
+		int i;
+
+		if (cdev->gadget->ssac) {
+			ssac = cdev->gadget->ssac;
+
+			/*
+			 * Paired RX and TX sublink speed attributes share
+			 * the same SSID.
+			 */
+			ssic = (ssac + 1) / 2 - 1;
+			min_ssid = cdev->gadget->min_speed_ssid;
+		}
 
 		ssp_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
 		bos->bNumDeviceCaps++;
 
-		/*
-		 * Report typical values.
-		 */
-
-		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(1));
-		ssp_cap->bLength = USB_DT_USB_SSP_CAP_SIZE(1);
+		le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SSP_CAP_SIZE(ssac));
+		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, 1) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, 0));
+			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_ATTRIBS, ssac) |
+				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_IDS, ssic));
 
 		ssp_cap->wFunctionalitySupport =
-			cpu_to_le16(FIELD_PREP(USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID, 0) |
+			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));
 
-		ssp_cap->bmSublinkSpeedAttr[0] =
-			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, USB_LSE_GBPS) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
-					       USB_ST_SYMMETRIC_RX) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, USB_LP_SSP) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
-
-		ssp_cap->bmSublinkSpeedAttr[1] =
-			cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, 0) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, USB_LSE_GBPS) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST,
-					       USB_ST_SYMMETRIC_TX) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, USB_LP_SSP) |
-				    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, 10));
+		/*
+		 * If the sublink speed attributes are not specified, then the
+		 * default will be a pair symmetric RX/TX sublink speed
+		 * attributes of 10 Gbps.
+		 */
+		for (i = 0; i < ssac + 1; i++) {
+			struct usb_sublink_speed default_ssa;
+			struct usb_sublink_speed *ptr;
+
+			if (cdev->gadget->ssac) {
+				ptr = &cdev->gadget->sublink_speed[i];
+			} else {
+				default_ssa.id = i / 2;
+				default_ssa.protocol = USB_LP_SSP;
+				default_ssa.exponent = USB_LSE_GBPS;
+				default_ssa.mantissa = 10;
+
+				if (i % 2)
+					default_ssa.type = USB_ST_SYMMETRIC_TX;
+				else
+					default_ssa.type = USB_ST_SYMMETRIC_RX;
+
+				ptr = &default_ssa;
+			}
+
+			ssp_cap->bmSublinkSpeedAttr[i] =
+				cpu_to_le32(FIELD_PREP(USB_SSP_SUBLINK_SPEED_SSID, ptr->id) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSE, ptr->exponent) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_ST, ptr->type) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LP, ptr->protocol) |
+					    FIELD_PREP(USB_SSP_SUBLINK_SPEED_LSM, ptr->mantissa));
+		}
 	}
 
 	return le16_to_cpu(bos->wTotalLength);
Signature · 1 line
2.11.0
author maintainer
#6 lore raw

PATCH v2 6/12 usb: devicetree: Introduce num-lanes and lsm

In reply to Thinh Nguyen

According to USB 3.2 spec, a super-speed-plus device can operate at gen2x2, gen2x1, or gen1x2. Introduce "num-lanes" and "lane-speed-mantissa-gbps" properties for devices operating in super-speed-plus. If the USB controller device supports multiple lanes at different transfer rate, the user can specify the HW capability via these properties.

---
Changes in v2:
- Make "num-lanes" and "lane-speed-mantissa-gbps" common USB properties

 Documentation/devicetree/bindings/usb/generic.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
Documentation/devicetree/bindings/usb/generic.txt +11 -0
index ba472e7aefc9..a8253da684af 100644
@@ -7,6 +7,17 @@ Optional properties:
 			"low-speed". In case this isn't passed via DT, USB
 			controllers should default to their maximum HW
 			capability.
+ - num-lanes: tells USB controllers that we want to work up to a certain number
+			of lanes. Valid arguments are 1 or 2. Apply if the
+			maximum-speed is super-speed-plus. In case this isn't
+			passed via DT, the USB controllers should default to
+			their maximum HW capability.
+ - lane-speed-mantissa-gbps: tells USB controllers that we want the symmetric
+			lanes to operate up to a certain rate in Gbps. Valid
+			inputs are 5 or 10 (i.e. Gen 1/Gen 2 transfer rate).
+			Apply if the maximum-speed is super-speed-plus. In case
+			this isn't passed via DT, the USB controllers should
+			default to their maximum HW capability.
  - dr_mode: tells Dual-Role USB controllers that we want to work on a
 			particular mode. Valid arguments are "host",
 			"peripheral" and "otg". In case this attribute isn't
Signature · 1 line
2.11.0
maintainer
#7 lore raw

Re: [PATCH v2 06/12] usb: devicetree: Introduce num-lanes and lsm

Quoted 24 lines · Thinh Nguyen

According to USB 3.2 spec, a super-speed-plus device can operate at gen2x2, gen2x1, or gen1x2. Introduce "num-lanes" and "lane-speed-mantissa-gbps" properties for devices operating in super-speed-plus. If the USB controller device supports multiple lanes at different transfer rate, the user can specify the HW capability via these properties.

---
Changes in v2:
- Make "num-lanes" and "lane-speed-mantissa-gbps" common USB properties

 Documentation/devicetree/bindings/usb/generic.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
Documentation/devicetree/bindings/usb/generic.txt +1 -0
index ba472e7aefc9..a8253da684af 100644
@@ -7,6 +7,17 @@ Optional properties:
 			"low-speed". In case this isn't passed via DT, USB
 			controllers should default to their maximum HW
 			capability.
+ - num-lanes: tells USB controllers that we want to work up to a certain number

Documentation/devicetree/bindings/usb/generic.txt Optional properties: · line 20

Quoted 10 lines
Documentation/devicetree/bindings/usb/generic.txt +10 -0
+			of lanes. Valid arguments are 1 or 2. Apply if the
+			maximum-speed is super-speed-plus. In case this isn't
+			passed via DT, the USB controllers should default to
+			their maximum HW capability.
+ - lane-speed-mantissa-gbps: tells USB controllers that we want the symmetric
+			lanes to operate up to a certain rate in Gbps. Valid
+			inputs are 5 or 10 (i.e. Gen 1/Gen 2 transfer rate).
+			Apply if the maximum-speed is super-speed-plus. In case
+			this isn't passed via DT, the USB controllers should
+			default to their maximum HW capability.

This still leaves 'maximum-speed = "super-speed-plus"' ambiguous. Fix that please.

To put it another way, we already have one way to define USB speeds. Don't define a new and different way that only covers a fraction of the possibilities.

Rob

author maintainer
#8 lore raw

Re: [PATCH v2 06/12] usb: devicetree: Introduce num-lanes and lsm

Quoted 38 lines · Rob Herring
Quoted 24 lines · Thinh Nguyen

According to USB 3.2 spec, a super-speed-plus device can operate at gen2x2, gen2x1, or gen1x2. Introduce "num-lanes" and "lane-speed-mantissa-gbps" properties for devices operating in super-speed-plus. If the USB controller device supports multiple lanes at different transfer rate, the user can specify the HW capability via these properties.

---
Changes in v2:
- Make "num-lanes" and "lane-speed-mantissa-gbps" common USB properties

  Documentation/devicetree/bindings/usb/generic.txt | 11 +++++++++++
  1 file changed, 11 insertions(+)
Documentation/devicetree/bindings/usb/generic.txt +1 -0
index ba472e7aefc9..a8253da684af 100644
@@ -7,6 +7,17 @@ Optional properties:
  			"low-speed". In case this isn't passed via DT, USB
  			controllers should default to their maximum HW
  			capability.
+ - num-lanes: tells USB controllers that we want to work up to a certain number

Documentation/devicetree/bindings/usb/generic.txt Optional properties: · line 20

Quoted 10 lines
Documentation/devicetree/bindings/usb/generic.txt +10 -0
+			of lanes. Valid arguments are 1 or 2. Apply if the
+			maximum-speed is super-speed-plus. In case this isn't
+			passed via DT, the USB controllers should default to
+			their maximum HW capability.
+ - lane-speed-mantissa-gbps: tells USB controllers that we want the symmetric
+			lanes to operate up to a certain rate in Gbps. Valid
+			inputs are 5 or 10 (i.e. Gen 1/Gen 2 transfer rate).
+			Apply if the maximum-speed is super-speed-plus. In case
+			this isn't passed via DT, the USB controllers should
+			default to their maximum HW capability.

This still leaves 'maximum-speed = "super-speed-plus"' ambiguous. Fix that please.

Quoted 4 lines

To put it another way, we already have one way to define USB speeds. Don't define a new and different way that only covers a fraction of the possibilities.

Then, we can just do away with the "num-lanes" property, and change it to something like this as you suggested?

(quoted diff) +0 -0

b/Documentation/devicetree/bindings/usb/generic.txt index ba472e7aefc9..8541b9571f2f 100644

Documentation/devicetree/bindings/usb/generic.txt +1 -4
@@ -3,10 +3,13 @@ Generic USB Properties
  Optional properties:
   - maximum-speed: tells USB controllers we want to work up to a certain
                         speed. Valid arguments are "super-speed-plus",
-                       "super-speed", "high-speed", "full-speed" and
-                       "low-speed". In case this isn't passed via DT, USB
-                       controllers should default to their maximum HW
-                       capability.
+                       "super-speed-plus-gen2x2", 

"super-speed-plus-gen2x1",
+                       "super-speed-plus-gen1x2", "super-speed", "high-speed",
+                       "full-speed" and "low-speed". In case this isn't passed
+                       via DT, USB controllers should default to their maximum
+                       HW capability. Similarly, if the argument is +                       "super-speed-plus", USB controllers should default to
+                       their maximum transfer rate and number of lanes.
  - dr_mode: tells Dual-Role USB controllers that we want to work on a particular mode. Valid arguments are "host", "peripheral" and "otg". In case this attribute
isn't

Is that ok?

BR,
Thinh

author maintainer
#9 lore raw

PATCH v2 7/12 usb: common: Add functions to get lanes and lsm

In reply to Thinh Nguyen

Add common functions to get the requested number of lanes and lane speed mantissa from device properties.

---
Changes in v2:
- New commit

 drivers/usb/common/common.c | 26 ++++++++++++++++++++++++++
 include/linux/usb/ch9.h     | 19 +++++++++++++++++++
 2 files changed, 45 insertions(+)
drivers/usb/common/common.c +26 -0
index 1433260d99b4..871a7326b6b7 100644
@@ -92,6 +92,32 @@ enum usb_device_speed usb_get_maximum_speed(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(usb_get_maximum_speed);
 
+u8 usb_get_num_lanes(struct device *dev)
+{
+	u8 num_lanes;
+	int ret;
+
+	ret = device_property_read_u8(dev, "num-lanes", &num_lanes);
+	if (ret < 0)
+		return 0;
+
+	return num_lanes;
+}
+EXPORT_SYMBOL_GPL(usb_get_num_lanes);
+
+u16 usb_get_lsm(struct device *dev)
+{
+	u16 lsm;
+	int ret;
+
+	ret = device_property_read_u16(dev, "lane-speed-mantissa-gbps", &lsm);
+	if (ret < 0)
+		return 0;
+
+	return lsm;
+}
+EXPORT_SYMBOL_GPL(usb_get_lsm);
+
 const char *usb_state_string(enum usb_device_state state)
 {
 	static const char *const names[] = {
include/linux/usb/ch9.h +19 -0
index f87ef9a2f859..1097c6f8e2ef 100644
@@ -105,6 +105,25 @@ extern const char *usb_speed_string(enum usb_device_speed speed);
  */
 extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
 
+/**
+ * usb_get_num_lanes - Get maximum number of requested lanes for a given USB
+ * controller.
+ * @dev: Pointer to the given USB controller device
+ *
+ * The function returns the number of requested lanes from the property
+ * "num-lanes".
+ */
+extern u8 usb_get_num_lanes(struct device *dev);
+
+/**
+ * usb_get_lsm - Get requested lane speed mantissa for a given USB controller.
+ * @dev: Pointer to the given USB controller device
+ *
+ * The function returns the requested symmetric lane speed mantissa in Gbps from
+ * the property "lane-speed-mantissa-gbps".
+ */
+extern u16 usb_get_lsm(struct device *dev);
+
 /**
  * usb_state_string - Returns human readable name for the state.
  * @state: The state to return a human-readable name for. If it's not
Signature · 1 line
2.11.0
author maintainer
#10 lore raw

PATCH v2 8/12 usb: dwc3: Initialize lane count and sublink speed

In reply to Thinh Nguyen

DWC_usb32 supports dual-lane operating at different sublink speeds. Initialize and validate the maximum number of lanes and speed the controller supports captured from the dwc3 device properties.

Currently the controller has no visibility into the HW parameter to determine the maximum number of lanes the phy supports. If the number of lanes is not specified, then set the default to 2 for DWC_usb32 and 1 for DWC_usb31 if operate in SSP.

---
Changes in v2:
- Use common functions to get num_lanes and lsm properties

 drivers/usb/dwc3/core.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/core.h | 10 ++++++++
 2 files changed, 72 insertions(+)
drivers/usb/dwc3/core.c +62 -0
index 25c686a752b0..d9b8e3f8d005 100644
@@ -1275,6 +1275,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 	hird_threshold = 12;
 
 	dwc->maximum_speed = usb_get_maximum_speed(dev);
+	dwc->maximum_num_lanes = usb_get_num_lanes(dev);
+	dwc->maximum_lsm = usb_get_lsm(dev);
 	dwc->dr_mode = usb_get_dr_mode(dev);
 	dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
 
@@ -1426,6 +1428,66 @@ static void dwc3_check_params(struct dwc3 *dwc)
 
 		break;
 	}
+
+	/*
+	 * If it's not DWC_usb32 IP or speed is less than SSP, no need to
+	 * validate the number of lanes or lane speed mantissa.
+	 *
+	 * Currently the controller does not have visibility into the HW
+	 * parameter to determine the maximum number of lanes the phy supports.
+	 * If the number of lanes is not specified in the device property, then
+	 * set the default to 2 for DWC_usb32 and 1 for DWC_usb31 if the device
+	 * is capable of super-speed-plus.
+	 */
+	if (!DWC3_IP_IS(DWC32) ||
+	    dwc->maximum_speed < USB_SPEED_SUPER_PLUS) {
+		if (dwc->maximum_lsm || dwc->maximum_num_lanes)
+			dev_warn(dev, "Ignore num_lanes and LSM properties\n");
+
+		if (DWC3_IP_IS(DWC31) &&
+		    dwc->maximum_speed == USB_SPEED_SUPER_PLUS) {
+			dwc->maximum_lsm = DWC3_LSM_10_GBPS;
+			dwc->maximum_num_lanes = 1;
+		} else {
+			dwc->maximum_lsm = DWC3_LSM_UNSPECIFIED;
+			dwc->maximum_num_lanes = 0;
+		}
+
+		return;
+	}
+
+	switch (dwc->maximum_lsm) {
+	case DWC3_LSM_10_GBPS:
+	case DWC3_LSM_5_GBPS:
+		break;
+	default:
+		dev_err(dev, "Invalid LSM (%dGbps)\n", dwc->maximum_lsm);
+		fallthrough;
+	case DWC3_LSM_UNSPECIFIED:
+		dwc->maximum_lsm = DWC3_LSM_10_GBPS;
+		break;
+	}
+
+	switch (dwc->maximum_num_lanes) {
+	case 2:
+		break;
+	case 1:
+		if (dwc->maximum_lsm == DWC3_LSM_5_GBPS) {
+			dev_err(dev, "Invalid num_lanes (%d) and LSM (%dGbps) for %s\n",
+				dwc->maximum_num_lanes,
+				dwc->maximum_lsm,
+				usb_speed_string(dwc->maximum_speed));
+			dwc->maximum_lsm = DWC3_LSM_10_GBPS;
+		}
+		break;
+	default:
+		dev_err(dev, "Invalid number of lanes (%d)\n",
+			dwc->maximum_num_lanes);
+		fallthrough;
+	case 0:
+		dwc->maximum_num_lanes = 2;
+		break;
+	}
 }
 
 static int dwc3_probe(struct platform_device *pdev)
drivers/usb/dwc3/core.h +10 -0
index 013f42a2b5dc..0e37a0471e5a 100644
@@ -40,6 +40,10 @@
 #define DWC3_XHCI_RESOURCES_NUM	2
 #define DWC3_ISOC_MAX_RETRIES	5
 
+#define DWC3_LSM_UNSPECIFIED	0
+#define DWC3_LSM_5_GBPS		5
+#define DWC3_LSM_10_GBPS	10
+
 #define DWC3_SCRATCHBUF_SIZE	4096	/* each buffer is assumed to be 4KiB */
 #define DWC3_EVENT_BUFFERS_SIZE	4096
 #define DWC3_EVENT_TYPE_MASK	0xfe
@@ -958,6 +962,8 @@ struct dwc3_scratchpad_array {
  * @nr_scratch: number of scratch buffers
  * @u1u2: only used on revisions <1.83a for workaround
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @maximum_lsm: maximum lane speed mantissa in Gbps
+ * @maximum_num_lanes: maximum number of lanes
  * @ip: controller's ID
  * @revision: controller's version of an IP
  * @version_type: VERSIONTYPE register contents, a sub release of a revision
@@ -988,6 +994,7 @@ struct dwc3_scratchpad_array {
  * @ep0state: state of endpoint zero
  * @link_state: link state
  * @speed: device speed (super, high, full, low)
+ * @num_lanes: number of connected lanes
  * @hwparams: copy of hwparams registers
  * @root: debugfs root folder pointer
  * @regset: debugfs pointer to regdump file
@@ -1119,6 +1126,8 @@ struct dwc3 {
 	u32			nr_scratch;
 	u32			u1u2;
 	u32			maximum_speed;
+	u16			maximum_lsm;
+	u8			maximum_num_lanes;
 
 	u32			ip;
 
@@ -1184,6 +1193,7 @@ struct dwc3 {
 	u8			u1pel;
 
 	u8			speed;
+	u8			num_lanes;
 
 	u8			num_eps;
 
Signature · 1 line
2.11.0
author maintainer
#11 lore raw

PATCH v2 9/12 usb: dwc3: gadget: Report sublink speed capability

In reply to Thinh Nguyen

Report the sublink speed attributes to the usb_gadget structure based on the dwc3 device capability maximum_lsm and maximum_num_lanes.

Only DWC_usb32 supports 2 sublink speeds if it can operate with 2 lanes. (i.e. at SSP, it can operate as gen1x2)

Note: the SSID DWC3_SSP_SSID_GEN2 and DWC3_SSP_SSID_GEN1 are arbitrary. There's no standard according to the USB 3.2 spec as long as they are unique and within 0-15.

---
Changes in v2:
- Fix missing check for gen1x2 when writing to sublink speed attributes
- Minor fix in commit message (first commit sentence ended with comma)

 drivers/usb/dwc3/core.h   |  4 ++++
 drivers/usb/dwc3/gadget.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 53 insertions(+)
drivers/usb/dwc3/core.h +4 -0
index 0e37a0471e5a..b44550173599 100644
@@ -44,6 +44,10 @@
 #define DWC3_LSM_5_GBPS		5
 #define DWC3_LSM_10_GBPS	10
 
+/* Sublink Speed Attribute ID */
+#define DWC3_SSP_SSID_GEN2	2
+#define DWC3_SSP_SSID_GEN1	1
+
 #define DWC3_SCRATCHBUF_SIZE	4096	/* each buffer is assumed to be 4KiB */
 #define DWC3_EVENT_BUFFERS_SIZE	4096
 #define DWC3_EVENT_TYPE_MASK	0xfe
drivers/usb/dwc3/gadget.c +49 -0

55 lines not on this page · raw message

Signature · 1 line
2.11.0
author maintainer
#12 lore raw

PATCH v2 10/12 usb: dwc3: gadget: Implement setting of sublink speed

In reply to Thinh Nguyen

Implement gadget ops udc_set_num_lanes_and_speed. This allows the gadget/core driver to select number of lanes to use and the sublink speed the controller supports.

---
Changes in v2:
- None

 drivers/usb/dwc3/core.h   |  2 ++
 drivers/usb/dwc3/gadget.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 65 insertions(+), 1 deletion(-)
drivers/usb/dwc3/core.h +2 -0
index b44550173599..36407a8e67b8 100644
@@ -389,6 +389,8 @@
 #define DWC3_GUCTL2_RST_ACTBITLATER		BIT(14)
 
 /* Device Configuration Register */
+#define DWC3_DCFG_NUMLANES(n)	(((n) & 0x3) << 30) /* DWC_usb32 only */
+
 #define DWC3_DCFG_DEVADDR(addr)	((addr) << 3)
 #define DWC3_DCFG_DEVADDR_MASK	DWC3_DCFG_DEVADDR(0x7f)
 
drivers/usb/dwc3/gadget.c +63 -1

82 lines not on this page · raw message

Signature · 1 line
2.11.0
author maintainer
#13 lore raw

PATCH v2 11/12 usb: dwc3: gadget: Track connected lane and sublink speed

In reply to Thinh Nguyen

Track the number of lanes connected in gadget->num_lanes and track the current sublink speed attribute ID for super-speed-plus operations.

Note: if the device is running in gen1x2, set the gadget->speed to USB_SPEED_SUPER_PLUS.

---
Changes in v2:
- None

 drivers/usb/dwc3/core.h   |  2 ++
 drivers/usb/dwc3/gadget.c | 21 ++++++++++++++++++++-
 2 files changed, 22 insertions(+), 1 deletion(-)
drivers/usb/dwc3/core.h +2 -0
index 36407a8e67b8..84e2150c1c0a 100644
@@ -464,6 +464,8 @@
 #define DWC3_DEVTEN_USBRSTEN		BIT(1)
 #define DWC3_DEVTEN_DISCONNEVTEN	BIT(0)
 
+#define DWC3_DSTS_CONNLANES(n)		(((n) >> 30) & 0x3) /* DWC_usb32 only */
+
 /* Device Status Register */
 #define DWC3_DSTS_DCNRD			BIT(29)
 
drivers/usb/dwc3/gadget.c +20 -1
index 5671a6762c83..2bc29d74e75a 100644
@@ -2306,6 +2306,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
 		}
 	}
+
+	if (DWC3_IP_IS(DWC32) && speed < USB_SPEED_SUPER_PLUS)
+		reg &= ~DWC3_DCFG_NUMLANES(~0);
+
 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
 
 	spin_unlock_irqrestore(&dwc->lock, flags);
@@ -3175,12 +3179,19 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 	struct dwc3_ep		*dep;
 	int			ret;
 	u32			reg;
+	u8			lanes = 1;
 	u8			speed;
 
 	reg = dwc3_readl(dwc->regs, DWC3_DSTS);
 	speed = reg & DWC3_DSTS_CONNECTSPD;
 	dwc->speed = speed;
 
+	if (DWC3_IP_IS(DWC32))
+		lanes = DWC3_DSTS_CONNLANES(reg) + 1;
+
+	dwc->gadget.num_lanes = lanes;
+	dwc->gadget.speed_ssid = 0;
+
 	/*
 	 * RAMClkSel is reset to 0 after USB reset, so it must be reprogrammed
 	 * each time on Connect Done.
@@ -3195,6 +3206,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 		dwc->gadget.ep0->maxpacket = 512;
 		dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+		dwc->gadget.speed_ssid = DWC3_SSP_SSID_GEN2;
 		break;
 	case DWC3_DSTS_SUPERSPEED:
 		/*
@@ -3215,7 +3227,13 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 		dwc->gadget.ep0->maxpacket = 512;
-		dwc->gadget.speed = USB_SPEED_SUPER;
+
+		if (lanes > 1) {
+			dwc->gadget.speed = USB_SPEED_SUPER_PLUS;
+			dwc->gadget.speed_ssid = DWC3_SSP_SSID_GEN1;
+		} else {
+			dwc->gadget.speed = USB_SPEED_SUPER;
+		}
 		break;
 	case DWC3_DSTS_HIGHSPEED:
 		dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
@@ -3696,6 +3714,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
 	dwc->gadget.sg_supported	= true;
 	dwc->gadget.name		= "dwc3-gadget";
 	dwc->gadget.lpm_capable		= true;
+	dwc->gadget.num_lanes		= 1;
 
 	/*
 	 * FIXME We might be setting max_speed to <SUPER, however versions
Signature · 1 line
2.11.0
author maintainer
#14 lore raw

PATCH v2 12/12 usb: dwc3: gadget: Set speed only up to the max supported

In reply to Thinh Nguyen

The setting of device speed should be limited by the device's maximum_speed. This patch adds a check and prevent the driver from attempting to configure higher than the maximum_speed.

---
Changes in v2:
- None

 drivers/usb/dwc3/gadget.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)
drivers/usb/dwc3/gadget.c +8 -3
index 2bc29d74e75a..9094690f8258 100644
@@ -2254,6 +2254,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 				  enum usb_device_speed speed)
 {
 	struct dwc3		*dwc = gadget_to_dwc(g);
+	enum usb_device_speed	selected_speed = speed;
 	unsigned long		flags;
 	u32			reg;
 
@@ -2278,7 +2279,10 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 	    !dwc->dis_metastability_quirk) {
 		reg |= DWC3_DCFG_SUPERSPEED;
 	} else {
-		switch (speed) {
+		if (speed > dwc->maximum_speed)
+			selected_speed = dwc->maximum_speed;
+
+		switch (selected_speed) {
 		case USB_SPEED_LOW:
 			reg |= DWC3_DCFG_LOWSPEED;
 			break;
@@ -2298,7 +2302,8 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 				reg |= DWC3_DCFG_SUPERSPEED_PLUS;
 			break;
 		default:
-			dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+			dev_err(dwc->dev, "invalid speed (%d)\n",
+				selected_speed);
 
 			if (DWC3_IP_IS(DWC3))
 				reg |= DWC3_DCFG_SUPERSPEED;
@@ -2307,7 +2312,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 		}
 	}
 
-	if (DWC3_IP_IS(DWC32) && speed < USB_SPEED_SUPER_PLUS)
+	if (DWC3_IP_IS(DWC32) && selected_speed < USB_SPEED_SUPER_PLUS)
 		reg &= ~DWC3_DCFG_NUMLANES(~0);
 
 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
Signature · 1 line
2.11.0