From nobody Mon Jun 8 05:26:08 2026 Received: from mail-oi1-f180.google.com (mail-oi1-f180.google.com [209.85.167.180]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 36E89331A66 for ; Thu, 4 Jun 2026 15:16:16 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780586178; cv=none; b=SiNTAjIBziRq0yl6OB46fK/36Ve4qrWkIuPxhodgbiqa5ga1Mz7TPTyJoNjoOJ2EQc+mgBllmedvVD0aYevkPDrJ5eigMnYpnxVo7tIzSPBAWX4BmSDGUt9cmi/LRFdyZh0yN95L0uAzhf2WVHE0UuY8tbMVgB0Ie8sMzBUrqRs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780586178; c=relaxed/simple; bh=qp3uSgXMXi3JidF0cUk72uprgKSiEqRV6mAUrfSXZNo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=WoVzlXm6oFF41Ta3IzjnNFFifE+HqbiKEp6pfQpTp5+49XFoYwU0gafrrymwfXTyONsOy0+VQe7h88WNopaV45Gl/HAdLFg93QEvD2UZwgKcU0rCQQY8bgueIdr0eUusvQi/fsraIRaxo9ex2th/5rTwv78P5UrHqzgwrruvJ3E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=hotmail.com; spf=pass smtp.mailfrom=gmail.com; arc=none smtp.client-ip=209.85.167.180 Authentication-Results: smtp.subspace.kernel.org; dmarc=fail (p=none dis=none) header.from=hotmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Received: by mail-oi1-f180.google.com with SMTP id 5614622812f47-486503ae8f0so579073b6e.0 for ; Thu, 04 Jun 2026 08:16:16 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1780586175; x=1781190975; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=7KsIpI/Vh8UZ3BGn8UGQQwPxQbsfP6AVGDXkTgZRzCw=; b=jG3woZLJGmvdQZ3QBL9glVtR8pDiBkyGgbJQEgGBTzsvEJ8sHXs8MhiKZUUQrsTstY GOIVtaPAioQg03GqiGwqeD5ctNPWvtjr3YRKA/64yZzir2kkt80cxkaBKphwzoCfQuTA LyvAujzfZG8KnU+tcNOE0vTabD3tFJ766z9MPQS9hWOBOrHe62Up8oeWnoDYHtvNxea+ O0JjuM5QzAsUEuMIkCHx2iDyHXVUT6Tk17KyS1T7rzNU72SrS0Hv7VFbiJJGMBATcIV+ sW5qK/sbNX6ExqWu9ATq87wXeAZkyU/gR/JilP3xh1uwnJ8OzWLXZL1k33p9gQS68hLk xLzw== X-Forwarded-Encrypted: i=1; AFNElJ+I7Zd3IX4NA2sX8xoKZYSRceaWbiTa3M968qIz93GC5P/U988qynn4aFp7NpGMT/PYxJVyl9vFmoBqh7s=@vger.kernel.org X-Gm-Message-State: AOJu0YwwJUD8oU2S/Asz3ZM0M0F6NDHgdcNGH179iCQlwwgfWRiAYPGv 0f6xJKSngnK4cjXQwQZJ077fgL9oveiRi5OZy1ErUqVEsd9OziRRGLVb X-Gm-Gg: Acq92OGBBbNqlm2w3ElRlX0VMnHgmhDJJpLNQI/FIZ3yk0ZRncKu2U2AcuT+pYc8mHw Y3bNytZS37rG2Jy99FYXLd3V92017/hw08duhHgRgVLt7Ui9lgla2d0G+Tggn1KMCNlo0L5juer dJdFxEqBjPhW+V9w3PEv9TikTYULasWXvdAMwlZBjeS5gnMY2yHyJjRU3HXPe1FQILcI3qNEcVv wAj//c+3hD7JU3VGbL07D5Y8bwWXDbO0fK3vYkgztwhCObcyN86eNWbGKRzd7prIi/QRzfhWpzn BMBDph0X8jyZpunLn60BywMdi0NKr0lcOVfDd0y959QHbxEiyGrPycGVaWGnD9z8Z+LI4+poMVc HC7/Y/+dLjMNd4bvxplQ2fM7obfPCcb/9iLmR13R9L9dX+X6uAsosdK9fHuxSnCPY2zWLa2n54/ JEEQSXkK/QanJQM9f2gNFd5JRoGyAyAGTCbVAFDgIp9DKsyLd4HuGnZZW/36GGaf3vXM93XNV2B UxZSwg83cJWKe+BdG0IvbKA5pPrqH2T X-Received: by 2002:a05:6808:2225:b0:47a:ae7:b604 with SMTP id 5614622812f47-4865aa88e9emr5352994b6e.21.1780586175042; Thu, 04 Jun 2026 08:16:15 -0700 (PDT) Received: from lxc-docker-tertiary.local (pool-108-45-26-115.washdc.fios.verizon.net. [108.45.26.115]) by smtp.gmail.com with ESMTPSA id d75a77b69052e-51775c08234sm54243621cf.6.2026.06.04.08.16.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 04 Jun 2026 08:16:14 -0700 (PDT) From: Greg Patrick To: Russell King , Andrew Lunn , Heiner Kallweit Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni Subject: [PATCH net-next v2] net: phy: sfp: detect presence via I2C when no MOD_DEF0 GPIO Date: Thu, 4 Jun 2026 15:16:14 +0000 Message-ID: <20260604151614.3310544-1-gregspatrick@hotmail.com> X-Mailer: git-send-email 2.53.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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). For such an SFP cage, derive presence from a throttled single-byte I2C read of the module EEPROM instead: an ACK asserts SFP_F_PRESENT, two 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. 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 --- v2: - Keep sff_gpio_get_state() and gate the new I2C-probe presence on the cage advertising SFP_F_PRESENT (an "sff,sfp" cage), so a soldered "sff,sff" module keeps its always-present behaviour. (Andrew Lunn) - Reword the commit message to state precisely that no presence signal reaches a readable input on the affected board, and that the cage bus is the RTL9303 SMBus master. (Andrew Lunn) - On using a 0-byte read like i2cdetect: not available on this board -- the cage master advertises only I2C_FUNC_SMBUS_BYTE_DATA (no I2C_FUNC_I2C, no SMBus Quick Command), so sfp_i2c_configure() selects sfp_smbus_byte_read() and a 1-byte read is the lightest presence primitive; it also works through sfp->read on both raw-I2C and SMBus-only adapters, whereas a zero-length transfer does not. (Andrew Lu= nn) v1: https://lore.kernel.org/netdev/20260602235528.2795028-1-gregspatrick@ho= tmail.com/ drivers/net/phy/sfp.c | 77 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index 376c705a9..056bbe6de 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -206,6 +206,11 @@ static const enum gpiod_flags gpio_flags[] =3D { #define T_PROBE_RETRY_SLOW msecs_to_jiffies(5000) #define R_PROBE_RETRY_SLOW 12 =20 +/* Interval at which sfp_i2c_get_state() re-probes the I2C bus for module + * presence on boards without a MOD_DEF0 GPIO (see sfp_i2c_get_state()). + */ +#define T_PROBE_PRESENT msecs_to_jiffies(2000) + /* 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 +254,13 @@ struct sfp { =20 bool need_poll; =20 + /* 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 +875,44 @@ static int sfp_read(struct sfp *sfp, bool a2, u8 addr,= void *buf, size_t len) return sfp->read(sfp, a2, addr, buf, len); } =20 +/* 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 pre= sence + * 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)) =3D=3D sizeof(i= d); +} + +/* get_state variant for boards without a MOD_DEF0 GPIO. Instead of assumi= ng + * 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 asse= rts + * presence; two 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 =3D sfp_gpio_get_state(sfp); + + if (time_after_eq(jiffies, sfp->i2c_present_next)) { + if (sfp_module_present_i2c(sfp)) { + sfp->i2c_present =3D true; + sfp->i2c_present_nak =3D 0; + } else if (sfp->i2c_present && ++sfp->i2c_present_nak >=3D 2) { + sfp->i2c_present =3D false; + sfp->i2c_present_nak =3D 0; + } + sfp->i2c_present_next =3D jiffies + T_PROBE_PRESENT; + } + + if (sfp->i2c_present) + state |=3D 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 +3218,30 @@ static int sfp_probe(struct platform_device *pdev) sfp->get_state =3D sfp_gpio_get_state; sfp->set_state =3D sfp_gpio_set_state; =20 - /* Modules that have no detect signal are always present */ - if (!(sfp->gpio[GPIO_MODDEF0])) - sfp->get_state =3D 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 =3D sfp_i2c_get_state; + sfp->i2c_block_size =3D sfp->i2c_max_block_size; + sfp->i2c_present_next =3D jiffies; + sfp->need_poll =3D true; + } else { + sfp->get_state =3D sff_gpio_get_state; + } + } =20 device_property_read_u32(&pdev->dev, "maximum-power-milliwatt", &sfp->max_power_mW); --=20 2.53.0