usb: dwc3: gadget: implement ->udc_set_speed()
authorFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 6 Jun 2017 13:05:23 +0000 (16:05 +0300)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 13 Jun 2017 10:21:03 +0000 (13:21 +0300)
Use this method to make sure we don't try to connect on speeds not
supported by the gadget driver.

Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/dwc3/gadget.c

index d2bd28dc28b6993428c7e7a2b7853b1bd0f1c4dd..01cd7ddc9981b56d4e53ef889efd5069ca6ad582 100644 (file)
@@ -1827,49 +1827,6 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
                dwc3_writel(dwc->regs, DWC3_DEV_IMOD(0), 0);
        }
 
-       reg = dwc3_readl(dwc->regs, DWC3_DCFG);
-       reg &= ~(DWC3_DCFG_SPEED_MASK);
-
-       /*
-        * WORKAROUND: DWC3 revision < 2.20a have an issue
-        * which would cause metastability state on Run/Stop
-        * bit if we try to force the IP to USB2-only mode.
-        *
-        * Because of that, we cannot configure the IP to any
-        * speed other than the SuperSpeed
-        *
-        * Refers to:
-        *
-        * STAR#9000525659: Clock Domain Crossing on DCTL in
-        * USB 2.0 Mode
-        */
-       if (dwc->revision < DWC3_REVISION_220A) {
-               reg |= DWC3_DCFG_SUPERSPEED;
-       } else {
-               switch (dwc->maximum_speed) {
-               case USB_SPEED_LOW:
-                       reg |= DWC3_DCFG_LOWSPEED;
-                       break;
-               case USB_SPEED_FULL:
-                       reg |= DWC3_DCFG_FULLSPEED;
-                       break;
-               case USB_SPEED_HIGH:
-                       reg |= DWC3_DCFG_HIGHSPEED;
-                       break;
-               case USB_SPEED_SUPER_PLUS:
-                       reg |= DWC3_DCFG_SUPERSPEED_PLUS;
-                       break;
-               default:
-                       dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n",
-                               dwc->maximum_speed);
-                       /* fall through */
-               case USB_SPEED_SUPER:
-                       reg |= DWC3_DCFG_SUPERSPEED;
-                       break;
-               }
-       }
-       dwc3_writel(dwc->regs, DWC3_DCFG, reg);
-
        /*
         * We are telling dwc3 that we want to use DCFG.NUMP as ACK TP's NUMP
         * field instead of letting dwc3 itself calculate that automatically.
@@ -2001,6 +1958,63 @@ out:
        return 0;
 }
 
+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;
+       u32                     reg;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       reg = dwc3_readl(dwc->regs, DWC3_DCFG);
+       reg &= ~(DWC3_DCFG_SPEED_MASK);
+
+       /*
+        * WORKAROUND: DWC3 revision < 2.20a have an issue
+        * which would cause metastability state on Run/Stop
+        * bit if we try to force the IP to USB2-only mode.
+        *
+        * Because of that, we cannot configure the IP to any
+        * speed other than the SuperSpeed
+        *
+        * Refers to:
+        *
+        * STAR#9000525659: Clock Domain Crossing on DCTL in
+        * USB 2.0 Mode
+        */
+       if (dwc->revision < DWC3_REVISION_220A) {
+               reg |= DWC3_DCFG_SUPERSPEED;
+       } else {
+               switch (speed) {
+               case USB_SPEED_LOW:
+                       reg |= DWC3_DCFG_LOWSPEED;
+                       break;
+               case USB_SPEED_FULL:
+                       reg |= DWC3_DCFG_FULLSPEED;
+                       break;
+               case USB_SPEED_HIGH:
+                       reg |= DWC3_DCFG_HIGHSPEED;
+                       break;
+               case USB_SPEED_SUPER:
+                       reg |= DWC3_DCFG_SUPERSPEED;
+                       break;
+               case USB_SPEED_SUPER_PLUS:
+                       reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+                       break;
+               default:
+                       dev_err(dwc->dev, "invalid speed (%d)\n", speed);
+
+                       if (dwc->revision & DWC3_REVISION_IS_DWC31)
+                               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
+                       else
+                               reg |= DWC3_DCFG_SUPERSPEED;
+               }
+       }
+       dwc3_writel(dwc->regs, DWC3_DCFG, reg);
+
+       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,
@@ -2008,6 +2022,7 @@ static const struct usb_gadget_ops dwc3_gadget_ops = {
        .pullup                 = dwc3_gadget_pullup,
        .udc_start              = dwc3_gadget_start,
        .udc_stop               = dwc3_gadget_stop,
+       .udc_set_speed          = dwc3_gadget_set_speed,
 };
 
 /* -------------------------------------------------------------------------- */