OSDN Git Service

net: sfp: create/destroy I2C mdiobus before PHY probe/after PHY release
authorMarek Behún <kabel@kernel.org>
Fri, 30 Sep 2022 14:21:08 +0000 (16:21 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 3 Oct 2022 10:08:33 +0000 (11:08 +0100)
Instead of configuring the I2C mdiobus when SFP driver is probed,
create/destroy the mdiobus before the PHY is probed for/after it is
released.

This way we can tell the mdio-i2c code which protocol to use for each
SFP transceiver.

Move the code that determines MDIO I2C protocol from
sfp_sm_probe_for_phy() to sfp_sm_mod_probe(), where most of the SFP ID
parsing is done. Don't allocate I2C bus if no PHY is expected.

Signed-off-by: Marek Behún <kabel@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/phy/sfp.c
include/linux/mdio/mdio-i2c.h

index 3201e27..ccd7710 100644 (file)
@@ -218,6 +218,7 @@ struct sfp {
        struct i2c_adapter *i2c;
        struct mii_bus *i2c_mii;
        struct sfp_bus *sfp_bus;
+       enum mdio_i2c_proto mdio_protocol;
        struct phy_device *mod_phy;
        const struct sff_data *type;
        size_t i2c_block_size;
@@ -530,9 +531,6 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
 
 static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 {
-       struct mii_bus *i2c_mii;
-       int ret;
-
        if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
                return -EINVAL;
 
@@ -540,7 +538,15 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
        sfp->read = sfp_i2c_read;
        sfp->write = sfp_i2c_write;
 
-       i2c_mii = mdio_i2c_alloc(sfp->dev, i2c);
+       return 0;
+}
+
+static int sfp_i2c_mdiobus_create(struct sfp *sfp)
+{
+       struct mii_bus *i2c_mii;
+       int ret;
+
+       i2c_mii = mdio_i2c_alloc(sfp->dev, sfp->i2c);
        if (IS_ERR(i2c_mii))
                return PTR_ERR(i2c_mii);
 
@@ -558,6 +564,12 @@ static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
        return 0;
 }
 
+static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
+{
+       mdiobus_unregister(sfp->i2c_mii);
+       sfp->i2c_mii = NULL;
+}
+
 /* Interface */
 static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 {
@@ -1674,6 +1686,14 @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
        }
 }
 
+static int sfp_sm_add_mdio_bus(struct sfp *sfp)
+{
+       if (sfp->mdio_protocol != MDIO_I2C_NONE)
+               return sfp_i2c_mdiobus_create(sfp);
+
+       return 0;
+}
+
 /* Probe a SFP for a PHY device if the module supports copper - the PHY
  * normally sits at I2C bus address 0x56, and may either be a clause 22
  * or clause 45 PHY.
@@ -1689,19 +1709,19 @@ static int sfp_sm_probe_for_phy(struct sfp *sfp)
 {
        int err = 0;
 
-       switch (sfp->id.base.extended_cc) {
-       case SFF8024_ECC_10GBASE_T_SFI:
-       case SFF8024_ECC_10GBASE_T_SR:
-       case SFF8024_ECC_5GBASE_T:
-       case SFF8024_ECC_2_5GBASE_T:
-               err = sfp_sm_probe_phy(sfp, true);
+       switch (sfp->mdio_protocol) {
+       case MDIO_I2C_NONE:
                break;
 
-       default:
-               if (sfp->id.base.e1000_base_t)
-                       err = sfp_sm_probe_phy(sfp, false);
+       case MDIO_I2C_MARVELL_C22:
+               err = sfp_sm_probe_phy(sfp, false);
+               break;
+
+       case MDIO_I2C_C45:
+               err = sfp_sm_probe_phy(sfp, true);
                break;
        }
+
        return err;
 }
 
@@ -2028,6 +2048,16 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 
        sfp->tx_fault_ignore = false;
 
+       if (sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SFI ||
+           sfp->id.base.extended_cc == SFF8024_ECC_10GBASE_T_SR ||
+           sfp->id.base.extended_cc == SFF8024_ECC_5GBASE_T ||
+           sfp->id.base.extended_cc == SFF8024_ECC_2_5GBASE_T)
+               sfp->mdio_protocol = MDIO_I2C_C45;
+       else if (sfp->id.base.e1000_base_t)
+               sfp->mdio_protocol = MDIO_I2C_MARVELL_C22;
+       else
+               sfp->mdio_protocol = MDIO_I2C_NONE;
+
        sfp->quirk = sfp_lookup_quirk(&id);
        if (sfp->quirk && sfp->quirk->fixup)
                sfp->quirk->fixup(sfp);
@@ -2204,6 +2234,8 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
                        sfp_module_stop(sfp->sfp_bus);
                if (sfp->mod_phy)
                        sfp_sm_phy_detach(sfp);
+               if (sfp->i2c_mii)
+                       sfp_i2c_mdiobus_destroy(sfp);
                sfp_module_tx_disable(sfp);
                sfp_soft_stop_poll(sfp);
                sfp_sm_next(sfp, SFP_S_DOWN, 0);
@@ -2266,6 +2298,12 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
                                     sfp->sm_fault_retries == N_FAULT_INIT);
                } else if (event == SFP_E_TIMEOUT || event == SFP_E_TX_CLEAR) {
        init_done:
+                       /* Create mdiobus and start trying for PHY */
+                       ret = sfp_sm_add_mdio_bus(sfp);
+                       if (ret < 0) {
+                               sfp_sm_next(sfp, SFP_S_FAIL, 0);
+                               break;
+                       }
                        sfp->sm_phy_retries = R_PHY_RETRY;
                        goto phy_probe;
                }
index b1d27f7..3bde1a5 100644 (file)
@@ -11,6 +11,12 @@ struct device;
 struct i2c_adapter;
 struct mii_bus;
 
+enum mdio_i2c_proto {
+       MDIO_I2C_NONE,
+       MDIO_I2C_MARVELL_C22,
+       MDIO_I2C_C45,
+};
+
 struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c);
 
 #endif