[PATCH v1 4/7] usb: ci_udc: Convert driver to DM_USB_GADGET

alice.guo at oss.nxp.com alice.guo at oss.nxp.com
Tue Dec 16 07:38:36 CET 2025


From: Sherry Sun <sherry.sun at nxp.com>

Convert the ci_udc driver to driver model by using the uclass
UCLASS_USB_GADGET_GENERIC. The clk and power of USB controller and USB
PHY both are initialized by parsing the device tree nodes.

If CONFIG_DM_USB_GADGET is defined, we use the ci_udc driver in DM way,
if it does not defined, we can use ci_udc driver in its original Non-DM
way.

Signed-off-by: Sherry Sun <sherry.sun at nxp.com>
Signed-off-by: Alice Guo <alice.guo at nxp.com>
Reviewed-by: Ye Li <ye.li at nxp.com>
---
 drivers/usb/gadget/ci_udc.c | 352 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 348 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 4bff75da759..154a3ab7f22 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -7,9 +7,17 @@
  * Murray.Jensen at cmst.csiro.au, 27-Jan-01.
  */
 
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/clock.h>
+#include <asm/mach-imx/regs-usbphy.h>
+#include <clk.h>
 #include <command.h>
 #include <config.h>
 #include <cpu_func.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/pinctrl.h>
+#include <power-domain.h>
 #include <net.h>
 #include <malloc.h>
 #include <wait_bit.h>
@@ -22,6 +30,8 @@
 #include <linux/types.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <usb/ehci-ci.h>
 #include <usb/ci_udc.h>
 #include "../host/ehci.h"
 #include "ci_udc.h"
@@ -93,9 +103,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_gadget_start(struct usb_gadget *g,
+			       struct usb_gadget_driver *driver);
+static int ci_udc_gadget_stop(struct usb_gadget *g);
+#endif
 
 static const struct usb_gadget_ops ci_udc_ops = {
 	.pullup = ci_pullup,
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+	.udc_start		= ci_udc_gadget_start,
+	.udc_stop		= ci_udc_gadget_stop,
+#endif
 };
 
 static const struct usb_ep_ops ci_ep_ops = {
@@ -927,7 +946,7 @@ void udc_irq(void)
 	}
 }
 
-int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+int ci_udc_handle_interrupts(void)
 {
 	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
 	u32 value;
@@ -1072,6 +1091,19 @@ static int ci_udc_probe(void)
 	return 0;
 }
 
+bool dfu_usb_get_reset(void)
+{
+	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+
+	return !!(readl(&udc->usbsts) & STS_URI);
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+	return ci_udc_handle_interrupts();
+}
+
 int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 {
 	int ret;
@@ -1125,10 +1157,322 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 
 	return 0;
 }
+#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
 
-bool dfu_usb_get_reset(void)
+static int ci_udc_gadget_start(struct usb_gadget *g,
+			       struct usb_gadget_driver *driver)
 {
-	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
+	if (!driver)
+		return -EINVAL;
+	if (!driver->bind || !driver->setup || !driver->disconnect)
+		return -EINVAL;
 
-	return !!(readl(&udc->usbsts) & STS_URI);
+	controller.driver = driver;
+	return 0;
+}
+
+static int ci_udc_gadget_stop(struct usb_gadget *g)
+{
+	controller.driver = NULL;
+
+	ci_ep_free_request(&controller.ep[0].ep, &controller.ep0_req->req);
+	free(controller.items_mem);
+	free(controller.epts);
+	return 0;
+}
+
+struct ci_udc_priv_data {
+	struct ehci_ctrl ctrl;
+	struct udevice otgdev;
+	struct clk_bulk		clks;
+	int phy_off;
+	struct power_domain otg_pd;
+	struct clk phy_clk;
+	struct power_domain phy_pd;
+	struct ehci_mx6_phy_data phy_data;
+};
+
+static int ci_udc_gadget_handle_interrupts(struct udevice *dev)
+{
+	return ci_udc_handle_interrupts();
+}
+
+static int ci_udc_phy_setup(struct udevice *dev, struct ci_udc_priv_data *priv)
+{
+	void *__iomem addr;
+	int misc_off;
+	struct udevice __maybe_unused phy_dev;
+
+	priv->phy_off = fdtdec_lookup_phandle(gd->fdt_blob,
+					      dev_of_offset(dev),
+					      "fsl,usbphy");
+	if (priv->phy_off < 0) {
+		priv->phy_off = fdtdec_lookup_phandle(gd->fdt_blob,
+						      dev_of_offset(dev), "phys");
+		if (priv->phy_off < 0)
+			return -EINVAL;
+	}
+
+	addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, priv->phy_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		addr = NULL;
+
+	priv->phy_data.phy_addr = addr;
+
+	misc_off = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev), "fsl,usbmisc");
+	if (misc_off < 0)
+		return -EINVAL;
+
+	addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, misc_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->phy_data.misc_addr = addr;
+
+#if defined(CONFIG_MX6)
+	int anatop_off;
+
+	/* Resolve ANATOP offset through USB PHY node */
+	anatop_off = fdtdec_lookup_phandle(gd->fdt_blob, priv->phy_off, "fsl,anatop");
+	if (anatop_off < 0)
+		return -EINVAL;
+
+	addr = (void __iomem *)fdtdec_get_addr(gd->fdt_blob, anatop_off, "reg");
+	if ((fdt_addr_t)addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->phy_data.anatop_addr = addr;
+#endif
+
+	dev_set_ofnode(&phy_dev, offset_to_ofnode(priv->phy_off));
+
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+	/* Need to power on the PHY before access it */
+	if (!power_domain_get(&phy_dev, &priv->phy_pd)) {
+		if (power_domain_on(&priv->phy_pd))
+			return -EINVAL;
+	}
+#endif
+
+#if CONFIG_IS_ENABLED(CLK)
+	int ret;
+
+	ret = clk_get_by_index(&phy_dev, 0, &priv->phy_clk);
+	if (ret) {
+		printf("Failed to get phy_clk\n");
+		return ret;
+	}
+
+	ret = clk_enable(&priv->phy_clk);
+	if (ret) {
+		printf("Failed to enable phy_clk\n");
+		return ret;
+	}
+#endif
+
+	return 0;
 }
+
+static int ci_udc_phy_shutdown(struct ci_udc_priv_data *priv)
+{
+	int ret = 0;
+
+#if CONFIG_IS_ENABLED(CLK)
+	if (priv->phy_clk.dev) {
+		ret = clk_disable(&priv->phy_clk);
+		if (ret)
+			return ret;
+	}
+#endif
+
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+	ret = power_domain_off(&priv->phy_pd);
+	if (ret)
+		printf("Power down USB PHY failed! (error = %d)\n", ret);
+#endif
+	return ret;
+}
+
+static int ci_udc_otg_clk_init(struct udevice *dev,
+			       struct clk_bulk *clks)
+{
+#if CONFIG_IS_ENABLED(CLK)
+	int ret;
+
+	ret = clk_get_bulk(dev, clks);
+	if (ret == -ENOSYS)
+		return 0;
+
+	if (ret)
+		return ret;
+
+	ret = clk_enable_bulk(clks);
+	if (ret) {
+		clk_release_bulk(clks);
+		return ret;
+	}
+#else
+	enable_usboh3_clk(1);
+#endif
+
+	return 0;
+}
+
+static int ci_udc_otg_phy_mode(struct udevice *dev)
+{
+	struct ci_udc_priv_data *priv = dev_get_priv(dev);
+
+	void *__iomem phy_ctrl, *__iomem phy_status;
+	void *__iomem phy_base = (void *__iomem)devfdt_get_addr(&priv->otgdev);
+	u32 val;
+
+	if (is_mx6() || is_mx7ulp() || is_imx8()) {
+		phy_base = (void __iomem *)fdtdec_get_addr(gd->fdt_blob,
+							   priv->phy_off,
+							   "reg");
+		if ((fdt_addr_t)phy_base == FDT_ADDR_T_NONE)
+			return -EINVAL;
+
+		phy_ctrl = (void __iomem *)(phy_base + USBPHY_CTRL);
+		val = readl(phy_ctrl);
+		if (val & USBPHY_CTRL_OTG_ID)
+			return USB_INIT_DEVICE;
+		else
+			return USB_INIT_HOST;
+	} else if (is_mx7() || is_imx8mm() || is_imx8mn()) {
+		phy_status = (void __iomem *)(phy_base +
+					      USBNC_PHY_STATUS_OFFSET);
+		val = readl(phy_status);
+		if (val & USBNC_PHYSTATUS_ID_DIG)
+			return USB_INIT_DEVICE;
+		else
+			return USB_INIT_HOST;
+	} else {
+		return -EINVAL;
+	}
+}
+
+static int ci_udc_otg_ofdata_to_platdata(struct udevice *dev)
+{
+	struct ci_udc_priv_data *priv = dev_get_priv(dev);
+	int node = dev_of_offset(dev);
+	int usbotg_off;
+
+	if (usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_PERIPHERAL) {
+		dev_dbg(dev, "Invalid mode\n");
+		return -ENODEV;
+	}
+
+	usbotg_off = fdtdec_lookup_phandle(gd->fdt_blob,
+					   node,
+					   "chipidea,usb");
+	if (usbotg_off < 0)
+		return -EINVAL;
+	dev_set_ofnode(&priv->otgdev, offset_to_ofnode(usbotg_off));
+	priv->otgdev.parent = dev->parent;
+
+	return 0;
+}
+
+static int ci_udc_otg_probe(struct udevice *dev)
+{
+	struct ci_udc_priv_data *priv = dev_get_priv(dev);
+	struct usb_ehci *ehci;
+	int ret;
+
+	ehci = (struct usb_ehci *)devfdt_get_addr(&priv->otgdev);
+
+	pinctrl_select_state(&priv->otgdev, "default");
+
+#if defined(CONFIG_MX6)
+	if (usb_fused((u32)ehci)) {
+		printf("USB at 0x%x is fused, disable it\n", (u32)ehci);
+		return -ENODEV;
+	}
+#endif
+
+	ret = board_usb_init(dev_seq(dev), USB_INIT_DEVICE);
+	if (ret) {
+		printf("Failed to initialize board for USB\n");
+		return ret;
+	}
+
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+	if (!power_domain_get(&priv->otgdev, &priv->otg_pd)) {
+		if (power_domain_on(&priv->otg_pd))
+			return -EINVAL;
+	}
+#endif
+
+	ret = ci_udc_phy_setup(&priv->otgdev, priv);
+	if (ret)
+		return ret;
+
+	ret = ci_udc_otg_clk_init(&priv->otgdev, &priv->clks);
+	if (ret)
+		return ret;
+
+	ehci_mx6_phy_init(ehci, &priv->phy_data, dev_seq(dev));
+
+	if (ci_udc_otg_phy_mode(dev) != USB_INIT_DEVICE)
+		return -ENODEV;
+
+	priv->ctrl.hccr = (struct ehci_hccr *)((ulong)&ehci->caplength);
+	priv->ctrl.hcor = (struct ehci_hcor *)((ulong)priv->ctrl.hccr +
+			HC_LENGTH(ehci_readl(&(priv->ctrl.hccr)->cr_capbase)));
+	controller.ctrl = &priv->ctrl;
+
+	ret = ci_udc_probe();
+	if (ret) {
+		DBG("udc probe failed, returned %d\n", ret);
+		return ret;
+	}
+
+	ret = usb_add_gadget_udc((struct device *)dev, &controller.gadget);
+
+	return ret;
+}
+
+static int ci_udc_otg_remove(struct udevice *dev)
+{
+	struct ci_udc_priv_data *priv = dev_get_priv(dev);
+
+	usb_del_gadget_udc(&controller.gadget);
+
+#if CONFIG_IS_ENABLED(CLK)
+	clk_release_bulk(&priv->clks);
+#endif
+	ci_udc_phy_shutdown(priv);
+#if CONFIG_IS_ENABLED(POWER_DOMAIN)
+	if (power_domain_off(&priv->otg_pd)) {
+		printf("Power down USB controller failed!\n");
+		return -EINVAL;
+	}
+#endif
+	board_usb_cleanup(dev_seq(dev), USB_INIT_DEVICE);
+
+	controller.ctrl = NULL;
+	return 0;
+}
+
+static const struct udevice_id ci_udc_otg_ids[] = {
+	{ .compatible = "fsl,imx27-usb-gadget" },
+	{ }
+};
+
+static const struct usb_gadget_generic_ops ci_udc_gadget_ops = {
+	.handle_interrupts	= ci_udc_gadget_handle_interrupts,
+};
+
+U_BOOT_DRIVER(ci_udc_otg) = {
+	.name	= "ci-udc-otg",
+	.id	= UCLASS_USB_GADGET_GENERIC,
+	.of_match = ci_udc_otg_ids,
+	.of_to_plat = ci_udc_otg_ofdata_to_platdata,
+	.probe = ci_udc_otg_probe,
+	.remove = ci_udc_otg_remove,
+	.ops	= &ci_udc_gadget_ops,
+	.priv_auto = sizeof(struct ci_udc_priv_data),
+};
+
+#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */

-- 
2.43.0



More information about the U-Boot mailing list