diff mbox series

[net-next,v5,4/5] net: sfp: create/destroy I2C mdiobus before PHY probe/after PHY release

Message ID 20210114044331.5073-5-kabel@kernel.org (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series Support for RollBall 10G copper SFP modules | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net-next
netdev/subject_prefix success Link
netdev/cc_maintainers warning 2 maintainers not CCed: linux@armlinux.org.uk hkallweit1@gmail.com
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 72 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link
netdev/stable success Stable not CCed

Commit Message

Marek Behún Jan. 14, 2021, 4:43 a.m. UTC
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.

Signed-off-by: Marek Behún <kabel@kernel.org>
Reviewed-by: Pali Rohár <pali@kernel.org>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/net/phy/sfp.c | 30 ++++++++++++++++++++++++++----
 1 file changed, 26 insertions(+), 4 deletions(-)

Comments

Russell King (Oracle) Jan. 14, 2021, 4:07 p.m. UTC | #1
On Thu, Jan 14, 2021 at 05:43:30AM +0100, Marek Behún wrote:
> 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.

I've been thinking a bit more about this. It looks like it will
allocate and free the MDIO bus each time any module is inserted or
removed, even a fiber module that wouldn't ever have a PHY. This adds
unnecessary noise to the kernel message log.

We only probe for a PHY if one of:

- id.base.extended_cc is SFF8024_ECC_10GBASE_T_SFI,
  SFF8024_ECC_10GBASE_T_SR, SFF8024_ECC_5GBASE_T, or
  SFF8024_ECC_2_5GBASE_T.
- id.base.e1000_base_t is set.

So, we only need the MDIO bus to be registered if one of those is true.

As you are introducing "enum mdio_i2c_proto", I'm wondering whether
that should include "MDIO_I2C_NONE", and we should only register the
bus and probe for a PHY if it is not MDIO_I2C_NONE.

Maybe we should have:

enum mdio_i2c_proto {
	MDIO_I2C_NONE,
	MDIO_I2C_MARVELL_C22,
	MDIO_I2C_C45,
	MDIO_I2C_ROLLBALL,
	...
};

with:

	sfp->mdio_protocol = MDIO_I2C_NONE;
	if (((!memcmp(id.base.vendor_name, "OEM             ", 16) ||
	      !memcmp(id.base.vendor_name, "Turris          ", 16)) &&
	     (!memcmp(id.base.vendor_pn, "SFP-10G-T       ", 16) ||
	      !memcmp(id.base.vendor_pn, "RTSFP-10", 8)))) {
		sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
		sfp->module_t_wait = T_WAIT_ROLLBALL;
	} else {
		switch (id.base.extended_cc) {
		...
		}
	}

static int sfp_sm_add_mdio_bus(struct sfp *sfp)
{
	int err = 0;

	if (sfp->mdio_protocol != MDIO_I2C_NONE)
		err = sfp_i2c_mdiobus_create(sfp);

	return err;
}

called from the place you call sfp_i2c_mdiobus_create(), and
sfp_sm_probe_for_phy() becomes:

static int sfp_sm_probe_for_phy(struct sfp *sfp)
{
	int err = 0;

	switch (sfp->mdio_protocol) {
	case MDIO_I2C_NONE:
		break;

	case MDIO_I2C_MARVELL_C22:
		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, false);
		break;

	case MDIO_I2C_C45:
		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, true);
		break;

	case MDIO_I2C_ROLLBALL:
		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true);
		break;
	}

	return err;
}

This avoids having to add the PHY address, as well as fudge around with
id.base.extended_cc to get the PHY probed.

Thoughts?
Pali Rohár Jan. 18, 2021, 9:38 a.m. UTC | #2
On Thursday 14 January 2021 16:07:19 Russell King - ARM Linux admin wrote:
> On Thu, Jan 14, 2021 at 05:43:30AM +0100, Marek Behún wrote:
> > 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.
> 
> I've been thinking a bit more about this. It looks like it will
> allocate and free the MDIO bus each time any module is inserted or
> removed, even a fiber module that wouldn't ever have a PHY. This adds
> unnecessary noise to the kernel message log.
> 
> We only probe for a PHY if one of:
> 
> - id.base.extended_cc is SFF8024_ECC_10GBASE_T_SFI,
>   SFF8024_ECC_10GBASE_T_SR, SFF8024_ECC_5GBASE_T, or
>   SFF8024_ECC_2_5GBASE_T.
> - id.base.e1000_base_t is set.
> 
> So, we only need the MDIO bus to be registered if one of those is true.
> 
> As you are introducing "enum mdio_i2c_proto", I'm wondering whether
> that should include "MDIO_I2C_NONE", and we should only register the
> bus and probe for a PHY if it is not MDIO_I2C_NONE.
> 
> Maybe we should have:
> 
> enum mdio_i2c_proto {
> 	MDIO_I2C_NONE,
> 	MDIO_I2C_MARVELL_C22,
> 	MDIO_I2C_C45,
> 	MDIO_I2C_ROLLBALL,
> 	...
> };
> 
> with:
> 
> 	sfp->mdio_protocol = MDIO_I2C_NONE;
> 	if (((!memcmp(id.base.vendor_name, "OEM             ", 16) ||
> 	      !memcmp(id.base.vendor_name, "Turris          ", 16)) &&
> 	     (!memcmp(id.base.vendor_pn, "SFP-10G-T       ", 16) ||
> 	      !memcmp(id.base.vendor_pn, "RTSFP-10", 8)))) {
> 		sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
> 		sfp->module_t_wait = T_WAIT_ROLLBALL;
> 	} else {
> 		switch (id.base.extended_cc) {
> 		...
> 		}
> 	}
> 
> static int sfp_sm_add_mdio_bus(struct sfp *sfp)
> {
> 	int err = 0;
> 
> 	if (sfp->mdio_protocol != MDIO_I2C_NONE)
> 		err = sfp_i2c_mdiobus_create(sfp);
> 
> 	return err;
> }
> 
> called from the place you call sfp_i2c_mdiobus_create(), and
> sfp_sm_probe_for_phy() becomes:
> 
> static int sfp_sm_probe_for_phy(struct sfp *sfp)
> {
> 	int err = 0;
> 
> 	switch (sfp->mdio_protocol) {
> 	case MDIO_I2C_NONE:
> 		break;
> 
> 	case MDIO_I2C_MARVELL_C22:
> 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, false);
> 		break;
> 
> 	case MDIO_I2C_C45:
> 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, true);
> 		break;
> 
> 	case MDIO_I2C_ROLLBALL:
> 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL, true);
> 		break;
> 	}
> 
> 	return err;
> }
> 
> This avoids having to add the PHY address, as well as fudge around with
> id.base.extended_cc to get the PHY probed.
> 
> Thoughts?

Hello Russell! For me this solution looks more cleaner. As all those
MDIO access protocols are vendor dependent, kernel code should not
detect them only from the standard (non-vendor) extended_cc property.
Marek Behún Jan. 18, 2021, 11:42 a.m. UTC | #3
On Mon, 18 Jan 2021 10:38:32 +0100
Pali Rohár <pali@kernel.org> wrote:

> On Thursday 14 January 2021 16:07:19 Russell King - ARM Linux admin
> wrote:
> > On Thu, Jan 14, 2021 at 05:43:30AM +0100, Marek Behún wrote:  
> > > 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.  
> > 
> > I've been thinking a bit more about this. It looks like it will
> > allocate and free the MDIO bus each time any module is inserted or
> > removed, even a fiber module that wouldn't ever have a PHY. This
> > adds unnecessary noise to the kernel message log.
> > 
> > We only probe for a PHY if one of:
> > 
> > - id.base.extended_cc is SFF8024_ECC_10GBASE_T_SFI,
> >   SFF8024_ECC_10GBASE_T_SR, SFF8024_ECC_5GBASE_T, or
> >   SFF8024_ECC_2_5GBASE_T.
> > - id.base.e1000_base_t is set.
> > 
> > So, we only need the MDIO bus to be registered if one of those is
> > true.
> > 
> > As you are introducing "enum mdio_i2c_proto", I'm wondering whether
> > that should include "MDIO_I2C_NONE", and we should only register the
> > bus and probe for a PHY if it is not MDIO_I2C_NONE.
> > 
> > Maybe we should have:
> > 
> > enum mdio_i2c_proto {
> > 	MDIO_I2C_NONE,
> > 	MDIO_I2C_MARVELL_C22,
> > 	MDIO_I2C_C45,
> > 	MDIO_I2C_ROLLBALL,
> > 	...
> > };
> > 
> > with:
> > 
> > 	sfp->mdio_protocol = MDIO_I2C_NONE;
> > 	if (((!memcmp(id.base.vendor_name, "OEM             ", 16)
> > || !memcmp(id.base.vendor_name, "Turris          ", 16)) &&
> > 	     (!memcmp(id.base.vendor_pn, "SFP-10G-T       ", 16) ||
> > 	      !memcmp(id.base.vendor_pn, "RTSFP-10", 8)))) {
> > 		sfp->mdio_protocol = MDIO_I2C_ROLLBALL;
> > 		sfp->module_t_wait = T_WAIT_ROLLBALL;
> > 	} else {
> > 		switch (id.base.extended_cc) {
> > 		...
> > 		}
> > 	}
> > 
> > static int sfp_sm_add_mdio_bus(struct sfp *sfp)
> > {
> > 	int err = 0;
> > 
> > 	if (sfp->mdio_protocol != MDIO_I2C_NONE)
> > 		err = sfp_i2c_mdiobus_create(sfp);
> > 
> > 	return err;
> > }
> > 
> > called from the place you call sfp_i2c_mdiobus_create(), and
> > sfp_sm_probe_for_phy() becomes:
> > 
> > static int sfp_sm_probe_for_phy(struct sfp *sfp)
> > {
> > 	int err = 0;
> > 
> > 	switch (sfp->mdio_protocol) {
> > 	case MDIO_I2C_NONE:
> > 		break;
> > 
> > 	case MDIO_I2C_MARVELL_C22:
> > 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, false);
> > 		break;
> > 
> > 	case MDIO_I2C_C45:
> > 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR, true);
> > 		break;
> > 
> > 	case MDIO_I2C_ROLLBALL:
> > 		err = sfp_sm_probe_phy(sfp, SFP_PHY_ADDR_ROLLBALL,
> > true); break;
> > 	}
> > 
> > 	return err;
> > }
> > 
> > This avoids having to add the PHY address, as well as fudge around
> > with id.base.extended_cc to get the PHY probed.
> > 
> > Thoughts?  
> 
> Hello Russell! For me this solution looks more cleaner. As all those
> MDIO access protocols are vendor dependent, kernel code should not
> detect them only from the standard (non-vendor) extended_cc property.

I shall respin this series with this modified, then.

Marek
diff mbox series

Patch

diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index fac5407c4b87..d1b655f805ab 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -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;
@@ -413,9 +414,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;
 
@@ -423,7 +421,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, MDIO_I2C_DEFAULT);
+	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, sfp->mdio_protocol);
 	if (IS_ERR(i2c_mii))
 		return PTR_ERR(i2c_mii);
 
@@ -441,6 +447,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)
 {
@@ -1881,6 +1893,8 @@  static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 	else
 		sfp->module_t_start_up = T_START_UP;
 
+	sfp->mdio_protocol = MDIO_I2C_DEFAULT;
+
 	return 0;
 }
 
@@ -2051,6 +2065,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);
@@ -2113,6 +2129,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_i2c_mdiobus_create(sfp);
+			if (ret < 0) {
+				sfp_sm_next(sfp, SFP_S_FAIL, 0);
+				break;
+			}
 			sfp->sm_phy_retries = R_PHY_RETRY;
 			goto phy_probe;
 		}