diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 8bd7315a06c9..f4366513acb6 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -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.