[U-Boot] [PATCH 07/16] usb: dwc2: convert driver to DM_USB_GADGET

Patrick Delaunay patrick.delaunay at st.com
Fri Mar 29 14:42:15 UTC 2019


Minimal conversion to driver model by using the uclass
UCLASS_USB_GADGET_GENERIC based on:
- reset uclass
- clock uclass
- generic uclass.

Signed-off-by: Patrick Delaunay <patrick.delaunay at st.com>
---

 doc/device-tree-bindings/usb/dwc2.txt |  54 +++++++
 drivers/usb/gadget/dwc2_udc_otg.c     | 292 +++++++++++++++++++++++++++++++++-
 2 files changed, 344 insertions(+), 2 deletions(-)
 create mode 100644 doc/device-tree-bindings/usb/dwc2.txt

diff --git a/doc/device-tree-bindings/usb/dwc2.txt b/doc/device-tree-bindings/usb/dwc2.txt
new file mode 100644
index 0000000..6dc3c4a
--- /dev/null
+++ b/doc/device-tree-bindings/usb/dwc2.txt
@@ -0,0 +1,54 @@
+Platform DesignWare HS OTG USB 2.0 controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : One of:
+  - brcm,bcm2835-usb: The DWC2 USB controller instance in the BCM2835 SoC.
+  - hisilicon,hi6220-usb: The DWC2 USB controller instance in the hi6220 SoC.
+  - rockchip,rk3066-usb: The DWC2 USB controller instance in the rk3066 Soc;
+  - "rockchip,px30-usb", "rockchip,rk3066-usb", "snps,dwc2": for px30 Soc;
+  - "rockchip,rk3188-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3188 Soc;
+  - "rockchip,rk3288-usb", "rockchip,rk3066-usb", "snps,dwc2": for rk3288 Soc;
+  - "lantiq,arx100-usb": The DWC2 USB controller instance in Lantiq ARX SoCs;
+  - "lantiq,xrx200-usb": The DWC2 USB controller instance in Lantiq XRX SoCs;
+  - "amlogic,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs;
+  - "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b SoCs;
+  - "amlogic,meson-gxbb-usb": The DWC2 USB controller instance in Amlogic S905 SoCs;
+  - "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs;
+  - snps,dwc2: A generic DWC2 USB controller with default parameters.
+  - "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs
+  configured in FS mode;
+  - "st,stm32f4x9-hsotg": The DWC2 USB HS controller instance in STM32F4x9 SoCs
+  configured in HS mode;
+  - "st,stm32f7-hsotg": The DWC2 USB HS controller instance in STM32F7 SoCs
+    configured in HS mode;
+- reg : Should contain 1 register range (address and length)
+- interrupts : Should contain 1 interrupt
+- clocks: clock provider specifier
+- clock-names: shall be "otg"
+Refer to clk/clock-bindings.txt for generic clock consumer properties
+
+Optional properties:
+- phys: phy provider specifier
+- phy-names: shall be "usb2-phy"
+Refer to phy/phy-bindings.txt for generic phy consumer properties
+- dr_mode: shall be one of "host", "peripheral" and "otg"
+  Refer to usb/generic.txt
+- g-rx-fifo-size: size of rx fifo size in gadget mode.
+- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
+- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
+
+Deprecated properties:
+- g-use-dma: gadget DMA mode is automatically detected
+
+Example:
+
+        usb at 101c0000 {
+                compatible = "ralink,rt3050-usb, snps,dwc2";
+                reg = <0x101c0000 40000>;
+                interrupts = <18>;
+		clocks = <&usb_otg_ahb_clk>;
+		clock-names = "otg";
+		phys = <&usbphy>;
+		phy-names = "usb2-phy";
+        };
diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c
index edca05d..af16fc1 100644
--- a/drivers/usb/gadget/dwc2_udc_otg.c
+++ b/drivers/usb/gadget/dwc2_udc_otg.c
@@ -18,11 +18,17 @@
  */
 #undef DEBUG
 #include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <generic-phy.h>
+#include <malloc.h>
+#include <reset.h>
+
 #include <linux/errno.h>
 #include <linux/list.h>
-#include <malloc.h>
 
 #include <linux/usb/ch9.h>
+#include <linux/usb/otg.h>
 #include <linux/usb/gadget.h>
 
 #include <asm/byteorder.h>
@@ -31,6 +37,8 @@
 
 #include <asm/mach-types.h>
 
+#include <power/regulator.h>
+
 #include "dwc2_udc_otg_regs.h"
 #include "dwc2_udc_otg_priv.h"
 
@@ -222,6 +230,7 @@ static int udc_enable(struct dwc2_udc *dev)
 	return 0;
 }
 
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
 /*
   Register entry point for the peripheral controller driver.
 */
@@ -296,6 +305,54 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 	udc_disable(dev);
 	return 0;
 }
+#else /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
+static int dwc2_gadget_start(struct usb_gadget *g,
+			     struct usb_gadget_driver *driver)
+{
+	struct dwc2_udc *dev = the_controller;
+
+	debug_cond(DEBUG_SETUP != 0, "%s: %s\n", __func__, "no name");
+
+	if (!driver ||
+	    (driver->speed != USB_SPEED_FULL &&
+	     driver->speed != USB_SPEED_HIGH) ||
+	    !driver->bind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (dev->driver)
+		return -EBUSY;
+
+	/* first hook up the driver ... */
+	dev->driver = driver;
+
+	debug_cond(DEBUG_SETUP != 0,
+		   "Registered gadget driver %s\n", dev->gadget.name);
+	return udc_enable(dev);
+}
+
+static int dwc2_gadget_stop(struct usb_gadget *g)
+{
+	struct dwc2_udc *dev = the_controller;
+
+	if (!dev)
+		return -ENODEV;
+
+	if (!dev->driver)
+		return -EINVAL;
+
+	dev->driver = 0;
+	stop_activity(dev, dev->driver);
+
+	udc_disable(dev);
+
+	return 0;
+}
+
+#endif /* !CONFIG_IS_ENABLED(DM_USB_GADGET) */
 
 /*
  *	done - retire a request; caller blocked irqs
@@ -730,6 +787,10 @@ static void dwc2_fifo_flush(struct usb_ep *_ep)
 
 static const struct usb_gadget_ops dwc2_udc_ops = {
 	/* current versions must always be self-powered */
+#if CONFIG_IS_ENABLED(DM_USB_GADGET)
+	.udc_start		= dwc2_gadget_start,
+	.udc_stop		= dwc2_gadget_stop,
+#endif
 };
 
 static struct dwc2_udc memory = {
@@ -841,12 +902,239 @@ int dwc2_udc_probe(struct dwc2_plat_otg_data *pdata)
 	return retval;
 }
 
-int usb_gadget_handle_interrupts(int index)
+int dwc2_udc_handle_interrupt(void)
 {
 	u32 intr_status = readl(&reg->gintsts);
 	u32 gintmsk = readl(&reg->gintmsk);
 
 	if (intr_status & gintmsk)
 		return dwc2_udc_irq(1, (void *)the_controller);
+
+	return 0;
+}
+
+#if !CONFIG_IS_ENABLED(DM_USB_GADGET)
+
+int usb_gadget_handle_interrupts(int index)
+{
+	return dwc2_udc_handle_interrupt();
+}
+
+#else /* CONFIG_IS_ENABLED(DM_USB_GADGET) */
+
+struct dwc2_priv_data {
+	struct clk_bulk		clks;
+	struct reset_ctl_bulk	resets;
+	struct phy *phys;
+	int num_phys;
+};
+
+int dm_usb_gadget_handle_interrupts(struct udevice *dev)
+{
+	return dwc2_udc_handle_interrupt();
+}
+
+int dwc2_phy_setup(struct udevice *dev, struct phy **array, int *num_phys)
+{
+	int i, ret, count;
+	struct phy *usb_phys;
+
+	/* Return if no phy declared */
+	if (!dev_read_prop(dev, "phys", NULL))
+		return 0;
+
+	count = dev_count_phandle_with_args(dev, "phys", "#phy-cells");
+	if (count <= 0)
+		return count;
+
+	usb_phys = devm_kcalloc(dev, count, sizeof(struct phy),
+				GFP_KERNEL);
+	if (!usb_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_get_by_index(dev, i, &usb_phys[i]);
+		if (ret && ret != -ENOENT) {
+			dev_err(dev, "Failed to get USB PHY%d for %s\n",
+				i, dev->name);
+			return ret;
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_init(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't init USB PHY%d for %s\n",
+				i, dev->name);
+			goto phys_init_err;
+		}
+	}
+
+	for (i = 0; i < count; i++) {
+		ret = generic_phy_power_on(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't power USB PHY%d for %s\n",
+				i, dev->name);
+			goto phys_poweron_err;
+		}
+	}
+
+	*array = usb_phys;
+	*num_phys =  count;
+
 	return 0;
+
+phys_poweron_err:
+	for (i = count - 1; i >= 0; i--)
+		generic_phy_power_off(&usb_phys[i]);
+
+	for (i = 0; i < count; i++)
+		generic_phy_exit(&usb_phys[i]);
+
+	return ret;
+
+phys_init_err:
+	for (; i >= 0; i--)
+		generic_phy_exit(&usb_phys[i]);
+
+	return ret;
 }
+
+void dwc2_phy_shutdown(struct udevice *dev, struct phy *usb_phys, int num_phys)
+{
+	int i, ret;
+
+	for (i = 0; i < num_phys; i++) {
+		if (!generic_phy_valid(&usb_phys[i]))
+			continue;
+
+		ret = generic_phy_power_off(&usb_phys[i]);
+		ret |= generic_phy_exit(&usb_phys[i]);
+		if (ret) {
+			dev_err(dev, "Can't shutdown USB PHY%d for %s\n",
+				i, dev->name);
+		}
+	}
+}
+
+static int dwc2_udc_otg_ofdata_to_platdata(struct udevice *dev)
+{
+	struct dwc2_plat_otg_data *platdata = dev_get_platdata(dev);
+	int node = dev_of_offset(dev);
+
+	if (usb_get_dr_mode(node) != USB_DR_MODE_PERIPHERAL) {
+		dev_dbg(dev, "Invalid mode\n");
+		return -ENODEV;
+	}
+
+	platdata->regs_otg = dev_read_addr(dev);
+
+	platdata->rx_fifo_sz = dev_read_u32_default(dev, "g-rx-fifo-size", 0);
+	platdata->np_tx_fifo_sz = dev_read_u32_default(dev,
+						       "g-np-tx-fifo-size", 0);
+	platdata->tx_fifo_sz = dev_read_u32_default(dev, "g-tx-fifo-size", 0);
+
+	return 0;
+}
+
+static int dwc2_udc_otg_reset_init(struct udevice *dev,
+				   struct reset_ctl_bulk *resets)
+{
+	int ret;
+
+	ret = reset_get_bulk(dev, resets);
+	if (ret == -ENOTSUPP)
+		return 0;
+
+	if (ret)
+		return ret;
+
+	ret = reset_deassert_bulk(resets);
+	if (ret) {
+		reset_release_bulk(resets);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dwc2_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;
+
+	ret = clk_enable_bulk(clks);
+	if (ret) {
+		clk_release_bulk(clks);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dwc2_udc_otg_probe(struct udevice *dev)
+{
+	struct dwc2_plat_otg_data *platdata = dev_get_platdata(dev);
+	struct dwc2_priv_data *priv = dev_get_priv(dev);
+	int ret;
+
+	ret = dwc2_udc_otg_clk_init(dev, &priv->clks);
+	if (ret)
+		return ret;
+
+	ret = dwc2_udc_otg_reset_init(dev, &priv->resets);
+	if (ret)
+		return ret;
+
+	ret = dwc2_phy_setup(dev, &priv->phys, &priv->num_phys);
+	if (ret)
+		return ret;
+
+	ret = dwc2_udc_probe(platdata);
+	if (ret)
+		return ret;
+
+	the_controller->driver = 0;
+
+	ret = usb_add_gadget_udc((struct device *)dev, &the_controller->gadget);
+
+	return ret;
+}
+
+static int dwc2_udc_otg_remove(struct udevice *dev)
+{
+	struct dwc2_priv_data *priv = dev_get_priv(dev);
+
+	usb_del_gadget_udc(&the_controller->gadget);
+
+	reset_release_bulk(&priv->resets);
+
+	clk_release_bulk(&priv->clks);
+
+	dwc2_phy_shutdown(dev, priv->phys, priv->num_phys);
+
+	return dm_scan_fdt_dev(dev);
+}
+
+static const struct udevice_id dwc2_udc_otg_ids[] = {
+	{ .compatible = "snps,dwc2" },
+};
+
+U_BOOT_DRIVER(dwc2_udc_otg) = {
+	.name	= "dwc2-udc-otg",
+	.id	= UCLASS_USB_GADGET_GENERIC,
+	.of_match = dwc2_udc_otg_ids,
+	.ofdata_to_platdata = dwc2_udc_otg_ofdata_to_platdata,
+	.probe = dwc2_udc_otg_probe,
+	.remove = dwc2_udc_otg_remove,
+	.platdata_auto_alloc_size = sizeof(struct dwc2_plat_otg_data),
+	.priv_auto_alloc_size = sizeof(struct dwc2_priv_data),
+};
+#endif /* CONFIG_IS_ENABLED(DM_USB_GADGET) */
-- 
2.7.4



More information about the U-Boot mailing list