[PATCH] usb: dwc3: Fix dr_mode validation

From: John Youn
Date: Thu Sep 01 2016 - 20:38:56 EST


From: Thinh Nguyen <thinhn@xxxxxxxxxxxx>

This patch follows the similar fix in dwc2. See
commit 5268ed9d2e3b ("usb: dwc2: Fix dr_mode validation")

Currently, the dr_mode is only checked against the module configuration.
It also needs to be checked against the hardware capablities.

The driver now checks if both the module configuration and hardware are
capable of the dr_mode value. If not, then it will issue a warning and
fall back to a supported value. If it is unable to fall back to a
suitable value, then the probe will fail.

Behavior summary:

module : actual
HW config dr_mode : dr_mode
---------------------------------
host host any : host
host dev any : INVALID
host otg any : host

dev host any : INVALID
dev dev any : dev
dev otg any : dev

otg host any : host
otg dev any : dev
otg otg any : dr_mode

Signed-off-by: Thinh Nguyen <thinhn@xxxxxxxxxxxx>
Signed-off-by: John Youn <johnyoun@xxxxxxxxxxxx>
---
drivers/usb/dwc3/core.c | 84 ++++++++++++++++++++++++++++++++++++++++++-------
drivers/usb/dwc3/core.h | 5 ++-
2 files changed, 77 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index d6d3fa0..f30cffa 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -49,6 +49,76 @@

#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */

+/* Returns the controller's GHWPARAMS0.DWC_USB3_MODE. */
+static unsigned int dwc3_usb3_mode(struct dwc3 *dwc)
+{
+ u32 ghwparams0_2_0 = dwc3_readl(dwc->regs, DWC3_GHWPARAMS0);
+
+ return DWC3_GHWPARAMS0_MODE(ghwparams0_2_0);
+}
+
+/* Returns true if the controller is host-only. */
+static bool dwc3_hw_is_host(struct dwc3 *dwc)
+{
+ unsigned int mode = dwc3_usb3_mode(dwc);
+
+ return mode == DWC3_GHWPARAMS0_MODE_HOST;
+}
+
+/* Returns true if the controller is device-only. */
+static bool dwc3_hw_is_gadget(struct dwc3 *dwc)
+{
+ unsigned int mode = dwc3_usb3_mode(dwc);
+
+ return mode == DWC3_GHWPARAMS0_MODE_GADGET;
+}
+
+/**
+ * dwc3_get_dr_mode - Validates and sets dr_mode
+ * @dwc: pointer to our context structure
+ */
+static int dwc3_get_dr_mode(struct dwc3 *dwc)
+{
+ enum usb_dr_mode mode;
+ struct device *dev = dwc->dev;
+
+ if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+ dwc->dr_mode = USB_DR_MODE_OTG;
+
+ mode = dwc->dr_mode;
+
+ if (dwc3_hw_is_gadget(dwc)) {
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) {
+ dev_err(dev,
+ "Controller does not support host mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_PERIPHERAL;
+ } else if (dwc3_hw_is_host(dwc)) {
+ if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) {
+ dev_err(dev,
+ "Controller does not support device mode.\n");
+ return -EINVAL;
+ }
+ mode = USB_DR_MODE_HOST;
+ } else {
+ if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+ mode = USB_DR_MODE_HOST;
+ else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+ mode = USB_DR_MODE_PERIPHERAL;
+ }
+
+ if (mode != dwc->dr_mode) {
+ dev_warn(dev,
+ "Configuration mismatch. dr_mode forced to %s\n",
+ mode == USB_DR_MODE_HOST ? "host" : "gadget");
+
+ dwc->dr_mode = mode;
+ }
+
+ return 0;
+}
+
void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
{
u32 reg;
@@ -1023,17 +1093,9 @@ static int dwc3_probe(struct platform_device *pdev)
goto err2;
}

- if (IS_ENABLED(CONFIG_USB_DWC3_HOST) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) &&
- (dwc->dr_mode == USB_DR_MODE_OTG ||
- dwc->dr_mode == USB_DR_MODE_UNKNOWN))
- dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
-
- if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
- dwc->dr_mode = USB_DR_MODE_OTG;
+ ret = dwc3_get_dr_mode(dwc);
+ if (ret)
+ goto err3;

ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index b2317e7..6b60e42 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -245,7 +245,10 @@
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)

/* Global HWPARAMS0 Register */
-#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE(n) ((n) & 0x3)
+#define DWC3_GHWPARAMS0_MODE_GADGET 0
+#define DWC3_GHWPARAMS0_MODE_HOST 1
+#define DWC3_GHWPARAMS0_MODE_DRD 2
#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7)
#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3)
#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff)
--
2.9.0