From 6602a93d39294cfc3c73073658da7ee26f167be5 Mon Sep 17 00:00:00 2001 From: Michele Bisogno Date: Sun, 26 Apr 2026 09:43:17 +0200 Subject: usb: gadget: rcar: Fix gadget registration lifecycle in remove The driver currently fails to unregister the USB gadget when the device is removed or the driver is unbound. This leads to dangling pointers in the UDC core and potential memory corruption. Add a call to usb_del_gadget_udc() in the remove path to ensure a clean teardown of the gadget interface. Reviewed-by: Marek Vasut Signed-off-by: Michele Bisogno --- drivers/usb/gadget/rcar/common.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/gadget/rcar/common.c b/drivers/usb/gadget/rcar/common.c index 2ba022a3f2c..d40d6736a54 100644 --- a/drivers/usb/gadget/rcar/common.c +++ b/drivers/usb/gadget/rcar/common.c @@ -447,8 +447,11 @@ err_clk: static int usbhs_udc_otg_remove(struct udevice *dev) { struct usbhs_priv_otg_data *priv = dev_get_priv(dev); + struct usb_gadget *gadget; usbhs_rcar3_power_ctrl(&priv->usbhs_priv, false); + gadget = usbhsg_get_gadget(&priv->usbhs_priv); + usb_del_gadget_udc(gadget); usbhs_mod_remove(&priv->usbhs_priv); usbhs_fifo_remove(&priv->usbhs_priv); usbhs_pipe_remove(&priv->usbhs_priv); -- cgit v1.2.3 From dc9f0d7177802191f67a42849f7e17f01c4b47dc Mon Sep 17 00:00:00 2001 From: Michele Bisogno Date: Sun, 26 Apr 2026 09:43:18 +0200 Subject: usb: gadget: rcar: Add support for reset controller Some Renesas SoCs, such as the RZ/G2L, require the USBHS core to be explicitly deasserted from reset before register access is possible. Update the OTG probe to handle a bulk reset controller. To maintain hardware stability, the reset is deasserted after clocks are enabled in probe(), and asserted before clocks are disabled in remove(). Update the error paths in probe to ensures clocks are disabled if the reset initialization fails. Reviewed-by: Marek Vasut Signed-off-by: Michele Bisogno --- drivers/usb/gadget/rcar/common.c | 42 ++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/rcar/common.c b/drivers/usb/gadget/rcar/common.c index d40d6736a54..2bf0dcff393 100644 --- a/drivers/usb/gadget/rcar/common.c +++ b/drivers/usb/gadget/rcar/common.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "common.h" @@ -290,6 +291,9 @@ struct usbhs_priv_otg_data { void __iomem *base; void __iomem *phybase; + struct clk_bulk clk_bulk; + struct reset_ctl_bulk reset_bulk; + struct platform_device usbhs_dev; struct usbhs_priv usbhs_priv; @@ -355,8 +359,10 @@ static int usbhs_udc_otg_gadget_handle_interrupts(struct udevice *dev) return 0; } -static int usbhs_probe(struct usbhs_priv *priv) +static int usbhs_probe(struct udevice *dev) { + struct usbhs_priv_otg_data *otg_priv = dev_get_priv(dev); + struct usbhs_priv *priv = &otg_priv->usbhs_priv; int ret; priv->dparam.type = USBHS_TYPE_RCAR_GEN3; @@ -396,34 +402,41 @@ static int usbhs_udc_otg_probe(struct udevice *dev) { struct usbhs_priv_otg_data *priv = dev_get_priv(dev); struct usb_gadget *gadget; - struct clk_bulk clk_bulk; int ret = -EINVAL; priv->base = dev_read_addr_ptr(dev); if (!priv->base) return -EINVAL; - ret = clk_get_bulk(dev, &clk_bulk); + ret = clk_get_bulk(dev, &priv->clk_bulk); if (ret) return ret; - ret = clk_enable_bulk(&clk_bulk); + ret = clk_enable_bulk(&priv->clk_bulk); if (ret) - return ret; + goto err_clk_enable; + + ret = reset_get_bulk(dev, &priv->reset_bulk); + if (ret) + goto err_clk; + + ret = reset_deassert_bulk(&priv->reset_bulk); + if (ret) + goto err_reset_deassert; clrsetbits_le32(priv->base + UGCTRL2, UGCTRL2_USB0SEL_MASK, UGCTRL2_USB0SEL_EHCI); clrsetbits_le16(priv->base + LPSTS, LPSTS_SUSPM, LPSTS_SUSPM); ret = generic_setup_phy(dev, &priv->phy, 0, PHY_MODE_USB_OTG, 1); if (ret) - goto err_clk; + goto err_reset; priv->phybase = dev_read_addr_ptr(priv->phy.dev); priv->usbhs_priv.pdev = &priv->usbhs_dev; priv->usbhs_priv.base = priv->base; priv->usbhs_dev.dev.driver_data = &priv->usbhs_priv; - ret = usbhs_probe(&priv->usbhs_priv); + ret = usbhs_probe(dev); if (ret < 0) goto err_phy; @@ -439,8 +452,15 @@ static int usbhs_udc_otg_probe(struct udevice *dev) err_phy: generic_shutdown_phy(&priv->phy); +err_reset: + reset_assert_bulk(&priv->reset_bulk); +err_reset_deassert: + reset_release_bulk(&priv->reset_bulk); err_clk: - clk_disable_bulk(&clk_bulk); + clk_disable_bulk(&priv->clk_bulk); +err_clk_enable: + clk_release_bulk(&priv->clk_bulk); + return ret; } @@ -458,6 +478,12 @@ static int usbhs_udc_otg_remove(struct udevice *dev) generic_shutdown_phy(&priv->phy); + reset_assert_bulk(&priv->reset_bulk); + reset_release_bulk(&priv->reset_bulk); + + clk_disable_bulk(&priv->clk_bulk); + clk_release_bulk(&priv->clk_bulk); + return dm_scan_fdt_dev(dev); } -- cgit v1.2.3 From d0d1d11119282b76551d45e531235238da51d7bc Mon Sep 17 00:00:00 2001 From: Michele Bisogno Date: Sun, 26 Apr 2026 09:43:19 +0200 Subject: usb: gadget: rcar: Add support for RZ/G2L (R9A07G044) The Renesas RZ/G2L (and RZ/G2LC) USBHS controller requires the CNEN bit in the SYSCFG register to be set for function operation. Additionally, its CFIFO is byte-addressable. Introduce a new renesas_usbhs_driver_param structure for the RZ/G2L SoC and link it via the udevice_id data pointer. Update usbhs_probe() to accept the udevice pointer to retrieve these parameters during initialization. This alignment follows the logic used in the Linux kernel renesas_usbhs driver. Reviewed-by: Marek Vasut Signed-off-by: Michele Bisogno --- drivers/usb/gadget/rcar/common.c | 20 ++++++++++++++++++++ drivers/usb/gadget/rcar/renesas_usb.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/usb/gadget/rcar/common.c b/drivers/usb/gadget/rcar/common.c index 2bf0dcff393..10548f7a20c 100644 --- a/drivers/usb/gadget/rcar/common.c +++ b/drivers/usb/gadget/rcar/common.c @@ -92,6 +92,12 @@ void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; u16 val = HSE | USBE; + /* CNEN bit is required for function operation */ + if (usbhs_get_dparam(priv, has_cnen)) { + mask |= CNEN; + val |= CNEN; + } + /* * if enable * @@ -363,13 +369,21 @@ static int usbhs_probe(struct udevice *dev) { struct usbhs_priv_otg_data *otg_priv = dev_get_priv(dev); struct usbhs_priv *priv = &otg_priv->usbhs_priv; + struct renesas_usbhs_driver_param *plat_param; int ret; + plat_param = (struct renesas_usbhs_driver_param *)dev_get_driver_data(dev); + priv->dparam.type = USBHS_TYPE_RCAR_GEN3; priv->dparam.pio_dma_border = 64; priv->dparam.pipe_configs = usbhsc_new_pipe; priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + if (plat_param) { + priv->dparam.has_cnen = plat_param->has_cnen; + priv->dparam.cfifo_byte_addr = plat_param->cfifo_byte_addr; + } + /* call pipe and module init */ ret = usbhs_pipe_probe(priv); if (ret < 0) @@ -487,8 +501,14 @@ static int usbhs_udc_otg_remove(struct udevice *dev) return dm_scan_fdt_dev(dev); } +static struct renesas_usbhs_driver_param rzg2l_param = { + .has_cnen = 1, + .cfifo_byte_addr = 1, +}; + static const struct udevice_id usbhs_udc_otg_ids[] = { { .compatible = "renesas,rcar-gen3-usbhs" }, + { .compatible = "renesas,rzg2l-usbhs", .data = (unsigned long)&rzg2l_param }, {}, }; diff --git a/drivers/usb/gadget/rcar/renesas_usb.h b/drivers/usb/gadget/rcar/renesas_usb.h index 8155e3dcaf6..140a70251cf 100644 --- a/drivers/usb/gadget/rcar/renesas_usb.h +++ b/drivers/usb/gadget/rcar/renesas_usb.h @@ -111,6 +111,7 @@ struct renesas_usbhs_driver_param { u32 has_otg:1; /* for controlling PWEN/EXTLP */ u32 has_sudmac:1; /* for SUDMAC */ u32 has_usb_dmac:1; /* for USB-DMAC */ + u32 has_cnen:1; u32 cfifo_byte_addr:1; /* CFIFO is byte addressable */ #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ u32 multi_clks:1; -- cgit v1.2.3