usb: dwc2: Fix dr_mode validation
authorJohn Youn <John.Youn@synopsys.com>
Thu, 17 Dec 2015 19:16:31 +0000 (11:16 -0800)
committerFelipe Balbi <balbi@ti.com>
Tue, 22 Dec 2015 17:57:35 +0000 (11:57 -0600)
The dr_mode parameter was being checked against how the dwc2 module
was being configured at compile time. But it wasn't checked against
the hardware capabilities, nor were the hardware capabilities checked
against the compilation parameters.

This commit adds those checks and adjusts dr_mode to an appropriate
value, if needed. If the hardware capabilities and module compilation
do not match then we fail as it wouldn't be possible to run properly.

The hardware, module, and dr_mode, can each be set to host, device,
or otg. Check that all these values are compatible and adjust the
value of dr_mode if possible.

The following table summarizes the behavior:

                     actual
   HW  MOD dr_mode   dr_mode
 ------------------------------
  HST  HST  any    :  HST
  HST  DEV  any    :  ---
  HST  OTG  any    :  HST

  DEV  HST  any    :  ---
  DEV  DEV  any    :  DEV
  DEV  OTG  any    :  DEV

  OTG  HST  any    :  HST
  OTG  DEV  any    :  DEV
  OTG  OTG  any    :  dr_mode

Signed-off-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <balbi@ti.com>
drivers/usb/dwc2/platform.c

index 8bd7315a06c900048b791fe44c6dcdfafbdcd1ae..f4366513acb6b32641cc452565da5db2b5e1cc8a 100644 (file)
@@ -149,6 +149,71 @@ static const struct dwc2_core_params params_rk3066 = {
        .hibernation                    = -1,
 };
 
+/*
+ * Check the dr_mode against the module configuration and hardware
+ * capabilities.
+ *
+ * The hardware, module, and dr_mode, can each be set to host, device,
+ * or otg. Check that all these values are compatible and adjust the
+ * value of dr_mode if possible.
+ *
+ *                      actual
+ *    HW  MOD dr_mode   dr_mode
+ *  ------------------------------
+ *   HST  HST  any    :  HST
+ *   HST  DEV  any    :  ---
+ *   HST  OTG  any    :  HST
+ *
+ *   DEV  HST  any    :  ---
+ *   DEV  DEV  any    :  DEV
+ *   DEV  OTG  any    :  DEV
+ *
+ *   OTG  HST  any    :  HST
+ *   OTG  DEV  any    :  DEV
+ *   OTG  OTG  any    :  dr_mode
+ */
+static int dwc2_get_dr_mode(struct dwc2_hsotg *hsotg)
+{
+       enum usb_dr_mode mode;
+
+       hsotg->dr_mode = usb_get_dr_mode(hsotg->dev);
+       if (hsotg->dr_mode == USB_DR_MODE_UNKNOWN)
+               hsotg->dr_mode = USB_DR_MODE_OTG;
+
+       mode = hsotg->dr_mode;
+
+       if (dwc2_hw_is_device(hsotg)) {
+               if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) {
+                       dev_err(hsotg->dev,
+                               "Controller does not support host mode.\n");
+                       return -EINVAL;
+               }
+               mode = USB_DR_MODE_PERIPHERAL;
+       } else if (dwc2_hw_is_host(hsotg)) {
+               if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL)) {
+                       dev_err(hsotg->dev,
+                               "Controller does not support device mode.\n");
+                       return -EINVAL;
+               }
+               mode = USB_DR_MODE_HOST;
+       } else {
+               if (IS_ENABLED(CONFIG_USB_DWC2_HOST))
+                       mode = USB_DR_MODE_HOST;
+               else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL))
+                       mode = USB_DR_MODE_PERIPHERAL;
+       }
+
+       if (mode != hsotg->dr_mode) {
+               dev_warn(hsotg->dev,
+                       "Configuration mismatch. dr_mode forced to %s\n",
+                       mode == USB_DR_MODE_HOST ? "host" : "device");
+
+               hsotg->dr_mode = mode;
+       }
+
+       return 0;
+}
+
 static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg)
 {
        struct platform_device *pdev = to_platform_device(hsotg->dev);
@@ -412,19 +477,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
        dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
                (unsigned long)res->start, hsotg->regs);
 
-       hsotg->dr_mode = usb_get_dr_mode(&dev->dev);
-       if (IS_ENABLED(CONFIG_USB_DWC2_HOST) &&
-                       hsotg->dr_mode != USB_DR_MODE_HOST) {
-               hsotg->dr_mode = USB_DR_MODE_HOST;
-               dev_warn(hsotg->dev,
-                       "Configuration mismatch. Forcing host mode\n");
-       } else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) &&
-                       hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
-               hsotg->dr_mode = USB_DR_MODE_PERIPHERAL;
-               dev_warn(hsotg->dev,
-                       "Configuration mismatch. Forcing peripheral mode\n");
-       }
-
        retval = dwc2_lowlevel_hw_init(hsotg);
        if (retval)
                return retval;
@@ -456,6 +508,10 @@ static int dwc2_driver_probe(struct platform_device *dev)
        if (retval)
                return retval;
 
+       retval = dwc2_get_dr_mode(hsotg);
+       if (retval)
+               return retval;
+
        /*
         * Reset before dwc2_get_hwparams() then it could get power-on real
         * reset value form registers.