In case a DT subnode describes the controller clocks and resets, make
sure that both the clocks and resets are bound to the parent device.

This fixes an object lifetime issue, where both reset_get_bulk() and
clk_get_bulk() internally call devm_kcalloc(dev, ...). In case the
udevice *dev passed into either function is that of a subnode instead
of the parent node, then the managed memory is also bound to the
subnode device instead of the parent device.

This triggers a problem in case the controller operates in peripheral
mode, where the peripheral is bound and unbound to the controller. If
the peripheral is unbound, which happens when DFU or FSG exits, then
the device managed memory bound to the subnode is free()d, and that
also frees the memory that was obtained in reset_get_bulk() and
clk_get_bulk() calls in case they were invoked with subnode device.
If anything rewrites that free()d memory and the controller driver
is then removed, bad things happen.

Example, enable CONFIG_DM_DEVICE_REMOVE=y , start 'ums 0 mmc N' ,
exit UMS, boot Linux kernel. U-Boot will most likely crash in
dwc3_glue_remove() due to invocation of reset_release_bulk() or
clk_release_bulk() which will attempt to use already free()d memory.

Fix this by always binding both bulk clocks and resets to the parent
device.

Signed-off-by: Marek Vasut <[email protected]>
---
Cc: Casey Connolly <[email protected]>
Cc: Fabrice GASNIER <[email protected]>
Cc: Jonas Karlman <[email protected]>
Cc: Kaustabh Chakraborty <[email protected]>
Cc: Mattijs Korpershoek <[email protected]>
Cc: Stefan Eichenberger <[email protected]>
Cc: Tom Rini <[email protected]>
Cc: [email protected]
---
 drivers/usb/dwc3/dwc3-generic.c | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/drivers/usb/dwc3/dwc3-generic.c b/drivers/usb/dwc3/dwc3-generic.c
index 2406f844617..efa07e67f98 100644
--- a/drivers/usb/dwc3/dwc3-generic.c
+++ b/drivers/usb/dwc3/dwc3-generic.c
@@ -670,6 +670,7 @@ int dwc3_glue_probe(struct udevice *dev)
        struct dwc3_glue_ops *ops = (struct dwc3_glue_ops 
*)dev_get_driver_data(dev);
        struct dwc3_glue_data *glue = dev_get_plat(dev);
        struct udevice *child = NULL;
+       ofnode node = dev_ofnode(dev);
        int index = 0;
        int ret;
        struct phy phy;
@@ -705,13 +706,17 @@ int dwc3_glue_probe(struct udevice *dev)
                return 0;
 
        if (glue->clks.count == 0) {
-               ret = dwc3_glue_clk_init(child, glue);
+               dev_set_ofnode(dev, dev_ofnode(child));
+               ret = dwc3_glue_clk_init(dev, glue);
+               dev_set_ofnode(dev, node);
                if (ret)
                        return ret;
        }
 
        if (glue->resets.count == 0) {
-               ret = dwc3_glue_reset_init(child, glue);
+               dev_set_ofnode(dev, dev_ofnode(child));
+               ret = dwc3_glue_reset_init(dev, glue);
+               dev_set_ofnode(dev, node);
                if (ret)
                        return ret;
        }
-- 
2.53.0

Reply via email to