]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/net/phy/phy_device.c
net: phy: phy drivers should not set SUPPORTED_[Asym_]Pause
[linux.git] / drivers / net / phy / phy_device.c
index aeaf1bcb12d01f61b209188424a204e42ef738c9..9c06f8028f0c349629365cbdc82f63f63b55c098 100644 (file)
@@ -235,6 +235,53 @@ int phy_register_fixup_for_id(const char *bus_id,
 }
 EXPORT_SYMBOL(phy_register_fixup_for_id);
 
+/**
+ * phy_unregister_fixup - remove a phy_fixup from the list
+ * @bus_id: A string matches fixup->bus_id (or PHY_ANY_ID) in phy_fixup_list
+ * @phy_uid: A phy id matches fixup->phy_id (or PHY_ANY_UID) in phy_fixup_list
+ * @phy_uid_mask: Applied to phy_uid and fixup->phy_uid before comparison
+ */
+int phy_unregister_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask)
+{
+       struct list_head *pos, *n;
+       struct phy_fixup *fixup;
+       int ret;
+
+       ret = -ENODEV;
+
+       mutex_lock(&phy_fixup_lock);
+       list_for_each_safe(pos, n, &phy_fixup_list) {
+               fixup = list_entry(pos, struct phy_fixup, list);
+
+               if ((!strcmp(fixup->bus_id, bus_id)) &&
+                   ((fixup->phy_uid & phy_uid_mask) ==
+                    (phy_uid & phy_uid_mask))) {
+                       list_del(&fixup->list);
+                       kfree(fixup);
+                       ret = 0;
+                       break;
+               }
+       }
+       mutex_unlock(&phy_fixup_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(phy_unregister_fixup);
+
+/* Unregisters a fixup of any PHY with the UID in phy_uid */
+int phy_unregister_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask)
+{
+       return phy_unregister_fixup(PHY_ANY_ID, phy_uid, phy_uid_mask);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_uid);
+
+/* Unregisters a fixup of the PHY with id string bus_id */
+int phy_unregister_fixup_for_id(const char *bus_id)
+{
+       return phy_unregister_fixup(bus_id, PHY_ANY_UID, 0xffffffff);
+}
+EXPORT_SYMBOL(phy_unregister_fixup_for_id);
+
 /* Returns 1 if fixup matches phydev in bus_id and phy_uid.
  * Fixups can be set to match any in one or more fields.
  */
@@ -858,11 +905,17 @@ EXPORT_SYMBOL(phy_attached_print);
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                      u32 flags, phy_interface_t interface)
 {
+       struct module *ndev_owner = dev->dev.parent->driver->owner;
        struct mii_bus *bus = phydev->mdio.bus;
        struct device *d = &phydev->mdio.dev;
        int err;
 
-       if (!try_module_get(bus->owner)) {
+       /* For Ethernet device drivers that register their own MDIO bus, we
+        * will have bus->owner match ndev_mod, so we do not want to increment
+        * our own module->refcnt here, otherwise we would not be able to
+        * unload later on.
+        */
+       if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
                dev_err(&dev->dev, "failed to get the bus module\n");
                return -EIO;
        }
@@ -924,7 +977,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 error:
        phy_detach(phydev);
        put_device(d);
-       module_put(bus->owner);
+       if (ndev_owner != bus->owner)
+               module_put(bus->owner);
        return err;
 }
 EXPORT_SYMBOL(phy_attach_direct);
@@ -974,6 +1028,8 @@ EXPORT_SYMBOL(phy_attach);
  */
 void phy_detach(struct phy_device *phydev)
 {
+       struct net_device *dev = phydev->attached_dev;
+       struct module *ndev_owner = dev->dev.parent->driver->owner;
        struct mii_bus *bus;
        int i;
 
@@ -1003,7 +1059,8 @@ void phy_detach(struct phy_device *phydev)
        bus = phydev->mdio.bus;
 
        put_device(&phydev->mdio.dev);
-       module_put(bus->owner);
+       if (ndev_owner != bus->owner)
+               module_put(bus->owner);
 }
 EXPORT_SYMBOL(phy_detach);
 
@@ -1662,6 +1719,25 @@ static int phy_probe(struct device *dev)
         */
        of_set_phy_eee_broken(phydev);
 
+       /* The Pause Frame bits indicate that the PHY can support passing
+        * pause frames. During autonegotiation, the PHYs will determine if
+        * they should allow pause frames to pass.  The MAC driver should then
+        * use that result to determine whether to enable flow control via
+        * pause frames.
+        *
+        * Normally, PHY drivers should not set the Pause bits, and instead
+        * allow phylib to do that.  However, there may be some situations
+        * (e.g. hardware erratum) where the driver wants to set only one
+        * of these bits.
+        */
+       if (phydrv->features & (SUPPORTED_Pause | SUPPORTED_Asym_Pause)) {
+               phydev->supported &= ~(SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+               phydev->supported |= phydrv->features &
+                                    (SUPPORTED_Pause | SUPPORTED_Asym_Pause);
+       } else {
+               phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       }
+
        /* Set the state to READY by default */
        phydev->state = PHY_READY;