mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-11-03 14:34:27 -05:00 
			
		
		
		
	Backport the phy/phylink/sfp patches currently queued in netdev or in mainline necessary to support GPON popular modules, specifically to support Huawei and Nokia GPON modules. Signed-off-by: Russell King <linux@armlinux.org.uk> [jonas.gorski: include kernel version in file names, refresh patches] Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
		
			
				
	
	
		
			185 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			185 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
From fdff863a4ce3677907f64396e34c45025abb6600 Mon Sep 17 00:00:00 2001
 | 
						|
From: Russell King <rmk+kernel@armlinux.org.uk>
 | 
						|
Date: Tue, 5 Nov 2019 12:59:36 +0000
 | 
						|
Subject: [PATCH 631/660] net: sfp: split power mode switching from probe
 | 
						|
 | 
						|
Switch the power mode switching from the probe, so that we don't
 | 
						|
repeatedly re-probe the SFP device if there is a problem accessing
 | 
						|
the registers at I2C address 0x51.
 | 
						|
 | 
						|
In splitting this out, we can also fix a bug where we leave the module
 | 
						|
in high-power mode when the upstream device is detached but the module
 | 
						|
is still inserted.
 | 
						|
 | 
						|
Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
 | 
						|
---
 | 
						|
 drivers/net/phy/sfp.c | 101 ++++++++++++++++++++++++++----------------
 | 
						|
 1 file changed, 64 insertions(+), 37 deletions(-)
 | 
						|
 | 
						|
--- a/drivers/net/phy/sfp.c
 | 
						|
+++ b/drivers/net/phy/sfp.c
 | 
						|
@@ -47,6 +47,7 @@ enum {
 | 
						|
 	SFP_MOD_EMPTY = 0,
 | 
						|
 	SFP_MOD_PROBE,
 | 
						|
 	SFP_MOD_HPOWER,
 | 
						|
+	SFP_MOD_WAITPWR,
 | 
						|
 	SFP_MOD_PRESENT,
 | 
						|
 	SFP_MOD_ERROR,
 | 
						|
 
 | 
						|
@@ -69,6 +70,7 @@ static const char  * const mod_state_str
 | 
						|
 	[SFP_MOD_EMPTY] = "empty",
 | 
						|
 	[SFP_MOD_PROBE] = "probe",
 | 
						|
 	[SFP_MOD_HPOWER] = "hpower",
 | 
						|
+	[SFP_MOD_WAITPWR] = "waitpwr",
 | 
						|
 	[SFP_MOD_PRESENT] = "present",
 | 
						|
 	[SFP_MOD_ERROR] = "error",
 | 
						|
 };
 | 
						|
@@ -1358,37 +1360,34 @@ static int sfp_module_parse_power(struct
 | 
						|
 	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
-static int sfp_sm_mod_hpower(struct sfp *sfp)
 | 
						|
+static int sfp_sm_mod_hpower(struct sfp *sfp, bool enable)
 | 
						|
 {
 | 
						|
 	u8 val;
 | 
						|
 	int err;
 | 
						|
 
 | 
						|
-	if (sfp->module_power_mW <= 1000)
 | 
						|
-		return 0;
 | 
						|
-
 | 
						|
 	err = sfp_read(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
 | 
						|
 	if (err != sizeof(val)) {
 | 
						|
 		dev_err(sfp->dev, "Failed to read EEPROM: %d\n", err);
 | 
						|
-		err = -EAGAIN;
 | 
						|
-		goto err;
 | 
						|
+		return -EAGAIN;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	val |= BIT(0);
 | 
						|
+	if (enable)
 | 
						|
+		val |= BIT(0);
 | 
						|
+	else
 | 
						|
+		val &= ~BIT(0);
 | 
						|
 
 | 
						|
 	err = sfp_write(sfp, true, SFP_EXT_STATUS, &val, sizeof(val));
 | 
						|
 	if (err != sizeof(val)) {
 | 
						|
 		dev_err(sfp->dev, "Failed to write EEPROM: %d\n", err);
 | 
						|
-		err = -EAGAIN;
 | 
						|
-		goto err;
 | 
						|
+		return -EAGAIN;
 | 
						|
 	}
 | 
						|
 
 | 
						|
-	dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
 | 
						|
-		 sfp->module_power_mW / 1000,
 | 
						|
-		 (sfp->module_power_mW / 100) % 10);
 | 
						|
-	return T_HPOWER_LEVEL;
 | 
						|
+	if (enable)
 | 
						|
+		dev_info(sfp->dev, "Module switched to %u.%uW power level\n",
 | 
						|
+			 sfp->module_power_mW / 1000,
 | 
						|
+			 (sfp->module_power_mW / 100) % 10);
 | 
						|
 
 | 
						|
-err:
 | 
						|
-	return err;
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 static int sfp_sm_mod_probe(struct sfp *sfp)
 | 
						|
@@ -1484,7 +1483,7 @@ static int sfp_sm_mod_probe(struct sfp *
 | 
						|
 	if (ret < 0)
 | 
						|
 		return ret;
 | 
						|
 
 | 
						|
-	return sfp_sm_mod_hpower(sfp);
 | 
						|
+	return 0;
 | 
						|
 }
 | 
						|
 
 | 
						|
 static void sfp_sm_mod_remove(struct sfp *sfp)
 | 
						|
@@ -1529,13 +1528,22 @@ static void sfp_sm_device(struct sfp *sf
 | 
						|
  */
 | 
						|
 static void sfp_sm_module(struct sfp *sfp, unsigned int event)
 | 
						|
 {
 | 
						|
-	/* Handle remove event globally, it resets this state machine.
 | 
						|
-	 * Also deal with upstream detachment.
 | 
						|
-	 */
 | 
						|
-	if (event == SFP_E_REMOVE || sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
						|
+	int err;
 | 
						|
+
 | 
						|
+	/* Handle remove event globally, it resets this state machine */
 | 
						|
+	if (event == SFP_E_REMOVE) {
 | 
						|
 		if (sfp->sm_mod_state > SFP_MOD_PROBE)
 | 
						|
 			sfp_sm_mod_remove(sfp);
 | 
						|
-		if (sfp->sm_mod_state != SFP_MOD_EMPTY)
 | 
						|
+		sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
						|
+		return;
 | 
						|
+	}
 | 
						|
+
 | 
						|
+	/* Handle device detach globally */
 | 
						|
+	if (sfp->sm_dev_state < SFP_DEV_DOWN) {
 | 
						|
+		if (sfp->module_power_mW > 1000 &&
 | 
						|
+		    sfp->sm_mod_state > SFP_MOD_HPOWER)
 | 
						|
+			sfp_sm_mod_hpower(sfp, false);
 | 
						|
+		if (sfp->sm_mod_state > SFP_MOD_EMPTY)
 | 
						|
 			sfp_sm_mod_next(sfp, SFP_MOD_EMPTY, 0);
 | 
						|
 		return;
 | 
						|
 	}
 | 
						|
@@ -1547,26 +1555,45 @@ static void sfp_sm_module(struct sfp *sf
 | 
						|
 		break;
 | 
						|
 
 | 
						|
 	case SFP_MOD_PROBE:
 | 
						|
-		if (event == SFP_E_TIMEOUT) {
 | 
						|
-			int val = sfp_sm_mod_probe(sfp);
 | 
						|
+		if (event != SFP_E_TIMEOUT)
 | 
						|
+			break;
 | 
						|
 
 | 
						|
-			if (val == 0)
 | 
						|
-				sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
						|
-			else if (val > 0)
 | 
						|
-				sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, val);
 | 
						|
-			else if (val != -EAGAIN)
 | 
						|
-				sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
						|
-			else
 | 
						|
-				sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
						|
+		err = sfp_sm_mod_probe(sfp);
 | 
						|
+		if (err == -EAGAIN) {
 | 
						|
+			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
						|
+			break;
 | 
						|
 		}
 | 
						|
-		break;
 | 
						|
+		if (err < 0) {
 | 
						|
+			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
						|
+			break;
 | 
						|
+		}
 | 
						|
+
 | 
						|
+		/* If this is a power level 1 module, we are done */
 | 
						|
+		if (sfp->module_power_mW <= 1000)
 | 
						|
+			goto insert;
 | 
						|
 
 | 
						|
+		sfp_sm_mod_next(sfp, SFP_MOD_HPOWER, 0);
 | 
						|
+		/* fall through */
 | 
						|
 	case SFP_MOD_HPOWER:
 | 
						|
-		if (event == SFP_E_TIMEOUT) {
 | 
						|
-			sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
						|
+		/* Enable high power mode */
 | 
						|
+		err = sfp_sm_mod_hpower(sfp, true);
 | 
						|
+		if (err == 0)
 | 
						|
+			sfp_sm_mod_next(sfp, SFP_MOD_WAITPWR, T_HPOWER_LEVEL);
 | 
						|
+		else if (err != -EAGAIN)
 | 
						|
+			sfp_sm_mod_next(sfp, SFP_MOD_ERROR, 0);
 | 
						|
+		else
 | 
						|
+			sfp_sm_set_timer(sfp, T_PROBE_RETRY);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
+	case SFP_MOD_WAITPWR:
 | 
						|
+		/* Wait for T_HPOWER_LEVEL to time out */
 | 
						|
+		if (event != SFP_E_TIMEOUT)
 | 
						|
 			break;
 | 
						|
-		}
 | 
						|
-		/* fallthrough */
 | 
						|
+
 | 
						|
+	insert:
 | 
						|
+		sfp_sm_mod_next(sfp, SFP_MOD_PRESENT, 0);
 | 
						|
+		break;
 | 
						|
+
 | 
						|
 	case SFP_MOD_PRESENT:
 | 
						|
 	case SFP_MOD_ERROR:
 | 
						|
 		break;
 |