]> Git Repo - linux.git/blobdiff - drivers/net/phy/phy_device.c
arm64: Use a variable to store non-global mappings decision
[linux.git] / drivers / net / phy / phy_device.c
index 9d2bbb13293ee0d32a1ad510dcb49bde44f3abf2..b13c52873ef5d75cbf8ed473db077a4fceb8d71d 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/bitmap.h>
 #include <linux/phy.h>
 #include <linux/phy_led_triggers.h>
+#include <linux/sfp.h>
 #include <linux/mdio.h>
 #include <linux/io.h>
 #include <linux/uaccess.h>
@@ -488,7 +489,7 @@ static int phy_bus_match(struct device *dev, struct device_driver *drv)
 
        if (phydev->is_c45) {
                for (i = 1; i < num_ids; i++) {
-                       if (!(phydev->c45_ids.devices_in_package & (1 << i)))
+                       if (phydev->c45_ids.device_ids[i] == 0xffffffff)
                                continue;
 
                        if ((phydrv->phy_id & phydrv->phy_id_mask) ==
@@ -552,7 +553,7 @@ static const struct device_type mdio_bus_phy_type = {
        .pm = MDIO_BUS_PHY_PM_OPS,
 };
 
-static int phy_request_driver_module(struct phy_device *dev, int phy_id)
+static int phy_request_driver_module(struct phy_device *dev, u32 phy_id)
 {
        int ret;
 
@@ -564,15 +565,15 @@ static int phy_request_driver_module(struct phy_device *dev, int phy_id)
         * then modprobe isn't available.
         */
        if (IS_ENABLED(CONFIG_MODULES) && ret < 0 && ret != -ENOENT) {
-               phydev_err(dev, "error %d loading PHY driver module for ID 0x%08x\n",
-                          ret, phy_id);
+               phydev_err(dev, "error %d loading PHY driver module for ID 0x%08lx\n",
+                          ret, (unsigned long)phy_id);
                return ret;
        }
 
        return 0;
 }
 
-struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
+struct phy_device *phy_device_create(struct mii_bus *bus, int addr, u32 phy_id,
                                     bool is_c45,
                                     struct phy_c45_device_ids *c45_ids)
 {
@@ -596,8 +597,8 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
        mdiodev->device_free = phy_mdio_device_free;
        mdiodev->device_remove = phy_mdio_device_remove;
 
-       dev->speed = 0;
-       dev->duplex = -1;
+       dev->speed = SPEED_UNKNOWN;
+       dev->duplex = DUPLEX_UNKNOWN;
        dev->pause = 0;
        dev->asym_pause = 0;
        dev->link = 0;
@@ -632,7 +633,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id,
                int i;
 
                for (i = 1; i < num_ids; i++) {
-                       if (!(c45_ids->devices_in_package & (1 << i)))
+                       if (c45_ids->device_ids[i] == 0xffffffff)
                                continue;
 
                        ret = phy_request_driver_module(dev,
@@ -812,10 +813,13 @@ static int get_phy_id(struct mii_bus *bus, int addr, u32 *phy_id,
  */
 struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45)
 {
-       struct phy_c45_device_ids c45_ids = {0};
+       struct phy_c45_device_ids c45_ids;
        u32 phy_id = 0;
        int r;
 
+       c45_ids.devices_in_package = 0;
+       memset(c45_ids.device_ids, 0xff, sizeof(c45_ids.device_ids));
+
        r = get_phy_id(bus, addr, &phy_id, is_c45, &c45_ids);
        if (r)
                return ERR_PTR(r);
@@ -1174,6 +1178,65 @@ phy_standalone_show(struct device *dev, struct device_attribute *attr,
 }
 static DEVICE_ATTR_RO(phy_standalone);
 
+/**
+ * phy_sfp_attach - attach the SFP bus to the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .attach member.
+ */
+void phy_sfp_attach(void *upstream, struct sfp_bus *bus)
+{
+       struct phy_device *phydev = upstream;
+
+       if (phydev->attached_dev)
+               phydev->attached_dev->sfp_bus = bus;
+       phydev->sfp_bus_attached = true;
+}
+EXPORT_SYMBOL(phy_sfp_attach);
+
+/**
+ * phy_sfp_detach - detach the SFP bus from the PHY upstream network device
+ * @upstream: pointer to the phy device
+ * @bus: sfp bus representing cage being attached
+ *
+ * This is used to fill in the sfp_upstream_ops .detach member.
+ */
+void phy_sfp_detach(void *upstream, struct sfp_bus *bus)
+{
+       struct phy_device *phydev = upstream;
+
+       if (phydev->attached_dev)
+               phydev->attached_dev->sfp_bus = NULL;
+       phydev->sfp_bus_attached = false;
+}
+EXPORT_SYMBOL(phy_sfp_detach);
+
+/**
+ * phy_sfp_probe - probe for a SFP cage attached to this PHY device
+ * @phydev: Pointer to phy_device
+ * @ops: SFP's upstream operations
+ */
+int phy_sfp_probe(struct phy_device *phydev,
+                 const struct sfp_upstream_ops *ops)
+{
+       struct sfp_bus *bus;
+       int ret;
+
+       if (phydev->mdio.dev.fwnode) {
+               bus = sfp_bus_find_fwnode(phydev->mdio.dev.fwnode);
+               if (IS_ERR(bus))
+                       return PTR_ERR(bus);
+
+               phydev->sfp_bus = bus;
+
+               ret = sfp_bus_add_upstream(bus, phydev, ops);
+               sfp_bus_put(bus);
+       }
+       return 0;
+}
+EXPORT_SYMBOL(phy_sfp_probe);
+
 /**
  * phy_attach_direct - attach a network device to a given PHY device pointer
  * @dev: network device to attach
@@ -1249,6 +1312,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
        if (dev) {
                phydev->attached_dev = dev;
                dev->phydev = phydev;
+
+               if (phydev->sfp_bus_attached)
+                       dev->sfp_bus = phydev->sfp_bus;
        }
 
        /* Some Ethernet drivers try to connect to a PHY device before
@@ -1270,7 +1336,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                        phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
        }
 
-       phydev->dev_flags = flags;
+       phydev->dev_flags |= flags;
 
        phydev->interface = interface;
 
@@ -1607,6 +1673,40 @@ static int genphy_config_advert(struct phy_device *phydev)
        return changed;
 }
 
+/**
+ * genphy_c37_config_advert - sanitize and advertise auto-negotiation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ *   after sanitizing the values to make sure we only advertise
+ *   what is supported.  Returns < 0 on error, 0 if the PHY's advertisement
+ *   hasn't changed, and > 0 if it has changed. This function is intended
+ *   for Clause 37 1000Base-X mode.
+ */
+static int genphy_c37_config_advert(struct phy_device *phydev)
+{
+       u16 adv = 0;
+
+       /* Only allow advertising what this PHY supports */
+       linkmode_and(phydev->advertising, phydev->advertising,
+                    phydev->supported);
+
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+                             phydev->advertising))
+               adv |= ADVERTISE_1000XFULL;
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                             phydev->advertising))
+               adv |= ADVERTISE_1000XPAUSE;
+       if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                             phydev->advertising))
+               adv |= ADVERTISE_1000XPSE_ASYM;
+
+       return phy_modify_changed(phydev, MII_ADVERTISE,
+                                 ADVERTISE_1000XFULL | ADVERTISE_1000XPAUSE |
+                                 ADVERTISE_1000XHALF | ADVERTISE_1000XPSE_ASYM,
+                                 adv);
+}
+
 /**
  * genphy_config_eee_advert - disable unwanted eee mode advertisement
  * @phydev: target phy_device struct
@@ -1715,6 +1815,54 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
 }
 EXPORT_SYMBOL(__genphy_config_aneg);
 
+/**
+ * genphy_c37_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ *   advertising, and then restart auto-negotiation.  If it is not
+ *   enabled, then we write the BMCR. This function is intended
+ *   for use with Clause 37 1000Base-X mode.
+ */
+int genphy_c37_config_aneg(struct phy_device *phydev)
+{
+       int err, changed;
+
+       if (phydev->autoneg != AUTONEG_ENABLE)
+               return genphy_setup_forced(phydev);
+
+       err = phy_modify(phydev, MII_BMCR, BMCR_SPEED1000 | BMCR_SPEED100,
+                        BMCR_SPEED1000);
+       if (err)
+               return err;
+
+       changed = genphy_c37_config_advert(phydev);
+       if (changed < 0) /* error */
+               return changed;
+
+       if (!changed) {
+               /* Advertisement hasn't changed, but maybe aneg was never on to
+                * begin with?  Or maybe phy was isolated?
+                */
+               int ctl = phy_read(phydev, MII_BMCR);
+
+               if (ctl < 0)
+                       return ctl;
+
+               if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
+                       changed = 1; /* do restart aneg */
+       }
+
+       /* Only restart aneg if we are advertising something different
+        * than we were before.
+        */
+       if (changed > 0)
+               return genphy_restart_aneg(phydev);
+
+       return 0;
+}
+EXPORT_SYMBOL(genphy_c37_config_aneg);
+
 /**
  * genphy_aneg_done - return auto-negotiation status
  * @phydev: target phy_device struct
@@ -1787,7 +1935,14 @@ int genphy_read_lpa(struct phy_device *phydev)
 {
        int lpa, lpagb;
 
-       if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+       if (phydev->autoneg == AUTONEG_ENABLE) {
+               if (!phydev->autoneg_complete) {
+                       mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising,
+                                                       0);
+                       mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, 0);
+                       return 0;
+               }
+
                if (phydev->is_gigabit_capable) {
                        lpagb = phy_read(phydev, MII_STAT1000);
                        if (lpagb < 0)
@@ -1815,6 +1970,8 @@ int genphy_read_lpa(struct phy_device *phydev)
                        return lpa;
 
                mii_lpa_mod_linkmode_lpa_t(phydev->lp_advertising, lpa);
+       } else {
+               linkmode_zero(phydev->lp_advertising);
        }
 
        return 0;
@@ -1877,6 +2034,63 @@ int genphy_read_status(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(genphy_read_status);
 
+/**
+ * genphy_c37_read_status - check the link status and update current link state
+ * @phydev: target phy_device struct
+ *
+ * Description: Check the link, then figure out the current state
+ *   by comparing what we advertise with what the link partner
+ *   advertises. This function is for Clause 37 1000Base-X mode.
+ */
+int genphy_c37_read_status(struct phy_device *phydev)
+{
+       int lpa, err, old_link = phydev->link;
+
+       /* Update the link, but return if there was an error */
+       err = genphy_update_link(phydev);
+       if (err)
+               return err;
+
+       /* why bother the PHY if nothing can have changed */
+       if (phydev->autoneg == AUTONEG_ENABLE && old_link && phydev->link)
+               return 0;
+
+       phydev->duplex = DUPLEX_UNKNOWN;
+       phydev->pause = 0;
+       phydev->asym_pause = 0;
+
+       if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
+               lpa = phy_read(phydev, MII_LPA);
+               if (lpa < 0)
+                       return lpa;
+
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
+                                phydev->lp_advertising, lpa & LPA_LPACK);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+                                phydev->lp_advertising, lpa & LPA_1000XFULL);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_Pause_BIT,
+                                phydev->lp_advertising, lpa & LPA_1000XPAUSE);
+               linkmode_mod_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT,
+                                phydev->lp_advertising,
+                                lpa & LPA_1000XPAUSE_ASYM);
+
+               phy_resolve_aneg_linkmode(phydev);
+       } else if (phydev->autoneg == AUTONEG_DISABLE) {
+               int bmcr = phy_read(phydev, MII_BMCR);
+
+               if (bmcr < 0)
+                       return bmcr;
+
+               if (bmcr & BMCR_FULLDPLX)
+                       phydev->duplex = DUPLEX_FULL;
+               else
+                       phydev->duplex = DUPLEX_HALF;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(genphy_c37_read_status);
+
 /**
  * genphy_soft_reset - software reset the PHY via BMCR_RESET bit
  * @phydev: target phy_device struct
@@ -2270,6 +2484,9 @@ static int phy_remove(struct device *dev)
        phydev->state = PHY_DOWN;
        mutex_unlock(&phydev->lock);
 
+       sfp_bus_del_upstream(phydev->sfp_bus);
+       phydev->sfp_bus = NULL;
+
        if (phydev->drv && phydev->drv->remove) {
                phydev->drv->remove(phydev);
 
This page took 0.044743 seconds and 4 git commands to generate.