With CONFIG_DM_ETH_PHY, priv->bus may come from eth_phy_get_mdio_bus(),
in which case it belongs to another controller. zynq_gem_remove() and
the probe error path nevertheless unregister it, and remove() also
frees it, pulling the bus out from under its owner.

The same applies to priv->phydev: phy_connect() caches PHY devices in
the bus's phymap[] and hands out the cached pointer, so on a shared bus
the phy_device belongs to the bus owner (and may even be the owner's
own PHY). Freeing it in remove() leaves the owner with a dangling
pointer.

Track whether the bus was allocated by this instance and only release
the bus and phy_device in that case.

Signed-off-by: Graeme Smecher <[email protected]>
---
 drivers/net/zynq_gem.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/drivers/net/zynq_gem.c b/drivers/net/zynq_gem.c
index 
957d5251cb8af15899648e7e61d53205d35f3db6..d28b97d01d08da50049280fe2f75277506a4b740
 100644
--- a/drivers/net/zynq_gem.c
+++ b/drivers/net/zynq_gem.c
@@ -257,6 +257,7 @@ struct zynq_gem_priv {
        struct phy_device *phydev;
        ofnode phy_of_node;
        struct mii_dev *bus;
+       bool own_bus;
        struct clk rx_clk;
        struct clk tx_clk;
        struct clk pclk;
@@ -998,6 +999,7 @@ static int zynq_gem_probe(struct udevice *dev)
 
        if (!priv->bus) {
                priv->bus = mdio_alloc();
+               priv->own_bus = true;
                priv->bus->read = zynq_gem_miiphy_read;
                priv->bus->write = zynq_gem_miiphy_write;
                priv->bus->priv = priv;
@@ -1040,7 +1042,8 @@ static int zynq_gem_probe(struct udevice *dev)
        return ret;
 
 err3:
-       mdio_unregister(priv->bus);
+       if (priv->own_bus)
+               mdio_unregister(priv->bus);
 err2:
        free(priv->tx_bd);
 err1:
@@ -1052,9 +1055,11 @@ static int zynq_gem_remove(struct udevice *dev)
 {
        struct zynq_gem_priv *priv = dev_get_priv(dev);
 
-       free(priv->phydev);
-       mdio_unregister(priv->bus);
-       mdio_free(priv->bus);
+       if (priv->own_bus) {
+               free(priv->phydev);
+               mdio_unregister(priv->bus);
+               mdio_free(priv->bus);
+       }
 
        return 0;
 }

-- 
2.47.3

Reply via email to