| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From da5bc1832b325b15e4cca3b63861ecf48be870ef Mon Sep 17 00:00:00 2001 | 
 | 2 | From: Russell King <rmk+kernel@armlinux.org.uk> | 
 | 3 | Date: Sun, 10 Jan 2021 10:58:32 +0000 | 
 | 4 | Subject: [PATCH] net: sfp: cope with SFPs that set both LOS normal and LOS | 
 | 5 |  inverted | 
 | 6 |  | 
 | 7 | The SFP MSA defines two option bits in byte 65 to indicate how the | 
 | 8 | Rx_LOS signal on SFP pin 8 behaves: | 
 | 9 |  | 
 | 10 | bit 2 - Loss of Signal implemented, signal inverted from standard | 
 | 11 |         definition in SFP MSA (often called "Signal Detect"). | 
 | 12 | bit 1 - Loss of Signal implemented, signal as defined in SFP MSA | 
 | 13 |         (often called "Rx_LOS"). | 
 | 14 |  | 
 | 15 | Clearly, setting both bits results in a meaningless situation: it would | 
 | 16 | mean that LOS is implemented in both the normal sense (1 = signal loss) | 
 | 17 | and inverted sense (0 = signal loss). | 
 | 18 |  | 
 | 19 | Unfortunately, there are modules out there which set both bits, which | 
 | 20 | will be initially interpret as "inverted" sense, and then, if the LOS | 
 | 21 | signal changes state, we will toggle between LINK_UP and WAIT_LOS | 
 | 22 | states. | 
 | 23 |  | 
 | 24 | Change our LOS handling to give well defined behaviour: only interpret | 
 | 25 | these bits as meaningful if exactly one is set, otherwise treat it as | 
 | 26 | if LOS is not implemented. | 
 | 27 |  | 
 | 28 | Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk> | 
 | 29 | Reviewed-by: Andrew Lunn <andrew@lunn.ch> | 
 | 30 | Link: https://lore.kernel.org/r/E1kyYQa-0004iR-CU@rmk-PC.armlinux.org.uk | 
 | 31 | Signed-off-by: Jakub Kicinski <kuba@kernel.org> | 
 | 32 | --- | 
 | 33 |  drivers/net/phy/sfp.c | 36 ++++++++++++++++++++++-------------- | 
 | 34 |  1 file changed, 22 insertions(+), 14 deletions(-) | 
 | 35 |  | 
 | 36 | --- a/drivers/net/phy/sfp.c | 
 | 37 | +++ b/drivers/net/phy/sfp.c | 
 | 38 | @@ -1430,15 +1430,19 @@ static void sfp_sm_link_down(struct sfp | 
 | 39 |   | 
 | 40 |  static void sfp_sm_link_check_los(struct sfp *sfp) | 
 | 41 |  { | 
 | 42 | -	unsigned int los = sfp->state & SFP_F_LOS; | 
 | 43 | +	const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); | 
 | 44 | +	const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); | 
 | 45 | +	__be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); | 
 | 46 | +	bool los = false; | 
 | 47 |   | 
 | 48 |  	/* If neither SFP_OPTIONS_LOS_INVERTED nor SFP_OPTIONS_LOS_NORMAL | 
 | 49 | -	 * are set, we assume that no LOS signal is available. | 
 | 50 | +	 * are set, we assume that no LOS signal is available. If both are | 
 | 51 | +	 * set, we assume LOS is not implemented (and is meaningless.) | 
 | 52 |  	 */ | 
 | 53 | -	if (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED)) | 
 | 54 | -		los ^= SFP_F_LOS; | 
 | 55 | -	else if (!(sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL))) | 
 | 56 | -		los = 0; | 
 | 57 | +	if (los_options == los_inverted) | 
 | 58 | +		los = !(sfp->state & SFP_F_LOS); | 
 | 59 | +	else if (los_options == los_normal) | 
 | 60 | +		los = !!(sfp->state & SFP_F_LOS); | 
 | 61 |   | 
 | 62 |  	if (los) | 
 | 63 |  		sfp_sm_next(sfp, SFP_S_WAIT_LOS, 0); | 
 | 64 | @@ -1448,18 +1452,22 @@ static void sfp_sm_link_check_los(struct | 
 | 65 |   | 
 | 66 |  static bool sfp_los_event_active(struct sfp *sfp, unsigned int event) | 
 | 67 |  { | 
 | 68 | -	return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && | 
 | 69 | -		event == SFP_E_LOS_LOW) || | 
 | 70 | -	       (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && | 
 | 71 | -		event == SFP_E_LOS_HIGH); | 
 | 72 | +	const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); | 
 | 73 | +	const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); | 
 | 74 | +	__be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); | 
 | 75 | + | 
 | 76 | +	return (los_options == los_inverted && event == SFP_E_LOS_LOW) || | 
 | 77 | +	       (los_options == los_normal && event == SFP_E_LOS_HIGH); | 
 | 78 |  } | 
 | 79 |   | 
 | 80 |  static bool sfp_los_event_inactive(struct sfp *sfp, unsigned int event) | 
 | 81 |  { | 
 | 82 | -	return (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_INVERTED) && | 
 | 83 | -		event == SFP_E_LOS_HIGH) || | 
 | 84 | -	       (sfp->id.ext.options & cpu_to_be16(SFP_OPTIONS_LOS_NORMAL) && | 
 | 85 | -		event == SFP_E_LOS_LOW); | 
 | 86 | +	const __be16 los_inverted = cpu_to_be16(SFP_OPTIONS_LOS_INVERTED); | 
 | 87 | +	const __be16 los_normal = cpu_to_be16(SFP_OPTIONS_LOS_NORMAL); | 
 | 88 | +	__be16 los_options = sfp->id.ext.options & (los_inverted | los_normal); | 
 | 89 | + | 
 | 90 | +	return (los_options == los_inverted && event == SFP_E_LOS_HIGH) || | 
 | 91 | +	       (los_options == los_normal && event == SFP_E_LOS_LOW); | 
 | 92 |  } | 
 | 93 |   | 
 | 94 |  static void sfp_sm_fault(struct sfp *sfp, unsigned int next_state, bool warn) |