Merge remote-tracking branches 'asoc/fix/tlv320aic3x' and 'asoc/fix/wm8962' into...
[linux-drm-fsl-dcu.git] / drivers / net / phy / phy_device.c
index c0f21112727446a3879584116e2caa80661ace48..f761288abe660b7d49b0a9a7dbd92b11efdbbbba 100644 (file)
@@ -383,6 +383,24 @@ int phy_device_register(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_device_register);
 
+/**
+ * phy_device_remove - Remove a previously registered phy device from the MDIO bus
+ * @phydev: phy_device structure to remove
+ *
+ * This doesn't free the phy_device itself, it merely reverses the effects
+ * of phy_device_register(). Use phy_device_free() to free the device
+ * after calling this function.
+ */
+void phy_device_remove(struct phy_device *phydev)
+{
+       struct mii_bus *bus = phydev->bus;
+       int addr = phydev->addr;
+
+       device_del(&phydev->dev);
+       bus->phy_map[addr] = NULL;
+}
+EXPORT_SYMBOL(phy_device_remove);
+
 /**
  * phy_find_first - finds the first PHY device on the bus
  * @bus: the target MII bus
@@ -578,14 +596,22 @@ EXPORT_SYMBOL(phy_init_hw);
  *     generic driver is used.  The phy_device is given a ptr to
  *     the attaching device, and given a callback for link status
  *     change.  The phy_device is returned to the attaching driver.
+ *     This function takes a reference on the phy device.
  */
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                      u32 flags, phy_interface_t interface)
 {
+       struct mii_bus *bus = phydev->bus;
        struct device *d = &phydev->dev;
-       struct module *bus_module;
        int err;
 
+       if (!try_module_get(bus->owner)) {
+               dev_err(&dev->dev, "failed to get the bus module\n");
+               return -EIO;
+       }
+
+       get_device(d);
+
        /* Assume that if there is no driver, that it doesn't
         * exist, and we should use the genphy driver.
         */
@@ -600,20 +626,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                        err = device_bind_driver(d);
 
                if (err)
-                       return err;
+                       goto error;
        }
 
        if (phydev->attached_dev) {
                dev_err(&dev->dev, "PHY already attached\n");
-               return -EBUSY;
-       }
-
-       /* Increment the bus module reference count */
-       bus_module = phydev->bus->dev.driver ?
-                    phydev->bus->dev.driver->owner : NULL;
-       if (!try_module_get(bus_module)) {
-               dev_err(&dev->dev, "failed to get the bus module\n");
-               return -EIO;
+               err = -EBUSY;
+               goto error;
        }
 
        phydev->attached_dev = dev;
@@ -636,6 +655,11 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                phy_resume(phydev);
 
        return err;
+
+error:
+       put_device(d);
+       module_put(bus->owner);
+       return err;
 }
 EXPORT_SYMBOL(phy_attach_direct);
 
@@ -677,14 +701,15 @@ EXPORT_SYMBOL(phy_attach);
 /**
  * phy_detach - detach a PHY device from its network device
  * @phydev: target phy_device struct
+ *
+ * This detaches the phy device from its network device and the phy
+ * driver, and drops the reference count taken in phy_attach_direct().
  */
 void phy_detach(struct phy_device *phydev)
 {
+       struct mii_bus *bus;
        int i;
 
-       if (phydev->bus->dev.driver)
-               module_put(phydev->bus->dev.driver->owner);
-
        phydev->attached_dev->phydev = NULL;
        phydev->attached_dev = NULL;
        phy_suspend(phydev);
@@ -700,6 +725,15 @@ void phy_detach(struct phy_device *phydev)
                        break;
                }
        }
+
+       /*
+        * The phydev might go away on the put_device() below, so avoid
+        * a use-after-free bug by reading the underlying bus first.
+        */
+       bus = phydev->bus;
+
+       put_device(&phydev->dev);
+       module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);