From nobody Fri Jun 12 11:30:20 2026 Received: from mail-wm1-f53.google.com (mail-wm1-f53.google.com [209.85.128.53]) (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 967F139A06B for ; Fri, 15 May 2026 11:55:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778846136; cv=none; b=Lsv9Fp/skxO61Ctfod217cPPMEBijvKeMU5hnZQ57i2MOsEscvrdJAo1BbHHmRvanOtsMBsn1ODEv1q0gb4nuHDf5IG+LAUhC4UvXqP0373GdUJzJfl26IVBo2G7BIaNTsrxxeU3Vbdu3alggtIpa5BOe6fTon7548rzjh3q7GM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778846136; c=relaxed/simple; bh=V6tTcHsDwH4Jkq806I6PVZvta1dDeg0sm0elJVzoiko=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=a3MAUzxfP9dGFF2GeYKTnfAf/qhnFcF8aqFNm/Y5mUXscneW9ngsrNFSIvVt5bDEHK43JVed/95p19cckQC1fPmQ2GP8YtS42e/7h3bRfv5UjB7WF3OctLkVBEWOFESGvL17xC+gWCtQihOZtayX4Ho2OmfJPhDNDeiWNb2PsIM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fe5MBmnW; arc=none smtp.client-ip=209.85.128.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fe5MBmnW" Received: by mail-wm1-f53.google.com with SMTP id 5b1f17b1804b1-48fde648a71so6569535e9.0 for ; Fri, 15 May 2026 04:55:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778846133; x=1779450933; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=ztSL5T0ZAVv9FW8m9Rl7jvhEnDHUw0SpW0Ca+WqJD/U=; b=fe5MBmnW0DjK/KTyj04Ls8IXvJgJuYqVTGDfuGvXPbD24PP81DaLBOmoMALSnqV3zs /CVHk+1j5cDBnwIFsYmfetHYar/B+/No4w5TpLqKsrqU0qS8CVm4RzynQX1JXn0TEQbB xPqxPwbEKtuz02c3wJ2FXfNOqKL0H5e3fD3KLtctYoYBJDKBLxajf5NqdSdtgr14Re5U JJfAdh1DzBeFjsMc2003H5aFQoqLMPtTKhfYf0pZu9Q/Oxfy6s7VgvxsRlmMMpXyDXkD C9pHV1v2xz4DgfT7jaCn7wmMsTrHC2wMG20KXFSUP2M4ifwibmQYXGz9XmtQwxEbERA3 bBfA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778846133; x=1779450933; 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=ztSL5T0ZAVv9FW8m9Rl7jvhEnDHUw0SpW0Ca+WqJD/U=; b=Ig02w7mk5KzmjXD6lSJ13wZK4BzuwoFXi3j0Im/9tpXIxrYvC4if/Jy1JwDJFa5v4Z OlZ0yKXeQvqkFpEJyGWia6irBmJ7NCKWUsz26WnDalxwFsLVUPiDMTLPBjhnGRlpFdQ/ hDOE+UHz3bpJmmbOdSnN9yqjo9xpVxR9vBGYoYf3U9zHWg/0r3UimJ6K1T7lgYwgEuCK i8Dn2hW8kjil4IVkFA/K/LHH2LU4pZikmOVN6+FYVC+rTAcYMAOTs+pYXSAztA9mwiJX zbnIyb41HIvoPvM3CdEEl0/cPUMFz8Y3AqMVls6d6lXUghklCBfq5beA4+e7JTRtbX9E sD3w== X-Forwarded-Encrypted: i=1; AFNElJ8MpV33to0Lh1KbaUnLxN9mQjn0gbxOQsYD0/NxGiqKrzdT4QXJdD0z1HBO0LcFs9XX/KtLtd0dgwrds3Y=@vger.kernel.org X-Gm-Message-State: AOJu0Ywk91pJIakPOv8MbcTTNuB7So0Y20A9noFx+jkTDP+RCAKvkpDk zl9VnPoEtZuRi3kdNWxSRsYR5fgFUr1RW3yimXzXoEV4gh+qYaDDd/2/ X-Gm-Gg: Acq92OG+wT6npY8riYFvqV2qWilpIYaoK2c/l1L8txmFxUQVL1e5WQeXQSxJ/ZTf0mn LHklbRgeD9JM40X1uDIOS4tmt9AQlWUdoZwOCFml1ggNA1K0psKimFFlad7OJLvVJAWKbC+0S1a 27iQ+ARGgow/skXESYRPxyLCRH/j4dSIDB5QbZmSIbYpeGkfgg+gAvS7Qa9pZTErYflOgZBLBS/ wvPvJQ26pyjRSBiAqAgK1A1woHYc2NvWQWblIPaJTHUrN2/8+1qy2ChD+3G4fJVALmDvO5ZKoqc hFf9MMELV6dsgauPeAw3RJegNx3gP1gXjCupxo/J2vSfQU+gFPiycCFW+my4fQvXMpj4ZUUveVL bqPuS7r64wZNp2mRT0z0s6HjJCVo3rKZmAdwnTiNQXzQxte0tZZfoe+Cpk4hoY4k30oT2wVYmp6 8BRA1zhiMJ/XsNypM6CJeoo13VtCeLb401Hxtf7c3TMWEQa3n+YUavBAI= X-Received: by 2002:a05:600c:1688:b0:488:c21a:4754 with SMTP id 5b1f17b1804b1-48fd646b8a0mr77077225e9.18.1778846132798; Fri, 15 May 2026 04:55:32 -0700 (PDT) Received: from localhost.localdomain ([87.236.194.191]) by smtp.gmail.com with ESMTPSA id ffacd0b85a97d-45da15a6449sm13740683f8f.37.2026.05.15.04.55.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 15 May 2026 04:55:32 -0700 (PDT) From: Petr Wozniak To: linux-netdev@vger.kernel.org Cc: bjorn@mork.no, andrew@lunn.ch, linux-kernel@vger.kernel.org Subject: [PATCH] net: phy: sfp: probe for RollBall I2C-to-MDIO bridge before assuming MDIO_I2C_ROLLBALL Date: Fri, 15 May 2026 13:55:27 +0200 Message-ID: <20260515115527.17241-1-petr.wozniak@gmail.com> X-Mailer: git-send-email 2.50.1 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" The "OEM"/"SFP-10G-T" quirk entry in sfp_fixup_rollball_cc() unconditionally forces MDIO_I2C_ROLLBALL for all modules matching that vendor/part-number combination. This works for modules that genuinely implement a RollBall I2C-to-MDIO bridge, but silently breaks modules that share the same EEPROM strings without having such a bridge. The Realtek RTL8261BE-CG is one such module: a pure copper 10G SFP+ media converter with no I2C-to-MDIO bridge. Its EEPROM reports vendor=3D"OEM", part=3D"SFP-10G-T", and -- critically -- Vendor OUI 00:00:00, making OUI-based differentiation impossible. With MDIO_I2C_ROLLBALL the kernel stalls waiting for a PHY that never appears: sfp sfp2: probing phy device through the [MDIO_I2C_ROLLBALL] protocol sfp sfp2: no PHY detected, 24 tries left sfp sfp2: no PHY detected, 23 tries left ... Fix this by probing for the RollBall bridge before committing to a protocol. The probe sends the RollBall unlock password (four 0xFF bytes to A2h:VSL+3), switches to MDIO page 3, issues CMD_READ, and polls for CMD_DONE. A genuine RollBall bridge responds with CMD_DONE within ~70 ms. A module without a bridge never asserts it; the probe times out after 200 ms (10 x 20 ms). On probe success the existing MDIO_I2C_ROLLBALL + extended_cc path is taken -- no behaviour change for real RollBall modules. On timeout, MDIO_I2C_NONE is selected, telling the MAC to derive link parameters directly from the EEPROM-declared interface type (10gbase-r) without attempting PHY register access. The probe adds at most 200 ms at link-up time for the no-bridge case, and ~70 ms for real RollBall modules. This is a one-shot cost, not on the data path. Also add a separate quirk entry for the industrial variant "SFP-10G-T-I" which uses the same RTL8261BE silicon. Tested on BPI-R4 (MediaTek MT7988A, Linux 6.12): - RTL8261BE negative case (no bridge, probe timeout -> MDIO_I2C_NONE): link up 10Gbps, iperf3 9.34 Gbit/s (93% line rate) [OK] - RollBall positive case (CMD_DONE -> MDIO_I2C_ROLLBALL): logic mirrors the unlock sequence already in mdio-i2c.c; not yet verified on physical RollBall hardware due to lack of a suitable test module. Signed-off-by: Petr Wozniak --- drivers/net/phy/sfp.c | 79 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c index XXXXXXX..XXXXXXX 100644 --- a/drivers/net/phy/sfp.c +++ b/drivers/net/phy/sfp.c @@ -960,11 +960,88 @@ static void sfp_fixup_rollball(struct sfp *sfp) sfp->phy_t_retry =3D msecs_to_jiffies(1000); } =20 -static void sfp_fixup_rollball_cc(struct sfp *sfp) +/* Local mirrors of RollBall protocol constants from mdio-i2c.c */ +#define SFP_ROLLBALL_PHY_ADDR 0x51 +#define SFP_ROLLBALL_MDIO_PAGE 3 +#define SFP_ROLLBALL_CMD_ADDR 0x80 +#define SFP_ROLLBALL_CMD_READ 0x02 +#define SFP_ROLLBALL_CMD_DONE 0x04 + +static int sfp_rollball_a2_write(struct sfp *sfp, u8 reg, + const u8 *data, int len) +{ + struct i2c_msg msg; + u8 buf[8]; + + buf[0] =3D reg; + memcpy(buf + 1, data, len); + msg.addr =3D SFP_ROLLBALL_PHY_ADDR; + msg.flags =3D 0; + msg.len =3D len + 1; + msg.buf =3D buf; + return i2c_transfer(sfp->i2c, &msg, 1) =3D=3D 1 ? 0 : -EIO; +} + +static int sfp_rollball_a2_read(struct sfp *sfp, u8 reg, u8 *val) { - sfp_fixup_rollball(sfp); + struct i2c_msg msgs[2]; + + msgs[0].addr =3D SFP_ROLLBALL_PHY_ADDR; + msgs[0].flags =3D 0; + msgs[0].len =3D 1; + msgs[0].buf =3D ® + msgs[1].addr =3D SFP_ROLLBALL_PHY_ADDR; + msgs[1].flags =3D I2C_M_RD; + msgs[1].len =3D 1; + msgs[1].buf =3D val; + return i2c_transfer(sfp->i2c, msgs, 2) =3D=3D 2 ? 0 : -EIO; +} + +/** + * sfp_has_rollball_bridge() - probe for a RollBall I2C-to-MDIO bridge + * @sfp: SFP instance + * + * Send the RollBall unlock password, switch to the MDIO page, issue CMD_R= EAD + * and poll for CMD_DONE. A genuine RollBall bridge asserts CMD_DONE within + * ~70 ms. Modules without a bridge (e.g. RTL8261BE pure media converter) + * never respond; the poll times out after 200 ms. + * + * Returns true if a RollBall bridge is present, false otherwise. + */ +static bool sfp_has_rollball_bridge(struct sfp *sfp) +{ + u8 pw[4] =3D { 0xff, 0xff, 0xff, 0xff }; + u8 page =3D SFP_ROLLBALL_MDIO_PAGE; + u8 cmd =3D SFP_ROLLBALL_CMD_READ; + u8 saved_page =3D 0, res; + int i; + + if (sfp_rollball_a2_write(sfp, SFP_VSL + 3, pw, sizeof(pw)) < 0) + return false; + + sfp_rollball_a2_read(sfp, SFP_PAGE, &saved_page); + + if (sfp_rollball_a2_write(sfp, SFP_PAGE, &page, 1) < 0 || + sfp_rollball_a2_write(sfp, SFP_ROLLBALL_CMD_ADDR, &cmd, 1) < 0) + goto restore; + + for (i =3D 0; i < 10; i++) { + msleep(20); + if (!sfp_rollball_a2_read(sfp, SFP_ROLLBALL_CMD_ADDR, &res) && + res =3D=3D SFP_ROLLBALL_CMD_DONE) { + sfp_rollball_a2_write(sfp, SFP_PAGE, &saved_page, 1); + return true; + } + } + +restore: + sfp_rollball_a2_write(sfp, SFP_PAGE, &saved_page, 1); + return false; +} + +static void sfp_fixup_rollball_cc(struct sfp *sfp) { + /* Probe for an I2C-to-MDIO bridge: genuine RollBall modules assert + * CMD_DONE within ~70 ms; pure media converters such as the RTL8261BE + * have no bridge and time out after 200 ms. + */ + if (!sfp_has_rollball_bridge(sfp)) { + sfp->mdio_protocol =3D MDIO_I2C_NONE; + return; + } + sfp_fixup_rollball(sfp); /* Some RollBall SFPs may have wrong (zero) extended compliance code * burned in EEPROM. For PHY probing we need the correct one. */ @@ -1036,7 +1113,8 @@ static const struct sfp_quirk sfp_quirks[] =3D { SFP_QUIRK_S("OEM", "SFP-GE-T", sfp_fixup_ignore_tx_fault), SFP_QUIRK_S("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g), SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-D", sfp_quirk_2500basex), - SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), + SFP_QUIRK_F("OEM", "SFP-10G-T-I", sfp_fixup_rollball_cc), + SFP_QUIRK_F("OEM", "SFP-10G-T", sfp_fixup_rollball_cc), SFP_QUIRK_S("OEM", "SFP-2.5G-BX10-U", sfp_quirk_2500basex),