diff mbox series

[net-next,v2] net: sfp: soft status and control support

Message ID E1iXP7P-0006DS-47@rmk-PC.armlinux.org.uk
State Accepted
Delegated to: David Miller
Headers show
Series [net-next,v2] net: sfp: soft status and control support | expand

Commit Message

Russell King (Oracle) Nov. 20, 2019, 12:29 p.m. UTC
Add support for the soft status and control register, which allows
TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
make use of this when the board does not support GPIOs for these
signals.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 drivers/net/phy/sfp.c | 110 ++++++++++++++++++++++++++++++++++--------
 include/linux/sfp.h   |   4 ++
 2 files changed, 94 insertions(+), 20 deletions(-)

Comments

Andrew Lunn Nov. 21, 2019, 2:36 a.m. UTC | #1
On Wed, Nov 20, 2019 at 12:29:59PM +0000, Russell King wrote:
> Add support for the soft status and control register, which allows
> TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> make use of this when the board does not support GPIOs for these
> signals.

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew
Florian Fainelli Nov. 21, 2019, 4 a.m. UTC | #2
On 11/20/2019 4:29 AM, Russell King wrote:
> Add support for the soft status and control register, which allows
> TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> make use of this when the board does not support GPIOs for these
> signals.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
David Miller Nov. 21, 2019, 6:29 a.m. UTC | #3
From: Russell King <rmk+kernel@armlinux.org.uk>
Date: Wed, 20 Nov 2019 12:29:59 +0000

> Add support for the soft status and control register, which allows
> TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> make use of this when the board does not support GPIOs for these
> signals.
> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>

Applied.
Ioana Ciornei Nov. 21, 2019, 3:51 p.m. UTC | #4
> Subject: [PATCH net-next v2] net: sfp: soft status and control support
> 
> Add support for the soft status and control register, which allows TX_FAULT
> and RX_LOS to be monitored and TX_DISABLE to be set.  We make use of this
> when the board does not support GPIOs for these signals.

Hi Russell,

With this addition, shouldn't the following print be removed?

[    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be emitting.

Ioana


> 
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---
>  drivers/net/phy/sfp.c | 110 ++++++++++++++++++++++++++++++++++-----
> ---
>  include/linux/sfp.h   |   4 ++
>  2 files changed, 94 insertions(+), 20 deletions(-)
> 
> diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index
> 69bedef96ca7..da9e7dd50b95 100644
> --- a/drivers/net/phy/sfp.c
> +++ b/drivers/net/phy/sfp.c
> @@ -201,7 +201,10 @@ struct sfp {
>  	struct gpio_desc *gpio[GPIO_MAX];
>  	int gpio_irq[GPIO_MAX];
> 
> +	bool need_poll;
> +
>  	struct mutex st_mutex;			/* Protects state */
> +	unsigned int state_soft_mask;
>  	unsigned int state;
>  	struct delayed_work poll;
>  	struct delayed_work timeout;
> @@ -395,24 +398,90 @@ static int sfp_i2c_configure(struct sfp *sfp, struct
> i2c_adapter *i2c)  }
> 
>  /* Interface */
> -static unsigned int sfp_get_state(struct sfp *sfp)
> +static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf,
> +size_t len)
>  {
> -	return sfp->get_state(sfp);
> +	return sfp->read(sfp, a2, addr, buf, len);
>  }
> 
> -static void sfp_set_state(struct sfp *sfp, unsigned int state)
> +static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf,
> +size_t len)
>  {
> -	sfp->set_state(sfp, state);
> +	return sfp->write(sfp, a2, addr, buf, len);
>  }
> 
> -static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
> +static unsigned int sfp_soft_get_state(struct sfp *sfp)
>  {
> -	return sfp->read(sfp, a2, addr, buf, len);
> +	unsigned int state = 0;
> +	u8 status;
> +
> +	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
> +		     sizeof(status)) {
> +		if (status & SFP_STATUS_RX_LOS)
> +			state |= SFP_F_LOS;
> +		if (status & SFP_STATUS_TX_FAULT)
> +			state |= SFP_F_TX_FAULT;
> +	}
> +
> +	return state & sfp->state_soft_mask;
>  }
> 
> -static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
> +static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
>  {
> -	return sfp->write(sfp, a2, addr, buf, len);
> +	u8 status;
> +
> +	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
> +		     sizeof(status)) {
> +		if (state & SFP_F_TX_DISABLE)
> +			status |= SFP_STATUS_TX_DISABLE_FORCE;
> +		else
> +			status &= ~SFP_STATUS_TX_DISABLE_FORCE;
> +
> +		sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status));
> +	}
> +}
> +
> +static void sfp_soft_start_poll(struct sfp *sfp) {
> +	const struct sfp_eeprom_id *id = &sfp->id;
> +
> +	sfp->state_soft_mask = 0;
> +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
> +	    !sfp->gpio[GPIO_TX_DISABLE])
> +		sfp->state_soft_mask |= SFP_F_TX_DISABLE;
> +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
> +	    !sfp->gpio[GPIO_TX_FAULT])
> +		sfp->state_soft_mask |= SFP_F_TX_FAULT;
> +	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
> +	    !sfp->gpio[GPIO_LOS])
> +		sfp->state_soft_mask |= SFP_F_LOS;
> +
> +	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
> +	    !sfp->need_poll)
> +		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies); }
> +
> +static void sfp_soft_stop_poll(struct sfp *sfp) {
> +	sfp->state_soft_mask = 0;
> +}
> +
> +static unsigned int sfp_get_state(struct sfp *sfp) {
> +	unsigned int state = sfp->get_state(sfp);
> +
> +	if (state & SFP_F_PRESENT &&
> +	    sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
> +		state |= sfp_soft_get_state(sfp);
> +
> +	return state;
> +}
> +
> +static void sfp_set_state(struct sfp *sfp, unsigned int state) {
> +	sfp->set_state(sfp, state);
> +
> +	if (state & SFP_F_PRESENT &&
> +	    sfp->state_soft_mask & SFP_F_TX_DISABLE)
> +		sfp_soft_set_state(sfp, state);
>  }
> 
>  static unsigned int sfp_check(void *buf, size_t len) @@ -1344,11 +1413,6
> @@ static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool
> warn)
>  	}
>  }
> 
> -static void sfp_sm_mod_init(struct sfp *sfp) -{
> -	sfp_module_tx_enable(sfp);
> -}
> -
>  static void sfp_sm_probe_for_phy(struct sfp *sfp)  {
>  	/* Setting the serdes link mode is guesswork: there's no @@ -1511,7
> +1575,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
>  		 (int)sizeof(id.ext.datecode), id.ext.datecode);
> 
>  	/* Check whether we support this module */
> -	if (!sfp->type->module_supported(&sfp->id)) {
> +	if (!sfp->type->module_supported(&id)) {
>  		dev_err(sfp->dev,
>  			"module is not supported - phys id 0x%02x
> 0x%02x\n",
>  			sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
> @@ -1701,6 +1765,7 @@ static void sfp_sm_main(struct sfp *sfp, unsigned
> int event)
>  		if (sfp->mod_phy)
>  			sfp_sm_phy_detach(sfp);
>  		sfp_module_tx_disable(sfp);
> +		sfp_soft_stop_poll(sfp);
>  		sfp_sm_next(sfp, SFP_S_DOWN, 0);
>  		return;
>  	}
> @@ -1712,7 +1777,10 @@ static void sfp_sm_main(struct sfp *sfp, unsigned
> int event)
>  		    sfp->sm_dev_state != SFP_DEV_UP)
>  			break;
> 
> -		sfp_sm_mod_init(sfp);
> +		if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE))
> +			sfp_soft_start_poll(sfp);
> +
> +		sfp_module_tx_enable(sfp);
> 
>  		/* Initialise the fault clearance retries */
>  		sfp->sm_retries = 5;
> @@ -1968,7 +2036,10 @@ static void sfp_poll(struct work_struct *work)
>  	struct sfp *sfp = container_of(work, struct sfp, poll.work);
> 
>  	sfp_check_state(sfp);
> -	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
> +
> +	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
> +	    sfp->need_poll)
> +		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
>  }
> 
>  static struct sfp *sfp_alloc(struct device *dev) @@ -2013,7 +2084,6 @@
> static int sfp_probe(struct platform_device *pdev)
>  	const struct sff_data *sff;
>  	struct i2c_adapter *i2c;
>  	struct sfp *sfp;
> -	bool poll = false;
>  	int err, i;
> 
>  	sfp = sfp_alloc(&pdev->dev);
> @@ -2120,7 +2190,7 @@ static int sfp_probe(struct platform_device *pdev)
> 
>  		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
>  		if (!sfp->gpio_irq[i]) {
> -			poll = true;
> +			sfp->need_poll = true;
>  			continue;
>  		}
> 
> @@ -2132,11 +2202,11 @@ static int sfp_probe(struct platform_device
> *pdev)
>  						dev_name(sfp->dev), sfp);
>  		if (err) {
>  			sfp->gpio_irq[i] = 0;
> -			poll = true;
> +			sfp->need_poll = true;
>  		}
>  	}
> 
> -	if (poll)
> +	if (sfp->need_poll)
>  		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
> 
>  	/* We could have an issue in cases no Tx disable pin is available or diff
> --git a/include/linux/sfp.h b/include/linux/sfp.h index
> 3b35efd85bb1..487fd9412d10 100644
> --- a/include/linux/sfp.h
> +++ b/include/linux/sfp.h
> @@ -428,6 +428,10 @@ enum {
>  	SFP_TEC_CUR			= 0x6c,
> 
>  	SFP_STATUS			= 0x6e,
> +	SFP_STATUS_TX_DISABLE		= BIT(7),
> +	SFP_STATUS_TX_DISABLE_FORCE	= BIT(6),
> +	SFP_STATUS_TX_FAULT		= BIT(2),
> +	SFP_STATUS_RX_LOS		= BIT(1),
>  	SFP_ALARM0			= 0x70,
>  	SFP_ALARM0_TEMP_HIGH		= BIT(7),
>  	SFP_ALARM0_TEMP_LOW		= BIT(6),
> --
> 2.20.1
Andrew Lunn Nov. 21, 2019, 4:21 p.m. UTC | #5
On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > Subject: [PATCH net-next v2] net: sfp: soft status and control support
> > 
> > Add support for the soft status and control register, which allows TX_FAULT
> > and RX_LOS to be monitored and TX_DISABLE to be set.  We make use of this
> > when the board does not support GPIOs for these signals.
> 
> Hi Russell,
> 
> With this addition, shouldn't the following print be removed?
> 
> [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be emitting.

Hi Ioana

Does the SFP you are using actually support soft status?

     Andrew
Russell King (Oracle) Nov. 21, 2019, 4:23 p.m. UTC | #6
On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > Subject: [PATCH net-next v2] net: sfp: soft status and control support
> > 
> > Add support for the soft status and control register, which allows TX_FAULT
> > and RX_LOS to be monitored and TX_DISABLE to be set.  We make use of this
> > when the board does not support GPIOs for these signals.
> 
> Hi Russell,
> 
> With this addition, shouldn't the following print be removed?
> 
> [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be emitting.

No, because modules do not have to provide the soft controls.
Ioana Ciornei Nov. 21, 2019, 6:24 p.m. UTC | #7
> Subject: Re: [PATCH net-next v2] net: sfp: soft status and control support
> 
> On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > > Subject: [PATCH net-next v2] net: sfp: soft status and control
> > > support
> > >
> > > Add support for the soft status and control register, which allows
> > > TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> > > make use of this when the board does not support GPIOs for these
> signals.
> >
> > Hi Russell,
> >
> > With this addition, shouldn't the following print be removed?
> >
> > [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be
> emitting.
> 
> Hi Ioana
> 
> Does the SFP you are using actually support soft status?
> 
>      Andrew

Yes, it does. I am testing with a FINISAR FTLX8571D3BCL and checked its datasheet
besides verifying that the laser is actually disabled.

Ioana
Ioana Ciornei Nov. 21, 2019, 6:33 p.m. UTC | #8
> Subject: Re: [PATCH net-next v2] net: sfp: soft status and control support
> 
> On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > > Subject: [PATCH net-next v2] net: sfp: soft status and control
> > > support
> > >
> > > Add support for the soft status and control register, which allows
> > > TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> > > make use of this when the board does not support GPIOs for these
> signals.
> >
> > Hi Russell,
> >
> > With this addition, shouldn't the following print be removed?
> >
> > [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be
> emitting.
> 
> No, because modules do not have to provide the soft controls.
> 

I understand that the soft controls are optional but can't we read
byte 93 (Enhanced Options) and see if bit 6 (Optional soft TX_DISABLE control)
is set or not (ie the soft TX_DISABLE is implemented)?

Ioana
Russell King (Oracle) Nov. 21, 2019, 6:54 p.m. UTC | #9
On Thu, Nov 21, 2019 at 06:33:41PM +0000, Ioana Ciornei wrote:
> 
> > Subject: Re: [PATCH net-next v2] net: sfp: soft status and control support
> > 
> > On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > > > Subject: [PATCH net-next v2] net: sfp: soft status and control
> > > > support
> > > >
> > > > Add support for the soft status and control register, which allows
> > > > TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be set.  We
> > > > make use of this when the board does not support GPIOs for these
> > signals.
> > >
> > > Hi Russell,
> > >
> > > With this addition, shouldn't the following print be removed?
> > >
> > > [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be
> > emitting.
> > 
> > No, because modules do not have to provide the soft controls.
> > 
> 
> I understand that the soft controls are optional but can't we read
> byte 93 (Enhanced Options) and see if bit 6 (Optional soft TX_DISABLE control)
> is set or not (ie the soft TX_DISABLE is implemented)?

At cage initialisation time, when we don't know whether there's a
module present or not?
Ioana Ciornei Nov. 21, 2019, 7:14 p.m. UTC | #10
> Subject: Re: [PATCH net-next v2] net: sfp: soft status and control support
> 
> On Thu, Nov 21, 2019 at 06:33:41PM +0000, Ioana Ciornei wrote:
> >
> > > Subject: Re: [PATCH net-next v2] net: sfp: soft status and control
> > > support
> > >
> > > On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > > > > Subject: [PATCH net-next v2] net: sfp: soft status and control
> > > > > support
> > > > >
> > > > > Add support for the soft status and control register, which
> > > > > allows TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be
> > > > > set.  We make use of this when the board does not support GPIOs
> > > > > for these
> > > signals.
> > > >
> > > > Hi Russell,
> > > >
> > > > With this addition, shouldn't the following print be removed?
> > > >
> > > > [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be
> > > emitting.
> > >
> > > No, because modules do not have to provide the soft controls.
> > >
> >
> > I understand that the soft controls are optional but can't we read
> > byte 93 (Enhanced Options) and see if bit 6 (Optional soft TX_DISABLE
> > control) is set or not (ie the soft TX_DISABLE is implemented)?
> 
> At cage initialisation time, when we don't know whether there's a module
> present or not?
> 

I was not suggesting to keep the print exactly in place.
Anyway, it was merely a curiosity because it can be a misleading info in some situations.

Ioana
Russell King (Oracle) Nov. 21, 2019, 11:17 p.m. UTC | #11
On Thu, Nov 21, 2019 at 07:14:01PM +0000, Ioana Ciornei wrote:
> > Subject: Re: [PATCH net-next v2] net: sfp: soft status and control support
> > 
> > On Thu, Nov 21, 2019 at 06:33:41PM +0000, Ioana Ciornei wrote:
> > >
> > > > Subject: Re: [PATCH net-next v2] net: sfp: soft status and control
> > > > support
> > > >
> > > > On Thu, Nov 21, 2019 at 03:51:07PM +0000, Ioana Ciornei wrote:
> > > > > > Subject: [PATCH net-next v2] net: sfp: soft status and control
> > > > > > support
> > > > > >
> > > > > > Add support for the soft status and control register, which
> > > > > > allows TX_FAULT and RX_LOS to be monitored and TX_DISABLE to be
> > > > > > set.  We make use of this when the board does not support GPIOs
> > > > > > for these
> > > > signals.
> > > > >
> > > > > Hi Russell,
> > > > >
> > > > > With this addition, shouldn't the following print be removed?
> > > > >
> > > > > [    2.967583] sfp sfp-mac4: No tx_disable pin: SFP modules will always be
> > > > emitting.
> > > >
> > > > No, because modules do not have to provide the soft controls.
> > > >
> > >
> > > I understand that the soft controls are optional but can't we read
> > > byte 93 (Enhanced Options) and see if bit 6 (Optional soft TX_DISABLE
> > > control) is set or not (ie the soft TX_DISABLE is implemented)?
> > 
> > At cage initialisation time, when we don't know whether there's a module
> > present or not?
> > 
> 
> I was not suggesting to keep the print exactly in place.
> Anyway, it was merely a curiosity because it can be a misleading info in
> some situations.

However, it's the safe thing to do, to assume that the module soft
TX disable may not be implemented or working.
diff mbox series

Patch

diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 69bedef96ca7..da9e7dd50b95 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -201,7 +201,10 @@  struct sfp {
 	struct gpio_desc *gpio[GPIO_MAX];
 	int gpio_irq[GPIO_MAX];
 
+	bool need_poll;
+
 	struct mutex st_mutex;			/* Protects state */
+	unsigned int state_soft_mask;
 	unsigned int state;
 	struct delayed_work poll;
 	struct delayed_work timeout;
@@ -395,24 +398,90 @@  static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
 }
 
 /* Interface */
-static unsigned int sfp_get_state(struct sfp *sfp)
+static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 {
-	return sfp->get_state(sfp);
+	return sfp->read(sfp, a2, addr, buf, len);
 }
 
-static void sfp_set_state(struct sfp *sfp, unsigned int state)
+static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
 {
-	sfp->set_state(sfp, state);
+	return sfp->write(sfp, a2, addr, buf, len);
 }
 
-static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+static unsigned int sfp_soft_get_state(struct sfp *sfp)
 {
-	return sfp->read(sfp, a2, addr, buf, len);
+	unsigned int state = 0;
+	u8 status;
+
+	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
+		     sizeof(status)) {
+		if (status & SFP_STATUS_RX_LOS)
+			state |= SFP_F_LOS;
+		if (status & SFP_STATUS_TX_FAULT)
+			state |= SFP_F_TX_FAULT;
+	}
+
+	return state & sfp->state_soft_mask;
 }
 
-static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+static void sfp_soft_set_state(struct sfp *sfp, unsigned int state)
 {
-	return sfp->write(sfp, a2, addr, buf, len);
+	u8 status;
+
+	if (sfp_read(sfp, true, SFP_STATUS, &status, sizeof(status)) ==
+		     sizeof(status)) {
+		if (state & SFP_F_TX_DISABLE)
+			status |= SFP_STATUS_TX_DISABLE_FORCE;
+		else
+			status &= ~SFP_STATUS_TX_DISABLE_FORCE;
+
+		sfp_write(sfp, true, SFP_STATUS, &status, sizeof(status));
+	}
+}
+
+static void sfp_soft_start_poll(struct sfp *sfp)
+{
+	const struct sfp_eeprom_id *id = &sfp->id;
+
+	sfp->state_soft_mask = 0;
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_DISABLE &&
+	    !sfp->gpio[GPIO_TX_DISABLE])
+		sfp->state_soft_mask |= SFP_F_TX_DISABLE;
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_TX_FAULT &&
+	    !sfp->gpio[GPIO_TX_FAULT])
+		sfp->state_soft_mask |= SFP_F_TX_FAULT;
+	if (id->ext.enhopts & SFP_ENHOPTS_SOFT_RX_LOS &&
+	    !sfp->gpio[GPIO_LOS])
+		sfp->state_soft_mask |= SFP_F_LOS;
+
+	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) &&
+	    !sfp->need_poll)
+		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+}
+
+static void sfp_soft_stop_poll(struct sfp *sfp)
+{
+	sfp->state_soft_mask = 0;
+}
+
+static unsigned int sfp_get_state(struct sfp *sfp)
+{
+	unsigned int state = sfp->get_state(sfp);
+
+	if (state & SFP_F_PRESENT &&
+	    sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT))
+		state |= sfp_soft_get_state(sfp);
+
+	return state;
+}
+
+static void sfp_set_state(struct sfp *sfp, unsigned int state)
+{
+	sfp->set_state(sfp, state);
+
+	if (state & SFP_F_PRESENT &&
+	    sfp->state_soft_mask & SFP_F_TX_DISABLE)
+		sfp_soft_set_state(sfp, state);
 }
 
 static unsigned int sfp_check(void *buf, size_t len)
@@ -1344,11 +1413,6 @@  static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
 	}
 }
 
-static void sfp_sm_mod_init(struct sfp *sfp)
-{
-	sfp_module_tx_enable(sfp);
-}
-
 static void sfp_sm_probe_for_phy(struct sfp *sfp)
 {
 	/* Setting the serdes link mode is guesswork: there's no
@@ -1511,7 +1575,7 @@  static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
 		 (int)sizeof(id.ext.datecode), id.ext.datecode);
 
 	/* Check whether we support this module */
-	if (!sfp->type->module_supported(&sfp->id)) {
+	if (!sfp->type->module_supported(&id)) {
 		dev_err(sfp->dev,
 			"module is not supported - phys id 0x%02x 0x%02x\n",
 			sfp->id.base.phys_id, sfp->id.base.phys_ext_id);
@@ -1701,6 +1765,7 @@  static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 		if (sfp->mod_phy)
 			sfp_sm_phy_detach(sfp);
 		sfp_module_tx_disable(sfp);
+		sfp_soft_stop_poll(sfp);
 		sfp_sm_next(sfp, SFP_S_DOWN, 0);
 		return;
 	}
@@ -1712,7 +1777,10 @@  static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 		    sfp->sm_dev_state != SFP_DEV_UP)
 			break;
 
-		sfp_sm_mod_init(sfp);
+		if (!(sfp->id.ext.diagmon & SFP_DIAGMON_ADDRMODE))
+			sfp_soft_start_poll(sfp);
+
+		sfp_module_tx_enable(sfp);
 
 		/* Initialise the fault clearance retries */
 		sfp->sm_retries = 5;
@@ -1968,7 +2036,10 @@  static void sfp_poll(struct work_struct *work)
 	struct sfp *sfp = container_of(work, struct sfp, poll.work);
 
 	sfp_check_state(sfp);
-	mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
+
+	if (sfp->state_soft_mask & (SFP_F_LOS | SFP_F_TX_FAULT) ||
+	    sfp->need_poll)
+		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 }
 
 static struct sfp *sfp_alloc(struct device *dev)
@@ -2013,7 +2084,6 @@  static int sfp_probe(struct platform_device *pdev)
 	const struct sff_data *sff;
 	struct i2c_adapter *i2c;
 	struct sfp *sfp;
-	bool poll = false;
 	int err, i;
 
 	sfp = sfp_alloc(&pdev->dev);
@@ -2120,7 +2190,7 @@  static int sfp_probe(struct platform_device *pdev)
 
 		sfp->gpio_irq[i] = gpiod_to_irq(sfp->gpio[i]);
 		if (!sfp->gpio_irq[i]) {
-			poll = true;
+			sfp->need_poll = true;
 			continue;
 		}
 
@@ -2132,11 +2202,11 @@  static int sfp_probe(struct platform_device *pdev)
 						dev_name(sfp->dev), sfp);
 		if (err) {
 			sfp->gpio_irq[i] = 0;
-			poll = true;
+			sfp->need_poll = true;
 		}
 	}
 
-	if (poll)
+	if (sfp->need_poll)
 		mod_delayed_work(system_wq, &sfp->poll, poll_jiffies);
 
 	/* We could have an issue in cases no Tx disable pin is available or
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index 3b35efd85bb1..487fd9412d10 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -428,6 +428,10 @@  enum {
 	SFP_TEC_CUR			= 0x6c,
 
 	SFP_STATUS			= 0x6e,
+	SFP_STATUS_TX_DISABLE		= BIT(7),
+	SFP_STATUS_TX_DISABLE_FORCE	= BIT(6),
+	SFP_STATUS_TX_FAULT		= BIT(2),
+	SFP_STATUS_RX_LOS		= BIT(1),
 	SFP_ALARM0			= 0x70,
 	SFP_ALARM0_TEMP_HIGH		= BIT(7),
 	SFP_ALARM0_TEMP_LOW		= BIT(6),