aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch')
-rw-r--r--target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch184
1 files changed, 184 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch b/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch
new file mode 100644
index 0000000000..06b3cb5428
--- /dev/null
+++ b/target/linux/generic/backport-5.4/733-v5.5-net-sfp-split-power-mode-switching-from-probe.patch
@@ -0,0 +1,184 @@
+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;