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. Signed-off-by: Thinh Nguyen <thinhn@xxxxxxxxxxxx> --- drivers/usb/gadget/udc/core.c | 24 +++++++++++++++++++----- include/linux/usb/gadget.h | 3 +++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 2e28dde8376f..1e916b3943fe 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -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); } /** diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 2ab929eed928..ec4ed17e7bf5 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -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 *); -- 2.11.0