mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Michael Grzeschik <m.grzeschik@pengutronix.de>
To: barebox@lists.infradead.org
Subject: [PATCH 5/5] usb: dwc2: add support to force gadget mode
Date: Wed, 16 Dec 2020 21:45:34 +0100	[thread overview]
Message-ID: <20201216204534.8054-6-m.grzeschik@pengutronix.de> (raw)
In-Reply-To: <20201216204534.8054-1-m.grzeschik@pengutronix.de>

In case the driver is to be run in peripheral mode rather than otg, it
needs to be forced in hardware to the selected mode. This patch adds
support to set and clear that hardware override. The functions are
mostly copied from linux.

Signed-off-by: Michael Grzeschik <m.grzeschik@pengutronix.de>
---
 drivers/usb/dwc2/gadget.c | 144 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 144 insertions(+)

diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index aa7447c9b4..b8ec37be78 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 #include <dma.h>
 #include <usb/gadget.h>
+#include <linux/iopoll.h>
 #include "dwc2.h"
 
 #define to_dwc2 gadget_to_dwc2
@@ -2660,6 +2661,144 @@ static int dwc2_eps_alloc(struct dwc2 *dwc2)
 	return 0;
 }
 
+/**
+ * dwc2_wait_for_mode() - Waits for the controller mode.
+ * @dwc2:	Programming view of the DWC_otg controller.
+ * @host_mode:	If true, waits for host mode, otherwise device mode.
+ */
+static void dwc2_wait_for_mode(struct dwc2 *dwc2, bool host_mode)
+{
+	int val, ret;
+
+	dev_vdbg(dwc2->dev, "Waiting for %s mode\n",
+		 host_mode ? "host" : "device");
+
+	ret = readx_poll_timeout(dwc2_is_host_mode, dwc2, val,
+			val == host_mode, 110 * USEC_PER_MSEC);
+	if (ret)
+		dev_err(dwc2->dev, "%s: Couldn't set %s mode\n",
+				 __func__, host_mode ? "host" : "device");
+
+	dev_vdbg(dwc2->dev, "%s mode set\n",
+		 host_mode ? "Host" : "Device");
+}
+
+/**
+ * dwc2_iddig_filter_enabled() - Returns true if the IDDIG debounce
+ * filter is enabled.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+static bool dwc2_iddig_filter_enabled(struct dwc2 *dwc2)
+{
+	u32 gsnpsid;
+	u32 ghwcfg4;
+
+	/* Check if core configuration includes the IDDIG filter. */
+	ghwcfg4 = dwc2_readl(dwc2, GHWCFG4);
+	if (!(ghwcfg4 & GHWCFG4_IDDIG_FILT_EN))
+		return false;
+
+	/*
+	 * Check if the IDDIG debounce filter is bypassed. Available
+	 * in core version >= 3.10a.
+	 */
+	gsnpsid = dwc2_readl(dwc2, GSNPSID);
+	if (gsnpsid >= DWC2_CORE_REV_3_10a) {
+		u32 gotgctl = dwc2_readl(dwc2, GOTGCTL);
+
+		if (gotgctl & GOTGCTL_DBNCE_FLTR_BYPASS)
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * dwc2_force_mode() - Force the mode of the controller.
+ *
+ * Forcing the mode is needed for two cases:
+ *
+ * 1) If the dr_mode is set to either HOST or PERIPHERAL we force the
+ * controller to stay in a particular mode regardless of ID pin
+ * changes. We do this once during probe.
+ *
+ * 2) During probe we want to read reset values of the hw
+ * configuration registers that are only available in either host or
+ * device mode. We may need to force the mode if the current mode does
+ * not allow us to access the register in the mode that we want.
+ *
+ * In either case it only makes sense to force the mode if the
+ * controller hardware is OTG capable.
+ *
+ * Checks are done in this function to determine whether doing a force
+ * would be valid or not.
+ *
+ * If a force is done, it requires a IDDIG debounce filter delay if
+ * the filter is configured and enabled. We poll the current mode of
+ * the controller to account for this delay.
+ *
+ * @dwc2: Programming view of DWC_otg controller
+ * @host: Host mode flag
+ */
+static void dwc2_force_mode(struct dwc2 *dwc2, bool host)
+{
+	u32 gusbcfg;
+	u32 set;
+	u32 clear;
+
+	dev_dbg(dwc2->dev, "Forcing mode to %s\n", host ? "host" : "device");
+
+	/*
+	 * If dr_mode is either peripheral or host only, there is no
+	 * need to ever force the mode to the opposite mode.
+	 */
+	if (WARN_ON(host && dwc2->dr_mode == USB_DR_MODE_PERIPHERAL))
+		return;
+
+	if (WARN_ON(!host && dwc2->dr_mode == USB_DR_MODE_HOST))
+		return;
+
+	gusbcfg = dwc2_readl(dwc2, GUSBCFG);
+
+	set = host ? GUSBCFG_FORCEHOSTMODE : GUSBCFG_FORCEDEVMODE;
+	clear = host ? GUSBCFG_FORCEDEVMODE : GUSBCFG_FORCEHOSTMODE;
+
+	gusbcfg &= ~clear;
+	gusbcfg |= set;
+	dwc2_writel(dwc2, gusbcfg, GUSBCFG);
+
+	dwc2_wait_for_mode(dwc2, host);
+
+	return;
+}
+
+/**
+ * dwc2_clear_force_mode() - Clears the force mode bits.
+ *
+ * After clearing the bits, wait up to 100 ms to account for any
+ * potential IDDIG filter delay. We can't know if we expect this delay
+ * or not because the value of the connector ID status is affected by
+ * the force mode. We only need to call this once during probe if
+ * dr_mode == OTG.
+ *
+ * @dwc2: Programming view of DWC_otg controller
+ */
+static void dwc2_clear_force_mode(struct dwc2 *dwc2)
+{
+	u32 gusbcfg;
+
+	dev_dbg(dwc2->dev, "Clearing force mode bits\n");
+
+	gusbcfg = dwc2_readl(dwc2, GUSBCFG);
+	gusbcfg &= ~GUSBCFG_FORCEHOSTMODE;
+	gusbcfg &= ~GUSBCFG_FORCEDEVMODE;
+	dwc2_writel(dwc2, gusbcfg, GUSBCFG);
+
+	if (dwc2_iddig_filter_enabled(dwc2))
+		mdelay(100);
+}
+
 int dwc2_gadget_init(struct dwc2 *dwc2)
 {
 	u32 dctl;
@@ -2684,6 +2823,11 @@ int dwc2_gadget_init(struct dwc2 *dwc2)
 
 	dwc2->gadget.is_otg = (dwc2->dr_mode == USB_DR_MODE_OTG) ? 1 : 0;
 
+	if (dwc2->gadget.is_otg)
+		dwc2_clear_force_mode(dwc2);
+	else
+		dwc2_force_mode(dwc2, false);
+
 	ret = dwc2_eps_alloc(dwc2);
 	if (ret) {
 		dwc2_err(dwc2, "Endpoints allocation failed: %d\n", ret);
-- 
2.29.2


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

      parent reply	other threads:[~2020-12-16 20:45 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-12-16 20:45 [PATCH 0/5] dwc2: improve gadget support Michael Grzeschik
2020-12-16 20:45 ` [PATCH 1/5] usb: dwc2: cleanup on error for deferred probing Michael Grzeschik
2020-12-17 11:00   ` Jules Maselbas
2020-12-16 20:45 ` [PATCH 2/5] usb: dwc2: add clk dependency for probe via oftree Michael Grzeschik
2020-12-16 20:45 ` [PATCH 3/5] usb: dwc2: add reset controller " Michael Grzeschik
2020-12-16 20:45 ` [PATCH 4/5] usb: dwc2: update the dr_mode on set_mode callback Michael Grzeschik
2020-12-16 20:45 ` Michael Grzeschik [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20201216204534.8054-6-m.grzeschik@pengutronix.de \
    --to=m.grzeschik@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox