U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Stefan Eichenberger <eichest@gmail.com>
To: francesco.dolcini@toradex.com, trini@konsulko.com,
	ilias.apalodimas@linaro.org, sbabic@nabladev.com,
	festevam@gmail.com, uboot-imx@nxp.com,
	marek.vasut+usb@mailbox.org, lukma@denx.de,
	mkorpershoek@kernel.org, antoine.gouby@toradex.com,
	alice.guo@nxp.com, stefan.eichenberger@toradex.com,
	sjg@chromium.org, emanuele.ghidoli@toradex.com,
	franz.schnyder@toradex.com, pbrobinson@gmail.com,
	quentin.schulz@cherry.de, jbx6244@gmail.com, ye.li@nxp.com,
	xu.yang_2@nxp.com, bmeng.cn@gmail.com, xypron.glpk@gmx.de,
	hayashi.kunihiko@socionext.com, sean.anderson@linux.dev
Cc: u-boot@lists.u-boot-project.org
Subject: [PATCH v2 1/7] usb: ci_udc: add driver model support
Date: Mon, 14 Sep 2026 09:42:43 +0200	[thread overview]
Message-ID: <20260914074314.12522-2-eichest@gmail.com> (raw)
In-Reply-To: <20260914074314.12522-1-eichest@gmail.com>

From: Stefan Eichenberger <stefan.eichenberger@toradex.com>

Register the ChipIdea gadget as a UCLASS_USB_GADGET_GENERIC driver so it
can be used on boards with DM_USB_GADGET enabled, which CI_UDC no longer
excludes. The driver enables the clocks and the PHY of its device tree
node and registers the gadget with usb_add_gadget_udc().

It has no of_match of its own. The controller node is shared with the
EHCI host driver, which owns it and binds this driver by name, so CI_UDC
depends on that driver once DM_USB_GADGET is enabled.

Boards without DM_USB_GADGET keep the existing behaviour.

Signed-off-by: Stefan Eichenberger <stefan.eichenberger@toradex.com>
---
 drivers/usb/gadget/Kconfig  |   8 +-
 drivers/usb/gadget/ci_udc.c | 152 +++++++++++++++++++++++++++++++++++-
 2 files changed, 158 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index e42d5a43696..67826ee5150 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -153,12 +153,18 @@ config USB_GADGET_OS_DESCRIPTORS
 
 config CI_UDC
 	bool "ChipIdea device controller"
-	depends on !DM_USB_GADGET
+	depends on !DM_USB_GADGET || USB_EHCI_MX6 || USB_EHCI_MX7
 	select USB_GADGET_DUALSPEED
 	help
 	  Say Y here to enable device controller functionality of the
 	  ChipIdea driver.
 
+	  With DM_USB_GADGET enabled this driver has no device tree match of
+	  its own. The EHCI host driver owns the controller node and binds
+	  this driver by name for the ports whose dr_mode is "peripheral" or
+	  "otg", which is why it is required. Without DM_USB_GADGET the
+	  controller is picked up through the EHCI host driver as before.
+
 config USB_GADGET_VBUS_DRAW
 	int "Maximum VBUS Power usage (2-500 mA)"
 	range 2 500
diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 53796887dac..f63cb17b58f 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -7,14 +7,18 @@
  * Murray.Jensen@cmst.csiro.au, 27-Jan-01.
  */
 
+#include <clk.h>
 #include <command.h>
 #include <config.h>
 #include <cpu_func.h>
+#include <dm.h>
+#include <generic-phy.h>
 #include <net.h>
 #include <malloc.h>
 #include <wait_bit.h>
 #include <asm/byteorder.h>
 #include <asm/cache.h>
+#include <dm/device_compat.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <asm/io.h>
@@ -23,6 +27,7 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <usb/ci_udc.h>
+#include <usb/ehci-ci.h>
 #include "../host/ehci.h"
 #include "ci_udc.h"
 
@@ -93,9 +98,18 @@ static int ci_ep_dequeue(struct usb_ep *ep, struct usb_request *req);
 static struct usb_request *
 ci_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags);
 static void ci_ep_free_request(struct usb_ep *ep, struct usb_request *_req);
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+static int ci_udc_start(struct usb_gadget *gadget,
+			struct usb_gadget_driver *driver);
+static int ci_udc_stop(struct usb_gadget *gadget);
+#endif
 
 static const struct usb_gadget_ops ci_udc_ops = {
 	.pullup = ci_pullup,
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+	.udc_start = ci_udc_start,
+	.udc_stop = ci_udc_stop,
+#endif
 };
 
 static const struct usb_ep_ops ci_ep_ops = {
@@ -994,7 +1008,7 @@ void udc_irq(void)
 	}
 }
 
-int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+static int ci_udc_gadget_handle_interrupts(struct udevice *dev)
 {
 	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
 	u32 value;
@@ -1139,6 +1153,12 @@ static int ci_udc_probe(void)
 	return 0;
 }
 
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+	return ci_udc_gadget_handle_interrupts(dev);
+}
+
 int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 {
 	int ret;
@@ -1192,6 +1212,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 
 	return 0;
 }
+#endif /* !DM_USB_GADGET */
 
 bool dfu_usb_get_reset(void)
 {
@@ -1199,3 +1220,132 @@ bool dfu_usb_get_reset(void)
 
 	return !!(readl(&udc->usbsts) & STS_URI);
 }
+
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+struct ci_udc_priv {
+	/* Only hccr and hcor are used, the gadget does not drive EHCI. */
+	struct ehci_ctrl	ctrl;
+	struct clk_bulk		clks;
+	struct phy		phy;
+};
+
+static int ci_udc_start(struct usb_gadget *gadget,
+			struct usb_gadget_driver *driver)
+{
+	if (!driver || !driver->bind || !driver->setup || !driver->disconnect)
+		return -EINVAL;
+
+	controller.driver = driver;
+
+	return 0;
+}
+
+static int ci_udc_stop(struct usb_gadget *gadget)
+{
+	controller.driver = NULL;
+
+	return 0;
+}
+
+static int ci_udc_clk_init(struct udevice *dev, struct clk_bulk *clks)
+{
+	int ret;
+
+	ret = clk_get_bulk(dev, clks);
+	if (ret == -ENOSYS || ret == -ENOENT)
+		return 0;
+	if (ret)
+		return ret;
+
+	ret = clk_enable_bulk(clks);
+	if (ret) {
+		clk_release_bulk(clks);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ci_udc_dm_probe(struct udevice *dev)
+{
+	struct ci_udc_priv *priv = dev_get_priv(dev);
+	struct usb_ehci *ehci;
+	int ret;
+
+	ehci = dev_read_addr_ptr(dev);
+	if (!ehci)
+		return -EINVAL;
+
+	ret = ci_udc_clk_init(dev, &priv->clks);
+	if (ret)
+		return ret;
+
+	ret = generic_setup_phy(dev, &priv->phy, 0, PHY_MODE_USB_DEVICE, 0);
+	if (ret)
+		goto err_clk;
+
+	/* Same register layout the EHCI host driver derives its regs from. */
+	priv->ctrl.hccr = (struct ehci_hccr *)&ehci->caplength;
+	priv->ctrl.hcor = (struct ehci_hcor *)((uintptr_t)priv->ctrl.hccr +
+			  HC_LENGTH(ehci_readl(&priv->ctrl.hccr->cr_capbase)));
+	controller.ctrl = &priv->ctrl;
+
+	ret = ci_udc_probe();
+	if (ret) {
+		dev_err(dev, "udc probe failed, returned %d\n", ret);
+		goto err_phy;
+	}
+
+	ret = usb_add_gadget_udc((struct device *)dev, &controller.gadget);
+	if (ret)
+		goto err_udc;
+
+	return 0;
+
+err_udc:
+	ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
+	free(controller.items_mem);
+	free(controller.epts);
+err_phy:
+	generic_shutdown_phy(&priv->phy);
+err_clk:
+	clk_release_bulk(&priv->clks);
+	return ret;
+}
+
+static int ci_udc_dm_remove(struct udevice *dev)
+{
+	struct ci_udc_priv *priv = dev_get_priv(dev);
+
+	usb_del_gadget_udc(&controller.gadget);
+
+	ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
+	free(controller.items_mem);
+	free(controller.epts);
+	controller.ctrl = NULL;
+
+	generic_shutdown_phy(&priv->phy);
+	clk_release_bulk(&priv->clks);
+
+	return 0;
+}
+
+static const struct usb_gadget_generic_ops ci_udc_gadget_ops = {
+	.handle_interrupts	= ci_udc_gadget_handle_interrupts,
+};
+
+/*
+ * No of_match: the ChipIdea controller shares its device tree node with the
+ * EHCI host driver, which owns the node and binds this driver by name for
+ * the ports that are usable in device mode.
+ */
+U_BOOT_DRIVER(ci_udc) = {
+	.name		= "ci_udc",
+	.id		= UCLASS_USB_GADGET_GENERIC,
+	.ops		= &ci_udc_gadget_ops,
+	.probe		= ci_udc_dm_probe,
+	.remove		= ci_udc_dm_remove,
+	.priv_auto	= sizeof(struct ci_udc_priv),
+	.flags		= DM_FLAG_ALLOC_PRIV_DMA,
+};
+#endif /* DM_USB_GADGET */
-- 
2.55.0


  reply	other threads:[~2026-09-14 14:17 UTC|newest]

Thread overview: 16+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-14  7:42 [PATCH v2 0/7] usb: fix DWC3/gadget issues and enable USB on Toradex i.MX95 boards Stefan Eichenberger
2026-09-14  7:42 ` Stefan Eichenberger [this message]
2026-09-14  7:42 ` [PATCH v2 2/7] usb: ehci-mx6: bind the ChipIdea gadget driver Stefan Eichenberger
2026-09-14  7:42 ` [PATCH v2 3/7] usb: dwc3-generic: fix ref clock lookup indexing wrong clk_bulk array Stefan Eichenberger
2026-09-14 19:26   ` Marek Vasut
2026-09-15  7:44     ` Stefan Eichenberger
2026-09-15 14:22       ` Marek Vasut
2026-09-14  7:42 ` [PATCH v2 4/7] usb: skip host-only devices when picking the ehci gadget controller Stefan Eichenberger
2026-09-14 19:31   ` Marek Vasut
2026-09-15  7:57     ` Stefan Eichenberger
2026-09-15 14:24       ` Marek Vasut
2026-09-16 12:47         ` Stefan Eichenberger
2026-09-16 14:29           ` Marek Vasut
2026-09-14  7:42 ` [PATCH v2 5/7] configs: verdin-imx95: enable CI_UDC usb gadget driver Stefan Eichenberger
2026-09-14  7:42 ` [PATCH v2 6/7] toradex: verdin-imx95: enable DWC3 USB controller support Stefan Eichenberger
2026-09-14  7:42 ` [PATCH v2 7/7] toradex: smarc-imx95: " Stefan Eichenberger

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=20260914074314.12522-2-eichest@gmail.com \
    --to=eichest@gmail.com \
    --cc=alice.guo@nxp.com \
    --cc=antoine.gouby@toradex.com \
    --cc=bmeng.cn@gmail.com \
    --cc=emanuele.ghidoli@toradex.com \
    --cc=festevam@gmail.com \
    --cc=francesco.dolcini@toradex.com \
    --cc=franz.schnyder@toradex.com \
    --cc=hayashi.kunihiko@socionext.com \
    --cc=ilias.apalodimas@linaro.org \
    --cc=jbx6244@gmail.com \
    --cc=lukma@denx.de \
    --cc=marek.vasut+usb@mailbox.org \
    --cc=mkorpershoek@kernel.org \
    --cc=pbrobinson@gmail.com \
    --cc=quentin.schulz@cherry.de \
    --cc=sbabic@nabladev.com \
    --cc=sean.anderson@linux.dev \
    --cc=sjg@chromium.org \
    --cc=stefan.eichenberger@toradex.com \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.u-boot-project.org \
    --cc=uboot-imx@nxp.com \
    --cc=xu.yang_2@nxp.com \
    --cc=xypron.glpk@gmx.de \
    --cc=ye.li@nxp.com \
    /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