From: Russell King <rmk+ker...@armlinux.org.uk>

Some GPON SFP modules (e.g. Ubiquiti U-Fiber Instant) have set both
SFP_OPTIONS_LOS_INVERTED and SFP_OPTIONS_LOS_NORMAL bits in their EEPROM.

Such combination of bits is meaningless so assume that LOS signal is not
implemented.

This patch fixes link carrier for GPON SFP module Ubiquiti U-Fiber Instant.

Signed-off-by: Russell King <rmk+ker...@armlinux.org.uk>
Signed-off-by: Pali Rohár <p...@kernel.org>

---
Changes in v2:
* Fix author/signed-off-by lines
---
 drivers/net/phy/sfp.c | 36 ++++++++++++++++++++++--------------
 1 file changed, 22 insertions(+), 14 deletions(-)

diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index c0a891cdcd73..15fb8f7dfe5b 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -1488,15 +1488,19 @@ static void sfp_sm_link_down(struct sfp *sfp)
 
 static void sfp_sm_link_check_los(struct sfp *sfp)
 {
-       unsigned int los = sfp->state & SFP_F_LOS;
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+       bool los = false;
 
        /* If neither SFP_OPTIONS_LOS_INVERTED nor SFP_OPTIONS_LOS_NORMAL
-        * are set, we assume that no LOS signal is available.
+        * are set, we assume that no LOS signal is available. If both are
+        * set, we assume LOS is not implemented (and is meaningless.)
         */
-       if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED))
-               los ^= SFP_F_LOS;
-       else if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL)))
-               los = 0;
+       if (los_options == los_inverted)
+               los = !(sfp->state & SFP_F_LOS);
+       else if (los_options == los_normal)
+               los = !!(sfp->state & SFP_F_LOS);
 
        if (los)
                sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0);
@@ -1506,18 +1510,22 @@ static void sfp_sm_link_check_los(struct sfp *sfp)
 
 static bool sfp_los_event_active(struct sfp *sfp, unsigned int event)
 {
-       return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) &&
-               event == SFP_E_LOS_LOW) ||
-              (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) &&
-               event == SFP_E_LOS_HIGH);
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+
+       return (los_options == los_inverted && event == SFP_E_LOS_LOW) ||
+              (los_options == los_normal && event == SFP_E_LOS_HIGH);
 }
 
 static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event)
 {
-       return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) &&
-               event == SFP_E_LOS_HIGH) ||
-              (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) &&
-               event == SFP_E_LOS_LOW);
+       const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED);
+       const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL);
+       __be16 los_options = sfp->id.ext.options & (los_inverted | los_normal);
+
+       return (los_options == los_inverted && event == SFP_E_LOS_HIGH) ||
+              (los_options == los_normal && event == SFP_E_LOS_LOW);
 }
 
 static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn)
-- 
2.20.1

Reply via email to