usb: dwc3: gadget: Implement setting of SSP rate
authorThinh Nguyen <Thinh.Nguyen@synopsys.com>
Wed, 20 Jan 2021 01:36:28 +0000 (17:36 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 6 Feb 2021 13:21:21 +0000 (14:21 +0100)
Implement gadget ops udc_set_ssp_rate(). This allows the gadget/core
driver to select SSP signaling rate and number of lanes to for DWC_usb32
controller.

Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
Link: https://lore.kernel.org/r/8998b65fddfa02cab57bfc6aa35e9f101b252068.1611106162.git.Thinh.Nguyen@synopsys.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/dwc3/core.h
drivers/usb/dwc3/gadget.c

index a482f4e..61ac298 100644 (file)
 #define DWC3_GUCTL3_SPLITDISABLE               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)
 
@@ -966,6 +968,8 @@ struct dwc3_scratchpad_array {
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
  * @max_ssp_rate: SuperSpeed Plus maximum signaling rate and lane count
  * @gadget_max_speed: maximum gadget speed requested
+ * @gadget_ssp_rate: Gadget driver's maximum supported SuperSpeed Plus signaling
+ *                     rate and lane count.
  * @ip: controller's ID
  * @revision: controller's version of an IP
  * @version_type: VERSIONTYPE register contents, a sub release of a revision
@@ -1130,6 +1134,7 @@ struct dwc3 {
        u32                     maximum_speed;
        u32                     gadget_max_speed;
        enum usb_ssp_rate       max_ssp_rate;
+       enum usb_ssp_rate       gadget_ssp_rate;
 
        u32                     ip;
 
index 00ba8f1..14ab559 100644 (file)
@@ -2038,10 +2038,40 @@ static void dwc3_stop_active_transfers(struct dwc3 *dwc)
        }
 }
 
+static void __dwc3_gadget_set_ssp_rate(struct dwc3 *dwc)
+{
+       enum usb_ssp_rate       ssp_rate = dwc->gadget_ssp_rate;
+       u32                     reg;
+
+       if (ssp_rate == USB_SSP_GEN_UNKNOWN)
+               ssp_rate = dwc->max_ssp_rate;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+       reg &= ~DWC3_DCFG_SPEED_MASK;
+       reg &= ~DWC3_DCFG_NUMLANES(~0);
+
+       if (ssp_rate == USB_SSP_GEN_1x2)
+               reg |= DWC3_DCFG_SUPERSPEED;
+       else if (dwc->max_ssp_rate != USB_SSP_GEN_1x2)
+               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+
+       if (ssp_rate != USB_SSP_GEN_2x1 &&
+           dwc->max_ssp_rate != USB_SSP_GEN_2x1)
+               reg |= DWC3_DCFG_NUMLANES(1);
+
+       dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+}
+
 static void __dwc3_gadget_set_speed(struct dwc3 *dwc)
 {
        u32                     reg;
 
+       if (dwc->gadget_max_speed == USB_SPEED_SUPER_PLUS &&
+           DWC3_IP_IS(DWC32)) {
+               __dwc3_gadget_set_ssp_rate(dwc);
+               return;
+       }
+
        reg = dwc3_readl(dwc->regs, DWC3_DCFG);
        reg &= ~(DWC3_DCFG_SPEED_MASK);
 
@@ -2475,6 +2505,17 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
        spin_unlock_irqrestore(&dwc->lock, flags);
 }
 
+static void dwc3_gadget_set_ssp_rate(struct usb_gadget *g,
+                                    enum usb_ssp_rate rate)
+{
+       struct dwc3             *dwc = gadget_to_dwc(g);
+       unsigned long           flags;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc->gadget_ssp_rate = rate;
+       spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
 static int dwc3_gadget_vbus_draw(struct usb_gadget *g, unsigned int mA)
 {
        struct dwc3             *dwc = gadget_to_dwc(g);
@@ -2493,6 +2534,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_ssp_rate       = dwc3_gadget_set_ssp_rate,
        .get_config_params      = dwc3_gadget_config_params,
        .vbus_draw              = dwc3_gadget_vbus_draw,
 };
@@ -3905,7 +3947,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
                goto err5;
        }
 
-       dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
+       if (DWC3_IP_IS(DWC32) && dwc->maximum_speed == USB_SPEED_SUPER_PLUS)
+               dwc3_gadget_set_ssp_rate(dwc->gadget, dwc->max_ssp_rate);
+       else
+               dwc3_gadget_set_speed(dwc->gadget, dwc->maximum_speed);
 
        return 0;