diff mbox series

[RFC,07/14] usb: dwc3: gadget: Set lane count and lsm

Message ID 808d8ad39fc584e2f85e20148d56bd49c50aebf6.1576118671.git.thinhn@synopsys.com (mailing list archive)
State New, archived
Headers show
Series usb: dwc3: Introduce DWC_usb32 | expand

Commit Message

Thinh Nguyen Dec. 12, 2019, 2:49 a.m. UTC
DWC_usb32 supports dual-lane at different transfer rate. This patch
initializes the controller to use the maximum support transfer rate
describes by the dwc3 device property of lane count and lane speed
mantissa in Gbps.

Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
---
 drivers/usb/dwc3/core.c   | 36 +++++++++++++++++++++++++++++
 drivers/usb/dwc3/core.h   | 10 ++++++++
 drivers/usb/dwc3/gadget.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 103 insertions(+), 1 deletion(-)

Comments

Felipe Balbi Dec. 12, 2019, 8:14 a.m. UTC | #1
Thinh Nguyen <Thinh.Nguyen@synopsys.com> writes:

> DWC_usb32 supports dual-lane at different transfer rate. This patch
> initializes the controller to use the maximum support transfer rate
> describes by the dwc3 device property of lane count and lane speed
> mantissa in Gbps.

Perhaps this should read as:

	"DWC_usb32 supports dual-lane at different transfer rate. This
	patch initializes the controller to use the maximum supported
	transfer rate described by the dwc3 device property of lane
	count and lane speed mantissa in Gbps."

> @@ -1424,6 +1428,38 @@ static void dwc3_check_params(struct dwc3 *dwc)
>  
>  		break;
>  	}
> +
> +	switch (dwc->maximum_lsm) {
> +	case 5:
> +		break;
> +	case 10:
> +		if (dwc->maximum_speed == USB_SPEED_SUPER)
> +			dev_err(dev, "invalid maximum_lsm parameter %d\n",
> +				dwc->maximum_lsm);
> +		/* Fall Through */
> +	default:
> +		if (dwc->maximum_speed == USB_SPEED_SUPER)
> +			dwc->maximum_lsm = 5;
> +		else if (dwc->maximum_speed > USB_SPEED_SUPER)
> +			dwc->maximum_lsm = 10;
> +		break;
> +	}
> +
> +	switch (dwc->maximum_lanes) {
> +	case 1:
> +	case 2:
> +		break;
> +	default:
> +		if (dwc->maximum_lanes > 2)
> +			dev_err(dev, "invalid number of lanes %d\n",
> +				dwc->maximum_lanes);
> +
> +		if (dwc3_is_usb32(dwc) &&
> +		    dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
> +			dwc->maximum_lanes = 2;
> +		else
> +			dwc->maximum_lanes = 1;
> +	}
>  }
>  
>  static int dwc3_probe(struct platform_device *pdev)
> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
> index 7fde3c7da543..8e729d4cd5bd 100644
> --- a/drivers/usb/dwc3/core.h
> +++ b/drivers/usb/dwc3/core.h
> @@ -376,6 +376,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)
>  
> @@ -449,6 +451,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)
>  
> @@ -946,6 +950,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_lanes: maximum lane count
>   * @ip: controller's ID
>   * @revision: controller's version of an IP
>   * @version_type: VERSIONTYPE register contents, a sub release of a revision
> @@ -973,6 +979,7 @@ struct dwc3_scratchpad_array {
>   * @ep0state: state of endpoint zero
>   * @link_state: link state
>   * @speed: device speed (super, high, full, low)
> + * @lane_count: number of connected lanes
>   * @hwparams: copy of hwparams registers
>   * @root: debugfs root folder pointer
>   * @regset: debugfs pointer to regdump file
> @@ -1100,6 +1107,8 @@ struct dwc3 {
>  	u32			nr_scratch;
>  	u32			u1u2;
>  	u32			maximum_speed;
> +	u8			maximum_lsm;
> +	u8			maximum_lanes;
>  
>  	u32			ip;
>  
> @@ -1159,6 +1168,7 @@ struct dwc3 {
>  	u8			u1pel;
>  
>  	u8			speed;
> +	u8			lane_count;
>  
>  	u8			num_eps;
>  
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index a6d562e208a9..c31144af3261 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -2183,6 +2183,53 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
>  	spin_unlock_irqrestore(&dwc->lock, flags);
>  }
>  
> +static void dwc3_gadget_set_sublink_attr(struct usb_gadget *g,
> +					 unsigned int lane_count,
> +					 unsigned int lsm)
> +{
> +	struct dwc3	*dwc = gadget_to_dwc(g);
> +	unsigned int	lanes;
> +	unsigned long	flags;
> +	u32		reg;
> +
> +	spin_lock_irqsave(&dwc->lock, flags);
> +	if (dwc->maximum_speed <= USB_SPEED_SUPER) {
> +		/* Fall back to maximum speed supported by HW */
> +		spin_unlock_irqrestore(&dwc->lock, flags);
> +		dwc3_gadget_set_speed(g, dwc->maximum_speed);
> +		spin_lock_irqsave(&dwc->lock, flags);

it looks like we should extract a __dwc3_gadget_set_speed() to avoid the
possible race here:

diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a9aba716bf80..e317b696029e 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2118,14 +2118,11 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
 				cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT);
 }
 
-static void dwc3_gadget_set_speed(struct usb_gadget *g,
+static void __dwc3_gadget_set_speed(struct dwc3 *dwc,
 				  enum usb_device_speed speed)
 {
-	struct dwc3		*dwc = gadget_to_dwc(g);
-	unsigned long		flags;
 	u32			reg;
 
-	spin_lock_irqsave(&dwc->lock, flags);
 	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
 	reg &= ~(DWC3_DCFG_SPEED_MASK);
 
@@ -2175,7 +2172,16 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
 		}
 	}
 	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
+static void dwc3_gadget_set_speed(struct usb_gadget *g,
+				  enum usb_device_speed speed)
+{
+	struct dwc3		*dwc = gadget_to_dwc(g);
+	unsigned long		flags;
 
+	spin_lock_irqsave(&dwc->lock, flags);
+	__dwc3_gadget_set_speed(dwc, speed);
 	spin_unlock_irqrestore(&dwc->lock, flags);
 }
 
Then your patch would look like:

static void dwc3_gadget_set_sublink_attr(struct usb_gadget *g,
					 unsigned int lane_count,
					 unsigned int lsm)
{
	struct dwc3	*dwc = gadget_to_dwc(g);
	unsigned int	lanes;
	unsigned long	flags;
	u32		reg;

	spin_lock_irqsave(&dwc->lock, flags);
	if (dwc->maximum_speed <= USB_SPEED_SUPER) {
		/* Fall back to maximum speed supported by HW */
		__dwc3_gadget_set_speed(dwc, dwc->maximum_speed);
		goto done;
	}

	[...]
}
Thinh Nguyen Dec. 12, 2019, 10:15 p.m. UTC | #2
Hi,

Felipe Balbi wrote:
> Thinh Nguyen <Thinh.Nguyen@synopsys.com> writes:
>
>> DWC_usb32 supports dual-lane at different transfer rate. This patch
>> initializes the controller to use the maximum support transfer rate
>> describes by the dwc3 device property of lane count and lane speed
>> mantissa in Gbps.
> Perhaps this should read as:
>
> 	"DWC_usb32 supports dual-lane at different transfer rate. This
> 	patch initializes the controller to use the maximum supported
> 	transfer rate described by the dwc3 device property of lane
> 	count and lane speed mantissa in Gbps."

Ok.

>> @@ -1424,6 +1428,38 @@ static void dwc3_check_params(struct dwc3 *dwc)
>>   
>>   		break;
>>   	}
>> +
>> +	switch (dwc->maximum_lsm) {
>> +	case 5:
>> +		break;
>> +	case 10:
>> +		if (dwc->maximum_speed == USB_SPEED_SUPER)
>> +			dev_err(dev, "invalid maximum_lsm parameter %d\n",
>> +				dwc->maximum_lsm);
>> +		/* Fall Through */
>> +	default:
>> +		if (dwc->maximum_speed == USB_SPEED_SUPER)
>> +			dwc->maximum_lsm = 5;
>> +		else if (dwc->maximum_speed > USB_SPEED_SUPER)
>> +			dwc->maximum_lsm = 10;
>> +		break;
>> +	}
>> +
>> +	switch (dwc->maximum_lanes) {
>> +	case 1:
>> +	case 2:
>> +		break;
>> +	default:
>> +		if (dwc->maximum_lanes > 2)
>> +			dev_err(dev, "invalid number of lanes %d\n",
>> +				dwc->maximum_lanes);
>> +
>> +		if (dwc3_is_usb32(dwc) &&
>> +		    dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
>> +			dwc->maximum_lanes = 2;
>> +		else
>> +			dwc->maximum_lanes = 1;
>> +	}
>>   }
>>   
>>   static int dwc3_probe(struct platform_device *pdev)
>> diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
>> index 7fde3c7da543..8e729d4cd5bd 100644
>> --- a/drivers/usb/dwc3/core.h
>> +++ b/drivers/usb/dwc3/core.h
>> @@ -376,6 +376,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)
>>   
>> @@ -449,6 +451,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)
>>   
>> @@ -946,6 +950,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_lanes: maximum lane count
>>    * @ip: controller's ID
>>    * @revision: controller's version of an IP
>>    * @version_type: VERSIONTYPE register contents, a sub release of a revision
>> @@ -973,6 +979,7 @@ struct dwc3_scratchpad_array {
>>    * @ep0state: state of endpoint zero
>>    * @link_state: link state
>>    * @speed: device speed (super, high, full, low)
>> + * @lane_count: number of connected lanes
>>    * @hwparams: copy of hwparams registers
>>    * @root: debugfs root folder pointer
>>    * @regset: debugfs pointer to regdump file
>> @@ -1100,6 +1107,8 @@ struct dwc3 {
>>   	u32			nr_scratch;
>>   	u32			u1u2;
>>   	u32			maximum_speed;
>> +	u8			maximum_lsm;
>> +	u8			maximum_lanes;
>>   
>>   	u32			ip;
>>   
>> @@ -1159,6 +1168,7 @@ struct dwc3 {
>>   	u8			u1pel;
>>   
>>   	u8			speed;
>> +	u8			lane_count;
>>   
>>   	u8			num_eps;
>>   
>> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
>> index a6d562e208a9..c31144af3261 100644
>> --- a/drivers/usb/dwc3/gadget.c
>> +++ b/drivers/usb/dwc3/gadget.c
>> @@ -2183,6 +2183,53 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
>>   	spin_unlock_irqrestore(&dwc->lock, flags);
>>   }
>>   
>> +static void dwc3_gadget_set_sublink_attr(struct usb_gadget *g,
>> +					 unsigned int lane_count,
>> +					 unsigned int lsm)
>> +{
>> +	struct dwc3	*dwc = gadget_to_dwc(g);
>> +	unsigned int	lanes;
>> +	unsigned long	flags;
>> +	u32		reg;
>> +
>> +	spin_lock_irqsave(&dwc->lock, flags);
>> +	if (dwc->maximum_speed <= USB_SPEED_SUPER) {
>> +		/* Fall back to maximum speed supported by HW */
>> +		spin_unlock_irqrestore(&dwc->lock, flags);
>> +		dwc3_gadget_set_speed(g, dwc->maximum_speed);
>> +		spin_lock_irqsave(&dwc->lock, flags);
> it looks like we should extract a __dwc3_gadget_set_speed() to avoid the
> possible race here:
>
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index a9aba716bf80..e317b696029e 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -2118,14 +2118,11 @@ static void dwc3_gadget_config_params(struct usb_gadget *g,
>   				cpu_to_le16(DWC3_DEFAULT_U2_DEV_EXIT_LAT);
>   }
>   
> -static void dwc3_gadget_set_speed(struct usb_gadget *g,
> +static void __dwc3_gadget_set_speed(struct dwc3 *dwc,
>   				  enum usb_device_speed speed)
>   {
> -	struct dwc3		*dwc = gadget_to_dwc(g);
> -	unsigned long		flags;
>   	u32			reg;
>   
> -	spin_lock_irqsave(&dwc->lock, flags);
>   	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
>   	reg &= ~(DWC3_DCFG_SPEED_MASK);
>   
> @@ -2175,7 +2172,16 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
>   		}
>   	}
>   	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
> +}
> +
> +static void dwc3_gadget_set_speed(struct usb_gadget *g,
> +				  enum usb_device_speed speed)
> +{
> +	struct dwc3		*dwc = gadget_to_dwc(g);
> +	unsigned long		flags;
>   
> +	spin_lock_irqsave(&dwc->lock, flags);
> +	__dwc3_gadget_set_speed(dwc, speed);
>   	spin_unlock_irqrestore(&dwc->lock, flags);
>   }
>   
> Then your patch would look like:
>
> static void dwc3_gadget_set_sublink_attr(struct usb_gadget *g,
> 					 unsigned int lane_count,
> 					 unsigned int lsm)
> {
> 	struct dwc3	*dwc = gadget_to_dwc(g);
> 	unsigned int	lanes;
> 	unsigned long	flags;
> 	u32		reg;
>
> 	spin_lock_irqsave(&dwc->lock, flags);
> 	if (dwc->maximum_speed <= USB_SPEED_SUPER) {
> 		/* Fall back to maximum speed supported by HW */
> 		__dwc3_gadget_set_speed(dwc, dwc->maximum_speed);
> 		goto done;
> 	}
>
> 	[...]
> }

Ok. I'll revise this.

Thanks!
Thinh
diff mbox series

Patch

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 0bae1beea8a6..d09e968644c1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1297,6 +1297,10 @@  static void dwc3_get_properties(struct dwc3 *dwc)
 				"snps,usb3_lpm_capable");
 	dwc->usb2_lpm_disable = device_property_read_bool(dev,
 				"snps,usb2-lpm-disable");
+	device_property_read_u8(dev, "snps,maximum-lane-count",
+				&dwc->maximum_lanes);
+	device_property_read_u8(dev, "snps,maximum-lsm",
+				&dwc->maximum_lsm);
 	device_property_read_u8(dev, "snps,rx-thr-num-pkt-prd",
 				&rx_thr_num_pkt_prd);
 	device_property_read_u8(dev, "snps,rx-max-burst-prd",
@@ -1424,6 +1428,38 @@  static void dwc3_check_params(struct dwc3 *dwc)
 
 		break;
 	}
+
+	switch (dwc->maximum_lsm) {
+	case 5:
+		break;
+	case 10:
+		if (dwc->maximum_speed == USB_SPEED_SUPER)
+			dev_err(dev, "invalid maximum_lsm parameter %d\n",
+				dwc->maximum_lsm);
+		/* Fall Through */
+	default:
+		if (dwc->maximum_speed == USB_SPEED_SUPER)
+			dwc->maximum_lsm = 5;
+		else if (dwc->maximum_speed > USB_SPEED_SUPER)
+			dwc->maximum_lsm = 10;
+		break;
+	}
+
+	switch (dwc->maximum_lanes) {
+	case 1:
+	case 2:
+		break;
+	default:
+		if (dwc->maximum_lanes > 2)
+			dev_err(dev, "invalid number of lanes %d\n",
+				dwc->maximum_lanes);
+
+		if (dwc3_is_usb32(dwc) &&
+		    dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
+			dwc->maximum_lanes = 2;
+		else
+			dwc->maximum_lanes = 1;
+	}
 }
 
 static int dwc3_probe(struct platform_device *pdev)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 7fde3c7da543..8e729d4cd5bd 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -376,6 +376,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)
 
@@ -449,6 +451,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)
 
@@ -946,6 +950,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_lanes: maximum lane count
  * @ip: controller's ID
  * @revision: controller's version of an IP
  * @version_type: VERSIONTYPE register contents, a sub release of a revision
@@ -973,6 +979,7 @@  struct dwc3_scratchpad_array {
  * @ep0state: state of endpoint zero
  * @link_state: link state
  * @speed: device speed (super, high, full, low)
+ * @lane_count: number of connected lanes
  * @hwparams: copy of hwparams registers
  * @root: debugfs root folder pointer
  * @regset: debugfs pointer to regdump file
@@ -1100,6 +1107,8 @@  struct dwc3 {
 	u32			nr_scratch;
 	u32			u1u2;
 	u32			maximum_speed;
+	u8			maximum_lsm;
+	u8			maximum_lanes;
 
 	u32			ip;
 
@@ -1159,6 +1168,7 @@  struct dwc3 {
 	u8			u1pel;
 
 	u8			speed;
+	u8			lane_count;
 
 	u8			num_eps;
 
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a6d562e208a9..c31144af3261 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -2183,6 +2183,53 @@  static void dwc3_gadget_set_speed(struct usb_gadget *g,
 	spin_unlock_irqrestore(&dwc->lock, flags);
 }
 
+static void dwc3_gadget_set_sublink_attr(struct usb_gadget *g,
+					 unsigned int lane_count,
+					 unsigned int lsm)
+{
+	struct dwc3	*dwc = gadget_to_dwc(g);
+	unsigned int	lanes;
+	unsigned long	flags;
+	u32		reg;
+
+	spin_lock_irqsave(&dwc->lock, flags);
+	if (dwc->maximum_speed <= USB_SPEED_SUPER) {
+		/* Fall back to maximum speed supported by HW */
+		spin_unlock_irqrestore(&dwc->lock, flags);
+		dwc3_gadget_set_speed(g, dwc->maximum_speed);
+		spin_lock_irqsave(&dwc->lock, flags);
+
+		goto done;
+	}
+
+	reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+	reg &= ~DWC3_DCFG_SPEED_MASK;
+
+	switch (lsm) {
+	case 5:
+		reg |= DWC3_DCFG_SUPERSPEED;
+		break;
+	case 10:
+		reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+		break;
+	default:
+		dev_err(dwc->dev, "invalid lane speed mantissa (%d)\n", lsm);
+		goto done;
+	}
+
+	/* Lane configuration is only available to dwc_usb32 and higher */
+	if (dwc3_is_usb32(dwc)) {
+		lanes = clamp_t(unsigned int, lane_count,
+				1, dwc->maximum_lanes);
+		reg &= ~DWC3_DCFG_NUMLANES(3);
+		reg |= DWC3_DCFG_NUMLANES(lanes - 1);
+	}
+
+	dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+done:
+	spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
 static const struct usb_gadget_ops dwc3_gadget_ops = {
 	.get_frame		= dwc3_gadget_get_frame,
 	.wakeup			= dwc3_gadget_wakeup,
@@ -2191,6 +2238,7 @@  static const struct usb_gadget_ops dwc3_gadget_ops = {
 	.udc_start		= dwc3_gadget_start,
 	.udc_stop		= dwc3_gadget_stop,
 	.udc_set_speed		= dwc3_gadget_set_speed,
+	.udc_set_sublink_attr	= dwc3_gadget_set_sublink_attr,
 	.get_config_params	= dwc3_gadget_config_params,
 };
 
@@ -3383,6 +3431,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.lane_count		= 1;
 
 	/*
 	 * FIXME We might be setting max_speed to <SUPER, however versions
@@ -3406,6 +3455,8 @@  int dwc3_gadget_init(struct dwc3 *dwc)
 				dwc->revision);
 
 	dwc->gadget.max_speed		= dwc->maximum_speed;
+	dwc->gadget.max_lsm		= dwc->maximum_lsm;
+	dwc->gadget.max_lane_count	= dwc->maximum_lanes;
 
 	/*
 	 * REVISIT: Here we should clear all pending IRQs to be
@@ -3422,7 +3473,12 @@  int dwc3_gadget_init(struct dwc3 *dwc)
 		goto err4;
 	}
 
-	dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
+	if (dwc3_is_usb32(dwc) && dwc->maximum_lsm)
+		dwc3_gadget_set_sublink_attr(&dwc->gadget,
+					     dwc->maximum_lanes,
+					     dwc->maximum_lsm);
+	else
+		dwc3_gadget_set_speed(&dwc->gadget, dwc->maximum_speed);
 
 	return 0;