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 Tested-by: Manuel Stocker --- v3: - Reduce the presence re-probe interval to 500 ms and require 3 consecutive failed probes (R_PROBE_ABSENT) to declare a live module removed, cutting removal-detection latency from ~4 s to ~1.5 s while still riding out a transient I2C error on a live module. Polling is gated by the existing need_poll path (100 ms), so this is one single-byte SMBus read per cage every 500 ms. Note that, as a polled scheme with no presence edge, a same-cage swap to a different module within the removal window cannot be observed; this is inherent to boards lacking a readable presence line. (Maxime Chevallier) - Hardware-validated on the NicGiga (now running from flash): insertion into a cage that was empty at boot is detected and links within ~1 s, and clean removal of a live module is detected in ~1.2-1.5 s. - Note the Horaco ZX-SW82TS-L2P (RTL9302D, 2x SFP+) as a second, independent board with the same no-MOD_DEF0 wiring. The Tested-by reflects a test of the I2C-presence mechanism on that board; the v3 delta is the timing constants above. v2: https://lore.kernel.org/netdev/20260604151614.3310544-1-gregspatrick@hotmail.com/ v1: https://lore.kernel.org/netdev/20260602235528.2795028-1-gregspatrick@hotmail.com/ drivers/net/phy/sfp.c | 83 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 376c705a9..5e952d095 100644 --- 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 @@ -863,6 +880,45 @@ static int sfp_read(struct sfp *sfp, bool a2, u8 addr, void *buf, size_t len) 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); @@ -3168,9 +3224,30 @@ static int sfp_probe(struct platform_device *pdev) 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); -- 2.53.0