Normally, all child devices are removed before their parent is removed.
However, mdio muxes are not direct children of their parents, so they
can stick around even when their parent has been removed. This can cause
crashes when the mux tries to access the parent bus.

Fix this by listening for device removals. When an MDIO bus is removed,
we remove any child that has that bus as a parent. It's possible we may
fail to remove a child if there are multiple children of a bus and the
first child fails to remove. However, there are no such configurations
in-tree so I went with a simpler approach.

Fixes: 8880edba06d5 ("net: add MDIO_MUX DM class")
Signed-off-by: Sean Anderson <[email protected]>
---

Changes in v2:
- Don't try to get the parent unless the mux is active

 drivers/net/Kconfig   |  1 +
 net/mdio-mux-uclass.c | 30 ++++++++++++++++++++++++++++++
 2 files changed, 31 insertions(+)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c057504cf66..5124c4a644e 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -30,6 +30,7 @@ config DM_MDIO
 
 config DM_MDIO_MUX
        bool "Enable Driver Model for MDIO MUX devices"
+       select DM_EVENT
        depends on DM_MDIO
        help
          Enable driver model for MDIO MUX devices
diff --git a/net/mdio-mux-uclass.c b/net/mdio-mux-uclass.c
index 14143ca0ab6..382f92d4fbe 100644
--- a/net/mdio-mux-uclass.c
+++ b/net/mdio-mux-uclass.c
@@ -7,6 +7,7 @@
 #include <dm.h>
 #include <log.h>
 #include <miiphy.h>
+#include <dm/device_compat.h>
 #include <dm/device-internal.h>
 #include <dm/uclass-internal.h>
 #include <dm/lists.h>
@@ -231,3 +232,32 @@ UCLASS_DRIVER(mdio_mux) = {
        .per_device_auto        = sizeof(struct mdio_mux_perdev_priv),
        .per_child_plat_auto    = sizeof(struct mdio_mux_ch_data),
 };
+
+static int dm_mdio_mux_parent_pre_remove(void *ctx, struct event *event)
+{
+       struct udevice *mdio_parent = event->data.dm.dev;
+       struct udevice *mux, *mux_next;
+       struct uclass *uc;
+       int ret;
+
+       if (device_get_uclass_id(mdio_parent) != UCLASS_MDIO)
+               return 0;
+
+       if (uclass_get(UCLASS_MDIO_MUX, &uc))
+               return 0;
+
+       uclass_foreach_dev_safe(mux, mux_next, uc) {
+               if (!device_active(mux) ||
+                   mmux_get_parent_mdio(mux) != mdio_parent)
+                       continue;
+
+               dev_dbg(mdio_parent, "removing %s %s\n", mux->driver->name,
+                       mux->name);
+               ret = device_remove(mux, DM_REMOVE_NORMAL);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EVENT_SPY_FULL(EVT_DM_PRE_REMOVE, dm_mdio_mux_parent_pre_remove);
-- 
2.53.0

Reply via email to