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

Peng Fan peng.fan at nxp.com
Mon Oct 12 08:23:53 CEST 2020


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>
Reviewed-by: Ye Li <ye.li at nxp.com>
[Peng: need to extract common code from host and gadget driver(TODO)]
Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
 drivers/usb/gadget/ci_udc.c | 305 +++++++++++++++++++++++++++++++++++-
 drivers/usb/host/ehci-mx6.c |  15 +-
 include/usb/ci_udc.h        |   3 +
 3 files changed, 305 insertions(+), 18 deletions(-)

diff --git a/drivers/usb/gadget/ci_udc.c b/drivers/usb/gadget/ci_udc.c
index 226a9e6d67..6f2e38ffd8 100644
--- a/drivers/usb/gadget/ci_udc.c
+++ b/drivers/usb/gadget/ci_udc.c
@@ -13,16 +13,25 @@
 #include <cpu_func.h>
 #include <net.h>
 #include <malloc.h>
+#include <dm.h>
+#include <clk.h>
+#include <power-domain.h>
 #include <asm/byteorder.h>
 #include <asm/cache.h>
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <asm/io.h>
 #include <asm/unaligned.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/clock.h>
+#include <asm/mach-imx/regs-usbphy.h>
 #include <linux/types.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <dm/pinctrl.h>
 #include <usb/ci_udc.h>
+#include <usb/ehci-ci.h>
 #include "../host/ehci.h"
 #include "ci_udc.h"
 
@@ -93,9 +102,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 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 struct usb_ep_ops ci_ep_ops = {
@@ -866,7 +884,7 @@ void udc_irq(void)
 	}
 }
 
-int usb_gadget_handle_interrupts(int index)
+int ci_udc_handle_interrupts(void)
 {
 	u32 value;
 	struct ci_udc *udc = (struct ci_udc *)controller.ctrl->hcor;
@@ -1010,6 +1028,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 usb_gadget_handle_interrupts(int index)
+{
+	return ci_udc_handle_interrupts();
+}
+
 int usb_gadget_register_driver(struct usb_gadget_driver *driver)
 {
 	int ret;
@@ -1063,10 +1094,276 @@ 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;
+};
+
+int dm_usb_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)
+{
+	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)
+		return -EINVAL;
+
+	phy_dev.node = 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;
+
+		ret = clk_free(&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)
+{
+	int ret;
+
+	ret = clk_get_bulk(dev, clks);
+	if (ret == -ENOSYS)
+		return 0;
+
+	if (ret)
+		return ret;
+
+#if CONFIG_IS_ENABLED(CLK)
+	ret = clk_enable_bulk(clks);
+	if (ret) {
+		clk_release_bulk(clks);
+		return ret;
+	}
+#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;
+	priv->otgdev.node = 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 (mx6_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, 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;
+
+	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);
+
+	clk_release_bulk(&priv->clks);
+	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, USB_INIT_DEVICE);
+
+	controller.ctrl = NULL;
+	return 0;
 }
+
+static const struct udevice_id ci_udc_otg_ids[] = {
+	{ .compatible = "fsl,imx27-usb-gadget" },
+	{ }
+};
+
+U_BOOT_DRIVER(ci_udc_otg) = {
+	.name	= "ci-udc-otg",
+	.id	= UCLASS_USB_GADGET_GENERIC,
+	.of_match = ci_udc_otg_ids,
+	.ofdata_to_platdata = ci_udc_otg_ofdata_to_platdata,
+	.probe = ci_udc_otg_probe,
+	.remove = ci_udc_otg_remove,
+	.priv_auto_alloc_size = sizeof(struct ci_udc_priv_data),
+};
+
+#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
diff --git a/drivers/usb/host/ehci-mx6.c b/drivers/usb/host/ehci-mx6.c
index 20617850f3..2bfae1f661 100644
--- a/drivers/usb/host/ehci-mx6.c
+++ b/drivers/usb/host/ehci-mx6.c
@@ -17,6 +17,7 @@
 #include <asm/arch/imx-regs.h>
 #include <asm/arch/clock.h>
 #include <asm/mach-imx/iomux-v3.h>
+#include <asm/mach-imx/regs-usbphy.h>
 #include <asm/mach-imx/sys_proto.h>
 #include <dm.h>
 #include <asm/mach-types.h>
@@ -37,18 +38,6 @@ DECLARE_GLOBAL_DATA_PTR;
 
 #define USB_H1_CTRL_OFFSET	0x04
 
-#define USBPHY_CTRL				0x00000030
-#define USBPHY_CTRL_SET				0x00000034
-#define USBPHY_CTRL_CLR				0x00000038
-#define USBPHY_CTRL_TOG				0x0000003c
-
-#define USBPHY_PWD				0x00000000
-#define USBPHY_CTRL_SFTRST			0x80000000
-#define USBPHY_CTRL_CLKGATE			0x40000000
-#define USBPHY_CTRL_ENUTMILEVEL3		0x00008000
-#define USBPHY_CTRL_ENUTMILEVEL2		0x00004000
-#define USBPHY_CTRL_OTG_ID			0x08000000
-
 #define ANADIG_USB2_CHRG_DETECT_EN_B		0x00100000
 #define ANADIG_USB2_CHRG_DETECT_CHK_CHRG_B	0x00080000
 
@@ -58,8 +47,6 @@ DECLARE_GLOBAL_DATA_PTR;
 #define ANADIG_USB2_PLL_480_CTRL_EN_USB_CLKS	0x00000040
 
 #define USBNC_OFFSET		0x200
-#define USBNC_PHY_STATUS_OFFSET	0x23C
-#define USBNC_PHYSTATUS_ID_DIG	(1 << 4) /* otg_id status */
 #define USBNC_PHYCFG2_ACAENB	(1 << 4) /* otg_id detection enable */
 #define UCTRL_PWR_POL		(1 << 9) /* OTG Polarity of Power Pin */
 #define UCTRL_OVER_CUR_POL	(1 << 8) /* OTG Polarity of Overcurrent */
diff --git a/include/usb/ci_udc.h b/include/usb/ci_udc.h
index 06adb2bb4d..ddae8e178b 100644
--- a/include/usb/ci_udc.h
+++ b/include/usb/ci_udc.h
@@ -7,7 +7,10 @@
 
 #ifndef __CI_UDC_H__
 #define __CI_UDC_H__
+#include <usb/ehci-ci.h>
 
 #define EP_MAX_PACKET_SIZE	0x200
 #define EP0_MAX_PACKET_SIZE	64
+
+int ehci_mx6_common_init(struct usb_ehci *ehci, int index);
 #endif /* __CI_UDC_H__ */
-- 
2.28.0



More information about the U-Boot mailing list