]> git.ipfire.org Git - thirdparty/openwrt.git/commitdiff
generic: 6.18: backport SFP I2C presence detection without MOD_DEF0 GPIO 23836/head
authorCarlo Szelinsky <github@szelinsky.de>
Tue, 16 Jun 2026 19:12:23 +0000 (21:12 +0200)
committerJonas Jelonek <jelonek.jonas@gmail.com>
Wed, 17 Jun 2026 06:53:33 +0000 (08:53 +0200)
Backport upstream net-next commit 8ac44d24c3a1. An SFP cage whose MOD_DEF0
signal is not wired to a readable GPIO currently uses sff_gpio_get_state(),
which always reports the module present: an empty cage gets stuck in
MOD_ERROR, hot-insertion is never detected and empty cages spam -EIO at
boot. Derive presence from a throttled single-byte I2C read of the module
EEPROM instead, so hot-plug works and the boot spam stops. A soldered-down
"sff,sff" module stays always-present.

This helps RTL93xx SFP boards that route no cage presence signal to a GPIO.

Remaining sfp.c patches refreshed.

Link: https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=8ac44d24c3a1
Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
Link: https://github.com/openwrt/openwrt/pull/23836
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
target/linux/generic/backport-6.18/711-v7.2-net-phy-sfp-detect-presence-via-I2C-when-no-MOD_DEF0-GPIO.patch [new file with mode: 0644]
target/linux/generic/pending-6.18/750-net-sfp-add-quirk-for-QINIYEK-BJ-SFP-10G-T-copper-SF.patch
target/linux/generic/pending-6.18/751-net-sfp-add-quirk-for-TP-LINK-SM410U.patch
target/linux/realtek/patches-6.18/705-v7.1-net-sfp-initialize-i2c_block_size-at-adapter-configu.patch
target/linux/realtek/patches-6.18/706-01-v7.2-net-sfp-apply-I2C-adapter-quirks-to-limit-block-size.patch
target/linux/realtek/patches-6.18/706-02-v7.2-net-sfp-extend-SMBus-support.patch
target/linux/realtek/patches-6.18/714-net-phy-sfp-add-support-for-SMBus.patch

diff --git a/target/linux/generic/backport-6.18/711-v7.2-net-phy-sfp-detect-presence-via-I2C-when-no-MOD_DEF0-GPIO.patch b/target/linux/generic/backport-6.18/711-v7.2-net-phy-sfp-detect-presence-via-I2C-when-no-MOD_DEF0-GPIO.patch
new file mode 100644 (file)
index 0000000..aca784e
--- /dev/null
@@ -0,0 +1,156 @@
+From 8ac44d24c3a148c4177bd3ad790c377279f4674f Mon Sep 17 00:00:00 2001
+From: Greg Patrick <gregspatrick@hotmail.com>
+Date: Thu, 11 Jun 2026 17:53:41 +0000
+Subject: [PATCH] net: phy: sfp: detect presence via I2C when no MOD_DEF0 GPIO
+
+An SFP cage (compatible "sff,sfp") whose MOD_DEF0 signal is not wired to a
+GPIO currently falls back to sff_gpio_get_state(), which unconditionally
+reports the module as present. An empty cage therefore fails its probe and
+is parked in SFP_MOD_ERROR forever; because SFP_F_PRESENT never deasserts
+there is no REMOVE event to recover the state machine, so a module inserted
+after boot is never detected, and empty cages spam -EIO at boot.
+
+This affects boards that route none of the cage presence signal to a
+software-readable input. On the NicGiga S100-0800S-M (RTL9303, 8x SFP+) the
+cage I2C bus is the switch's SMBus master; TX_DISABLE is driven via a
+PCA9534 I/O expander, but no MOD_ABS/MOD_DEF0 line reaches a readable GPIO
+(the RTL9303 gpio0 lines read stuck-low, the single PCA9534 is fully
+consumed by TX_DISABLE, and there is no RTL8231). The Horaco ZX-SW82TS-L2P
+(RTL9302D, 2x SFP+) is independently affected in the same way.
+
+For such an SFP cage, derive presence from a throttled single-byte I2C read
+of the module EEPROM instead: a successful read asserts SFP_F_PRESENT,
+R_PROBE_ABSENT consecutive failures clear it (to ride out a transient error
+on a live module). The existing poll then emits SFP_E_INSERT / SFP_E_REMOVE
+normally, giving working hot-plug and silencing the boot-time -EIO spam on
+empty cages. Presence is re-probed every T_PROBE_PRESENT, so insertion is
+detected within that interval and removal within
+T_PROBE_PRESENT * R_PROBE_ABSENT.
+
+A soldered-down module (compatible "sff,sff") has no presence signal and is
+genuinely always present, so it continues to use sff_gpio_get_state(); the
+new path is gated on the cage type advertising SFP_F_PRESENT.
+
+Signed-off-by: Greg Patrick <gregspatrick@hotmail.com>
+Tested-by: Manuel Stocker <mensi@mensi.ch>
+Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Tested-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
+Link: https://patch.msgid.link/20260611175341.2223184-1-gregspatrick@hotmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+---
+ drivers/net/phy/sfp.c | 83 +++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 80 insertions(+), 3 deletions(-)
+
+--- a/drivers/net/phy/sfp.c
++++ b/drivers/net/phy/sfp.c
+@@ -206,6 +206,16 @@ static const enum gpiod_flags gpio_flags
+ #define T_PROBE_RETRY_SLOW    msecs_to_jiffies(5000)
+ #define R_PROBE_RETRY_SLOW    12
++/* Polling interval and consecutive-failure threshold for the I2C presence
++ * probe used on boards without a MOD_DEF0 GPIO (see sfp_i2c_get_state()).
++ * A single successful read asserts presence immediately; R_PROBE_ABSENT
++ * consecutive failures are required to declare a live module removed, to ride
++ * out a transient I2C error. Insertion is thus detected within
++ * T_PROBE_PRESENT and removal within T_PROBE_PRESENT * R_PROBE_ABSENT.
++ */
++#define T_PROBE_PRESENT               msecs_to_jiffies(500)
++#define R_PROBE_ABSENT                3
++
+ /* SFP modules appear to always have their PHY configured for bus address
+  * 0x56 (which with mdio-i2c, translates to a PHY address of 22).
+  * RollBall SFPs access phy via SFP Enhanced Digital Diagnostic Interface
+@@ -249,6 +259,13 @@ struct sfp {
+       bool need_poll;
++      /* I2C-probed presence, for boards without a MOD_DEF0 GPIO.
++       * Access rules: st_mutex held (updated from the poll/state machine).
++       */
++      bool i2c_present;
++      u8 i2c_present_nak;
++      unsigned long i2c_present_next;
++
+       /* Access rules:
+        * state_hw_drive: st_mutex held
+        * state_hw_mask: st_mutex held
+@@ -860,6 +877,45 @@ static int sfp_read(struct sfp *sfp, boo
+       return sfp->read(sfp, a2, addr, buf, len);
+ }
++/* Probe whether a module is physically present by attempting a single-byte
++ * I2C read of the EEPROM identifier (an empty cage NAKs). Used as the presence
++ * source on boards that do not wire MOD_DEF0 to a GPIO.
++ */
++static bool sfp_module_present_i2c(struct sfp *sfp)
++{
++      u8 id;
++
++      return sfp_read(sfp, false, SFP_PHYS_ID, &id, sizeof(id)) == sizeof(id);
++}
++
++/* get_state variant for boards without a MOD_DEF0 GPIO. Instead of assuming
++ * the module is always present, derive SFP_F_PRESENT from a throttled I2C
++ * probe so that hot-insertion and removal are detected. A single ACK asserts
++ * presence; R_PROBE_ABSENT consecutive failures clear it, to ride out a
++ * transient I2C error on a live module.
++ */
++static unsigned int sfp_i2c_get_state(struct sfp *sfp)
++{
++      unsigned int state = sfp_gpio_get_state(sfp);
++
++      if (time_after_eq(jiffies, sfp->i2c_present_next)) {
++              if (sfp_module_present_i2c(sfp)) {
++                      sfp->i2c_present = true;
++                      sfp->i2c_present_nak = 0;
++              } else if (sfp->i2c_present &&
++                         ++sfp->i2c_present_nak >= R_PROBE_ABSENT) {
++                      sfp->i2c_present = false;
++                      sfp->i2c_present_nak = 0;
++              }
++              sfp->i2c_present_next = jiffies + T_PROBE_PRESENT;
++      }
++
++      if (sfp->i2c_present)
++              state |= SFP_F_PRESENT;
++
++      return state;
++}
++
+ static int sfp_write(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len)
+ {
+       return sfp->write(sfp, a2, addr, buf, len);
+@@ -3158,9 +3214,30 @@ static int sfp_probe(struct platform_dev
+       sfp->get_state = sfp_gpio_get_state;
+       sfp->set_state = sfp_gpio_set_state;
+-      /* Modules that have no detect signal are always present */
+-      if (!(sfp->gpio[GPIO_MODDEF0]))
+-              sfp->get_state = sff_gpio_get_state;
++      /* An SFP cage with no MOD_DEF0 GPIO has no hardware presence signal.
++       * Assuming the module is always present traps an empty cage in
++       * MOD_ERROR and never detects hot-insertion, so derive presence from a
++       * throttled I2C probe and poll for changes instead. sfp_i2c_configure()
++       * has already set i2c_max_block_size; seed i2c_block_size so the
++       * presence read does not issue a zero-length transfer before the first
++       * EEPROM read. Seed i2c_present_next to jiffies so the first probe
++       * happens immediately (a zero value would be in the past relative to
++       * the negative INITIAL_JIFFIES at boot and delay detection).
++       *
++       * A soldered-down module (sff,sff) has no presence signal and is
++       * genuinely always present, so it keeps the always-present behaviour;
++       * the I2C probe is gated on the cage type advertising SFP_F_PRESENT.
++       */
++      if (!sfp->gpio[GPIO_MODDEF0]) {
++              if (sff->gpios & SFP_F_PRESENT) {
++                      sfp->get_state = sfp_i2c_get_state;
++                      sfp->i2c_block_size = sfp->i2c_max_block_size;
++                      sfp->i2c_present_next = jiffies;
++                      sfp->need_poll = true;
++              } else {
++                      sfp->get_state = sff_gpio_get_state;
++              }
++      }
+       device_property_read_u32(&pdev->dev, "maximum-power-milliwatt",
+                                &sfp->max_power_mW);
index 21ad584fdb2a6caa97b81a4ced3e2c6849c52f47..b09463fcddbc40d1cba140d87e01d976bf56b67d 100644 (file)
@@ -16,7 +16,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/phy/sfp.c
 +++ b/drivers/net/phy/sfp.c
-@@ -585,6 +585,7 @@ static const struct sfp_quirk sfp_quirks
+@@ -602,6 +602,7 @@ static const struct sfp_quirk sfp_quirks
        SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),
        SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc),
        SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc),
index b0b71cb91747df7d2d301bdc513098542df7f91c..c1f17ae69b8c2d8f31efb126eaead7d4287397f9 100644 (file)
@@ -14,7 +14,7 @@ Signed-off-by: Daniel Golle <daniel@makrotopia.org>
 
 --- a/drivers/net/phy/sfp.c
 +++ b/drivers/net/phy/sfp.c
-@@ -591,6 +591,7 @@ static const struct sfp_quirk sfp_quirks
+@@ -608,6 +608,7 @@ static const struct sfp_quirk sfp_quirks
        SFP_QUIRK_F("Turris", "RTSFP-10G", sfp_fixup_rollball),
  
        SFP_QUIRK_S("ZOERAX", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
index c252aa95a05876442ad447856011b6d1a6480f40..b1cac719cb2699b109f1cbf6f0c32ca1dea5eb60 100644 (file)
@@ -31,7 +31,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 
 --- a/drivers/net/phy/sfp.c
 +++ b/drivers/net/phy/sfp.c
-@@ -824,6 +824,7 @@ static int sfp_i2c_configure(struct sfp
+@@ -841,6 +841,7 @@ static int sfp_i2c_configure(struct sfp
                return -EINVAL;
        }
  
index 75164506462b0f329eb427ec810bb4f6716d7d22..d2ce503e81046b4ec3e43e44e72f47f958186ddf 100644 (file)
@@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 
 --- a/drivers/net/phy/sfp.c
 +++ b/drivers/net/phy/sfp.c
-@@ -809,21 +809,29 @@ static int sfp_smbus_byte_write(struct s
+@@ -826,21 +826,29 @@ static int sfp_smbus_byte_write(struct s
  
  static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
  {
index e09244370725c2a4c8f63495900e9ad9aeb582c1..c00c0d8580384d4726bf6eb5d9bf70c177fc448a 100644 (file)
@@ -75,7 +75,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
  #include <linux/workqueue.h>
  
  #include "sfp.h"
-@@ -758,50 +759,113 @@ static int sfp_i2c_write(struct sfp *sfp
+@@ -775,50 +776,113 @@ static int sfp_i2c_write(struct sfp *sfp
        return ret == ARRAY_SIZE(msgs) ? len : 0;
  }
  
@@ -213,7 +213,7 @@ Signed-off-by: Jakub Kicinski <kuba@kernel.org>
        }
  
        return data - (u8 *)buf;
-@@ -817,10 +881,29 @@ static int sfp_i2c_configure(struct sfp
+@@ -834,10 +898,29 @@ static int sfp_i2c_configure(struct sfp
                sfp->read = sfp_i2c_read;
                sfp->write = sfp_i2c_write;
                max_block_size = SFP_EEPROM_BLOCK_SIZE;
index cc6283f0a7ce71871e84e0ab5348780983cbc3cb..ceeaae4c75a85043769fa5da0571366598510da2 100644 (file)
@@ -10,7 +10,7 @@ Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
 
 --- a/drivers/net/phy/sfp.c
 +++ b/drivers/net/phy/sfp.c
-@@ -942,6 +942,29 @@ static int sfp_i2c_mdiobus_create(struct
+@@ -959,6 +959,29 @@ static int sfp_i2c_mdiobus_create(struct
        return 0;
  }
  
@@ -40,7 +40,7 @@ Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
  static void sfp_i2c_mdiobus_destroy(struct sfp *sfp)
  {
        mdiobus_unregister(sfp->i2c_mii);
-@@ -2116,9 +2139,15 @@ static void sfp_sm_fault(struct sfp *sfp
+@@ -2172,9 +2195,15 @@ static void sfp_sm_fault(struct sfp *sfp
  
  static int sfp_sm_add_mdio_bus(struct sfp *sfp)
  {