From: Daniel Wagner <wagner.daniel.t@gmail.com>
To: netdev@vger.kernel.org
Cc: Florian Fainelli <florian.fainelli@broadcom.com>,
Andrew Lunn <andrew@lunn.ch>,
Heiner Kallweit <hkallweit1@gmail.com>,
Russell King <linux@armlinux.org.uk>,
bcm-kernel-feedback-list@broadcom.com,
Daniel Wagner <wagner.daniel.t@gmail.com>
Subject: [PATCH net-next] net: phy: bcm84881: add BCM84891/BCM84892 support
Date: Tue, 24 Mar 2026 15:25:04 +0000 [thread overview]
Message-ID: <20260324152503.1522071-2-wagner.daniel.t@gmail.com> (raw)
The BCM84891 and BCM84892 are 10GBASE-T PHYs in the same family as the
BCM84881, sharing the register map and most callbacks. They add USXGMII
as a host interface mode.
There are three behavioral differences from BCM84881:
- USXGMII host interface: the 0x4011 host mode register reports 10GBASER
(the USXGMII SerDes line rate) regardless of the negotiated copper
speed. Skip it for USXGMII; phy_resolve_aneg_linkmode() has already
set the correct speed from standard C45 AN resolution.
- Low power: the PHY firmware has been observed setting MDIO_CTRL1_LPOWER
autonomously (e.g. during SerDes reconfiguration). Clear it in
config_aneg() so AN can proceed.
- Speed LED: bicolor (green/amber) via vendor registers Dev1:0xa82f
(amber gate) and Dev1:0xa83b (color select). 10G = green, else amber,
matching vendor firmware behavior. Cleared at config_init() since the
bootloader may leave it on.
The is_bcm8489x() helper checks phydev->drv->phy_id rather than
phydev->phy_id: for C45-enumerated devices, phy_id is left at 0 by
get_phy_device() and matching uses c45_ids.device_ids[].
Tested on TRENDnet TEG-S750 (RTL9303 + 1x BCM84891 + 4x BCM84892)
running OpenWrt, where the MDIO controller driver is currently
OpenWrt-specific. Link verified at 100M, 1G, 2.5G, 10G.
Signed-off-by: Daniel Wagner <wagner.daniel.t@gmail.com>
---
drivers/net/phy/bcm84881.c | 97 +++++++++++++++++++++++++++++++++++++-
1 file changed, 96 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index d7f7cc44c5..e95242486d 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -20,6 +20,10 @@ enum {
MDIO_AN_C22 = 0xffe0,
};
+/* BCM84891/92 vendor registers */
+#define BCM8489X_LED_GATE 0xa82f /* Dev1: gates amber only */
+#define BCM8489X_LED_COLOR 0xa83b /* Dev1: color select */
+
static int bcm84881_wait_init(struct phy_device *phydev)
{
int val;
@@ -29,6 +33,17 @@ static int bcm84881_wait_init(struct phy_device *phydev)
100000, 2000000, false);
}
+static bool bcm84881_is_bcm8489x(struct phy_device *phydev)
+{
+ /* For C45 PHYs, phydev->phy_id is 0; match via the driver entry's
+ * declared ID, which is what phy_bus_match() used to bind us.
+ */
+ u32 id = phydev->drv->phy_id;
+
+ return (id & 0xfffffff0) == 0x35905080 ||
+ (id & 0xfffffff0) == 0x359050a0;
+}
+
static void bcm84881_fill_possible_interfaces(struct phy_device *phydev)
{
unsigned long *possible = phydev->possible_interfaces;
@@ -42,10 +57,22 @@ static int bcm84881_config_init(struct phy_device *phydev)
{
bcm84881_fill_possible_interfaces(phydev);
+ if (bcm84881_is_bcm8489x(phydev)) {
+ __set_bit(PHY_INTERFACE_MODE_USXGMII,
+ phydev->possible_interfaces);
+ /* Bootloader may have left the speed LED on, and
+ * link_change_notify won't fire for ports with no cable.
+ * Clear both color bits (green ignores the gate register).
+ */
+ phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD,
+ BCM8489X_LED_COLOR, 0x0012);
+ }
+
switch (phydev->interface) {
case PHY_INTERFACE_MODE_SGMII:
case PHY_INTERFACE_MODE_2500BASEX:
case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_USXGMII:
break;
default:
return -ENODEV;
@@ -96,6 +123,17 @@ static int bcm84881_config_aneg(struct phy_device *phydev)
if (ret)
return ret;
+ /* BCM84891/92 firmware has been observed setting MDIO_CTRL1_LPOWER
+ * autonomously (e.g. during SerDes reconfiguration). Clear it so AN
+ * can proceed.
+ */
+ if (bcm84881_is_bcm8489x(phydev)) {
+ ret = phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_CTRL1,
+ MDIO_CTRL1_LPOWER);
+ if (ret < 0)
+ return ret;
+ }
+
/* We don't support manual MDI control */
phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
@@ -201,6 +239,15 @@ static int bcm84881_read_status(struct phy_device *phydev)
return 0;
}
+ /* For BCM84891/92 in USXGMII mode, the host-side register 0x4011
+ * reports 10GBASER (the USXGMII SerDes line rate) regardless of the
+ * negotiated copper speed. Skip it; phy_resolve_aneg_linkmode()
+ * above already set the correct speed from standard AN resolution.
+ */
+ if (bcm84881_is_bcm8489x(phydev) &&
+ phydev->interface == PHY_INTERFACE_MODE_USXGMII)
+ return genphy_c45_read_mdix(phydev);
+
/* Set the host link mode - we set the phy interface mode and
* the speed according to this register so that downshift works.
* We leave the duplex setting as per the resolution from the
@@ -235,6 +282,26 @@ static int bcm84881_read_status(struct phy_device *phydev)
return genphy_c45_read_mdix(phydev);
}
+/* BCM8489x speed LED control.
+ * Dev1:a83b bit 1 (0x0002) = green, bit 4 (0x0010) = amber.
+ * Dev1:a82f = 0x0020 gates amber on; green ignores the gate.
+ * Writes are best-effort (LED is cosmetic; this callback is void).
+ */
+static void bcm84881_link_change_notify(struct phy_device *phydev)
+{
+ if (phydev->link) {
+ /* 10G = green, else amber; matches vendor firmware */
+ u16 color = (phydev->speed == SPEED_10000) ? 0x0002 : 0x0010;
+
+ phy_write_mmd(phydev, MDIO_MMD_PMAPMD, BCM8489X_LED_GATE, 0x0020);
+ phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, BCM8489X_LED_COLOR,
+ 0x0012, color);
+ } else {
+ phy_clear_bits_mmd(phydev, MDIO_MMD_PMAPMD,
+ BCM8489X_LED_COLOR, 0x0012);
+ }
+}
+
/* The Broadcom BCM84881 in the Methode DM7052 is unable to provide a SGMII
* or 802.3z control word, so inband will not work.
*/
@@ -257,6 +324,32 @@ static struct phy_driver bcm84881_drivers[] = {
.aneg_done = bcm84881_aneg_done,
.read_status = bcm84881_read_status,
},
+ {
+ .phy_id = 0x35905081,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM84891",
+ .inband_caps = bcm84881_inband_caps,
+ .config_init = bcm84881_config_init,
+ .probe = bcm84881_probe,
+ .get_features = bcm84881_get_features,
+ .config_aneg = bcm84881_config_aneg,
+ .aneg_done = bcm84881_aneg_done,
+ .read_status = bcm84881_read_status,
+ .link_change_notify = bcm84881_link_change_notify,
+ },
+ {
+ .phy_id = 0x359050a1,
+ .phy_id_mask = 0xfffffff0,
+ .name = "Broadcom BCM84892",
+ .inband_caps = bcm84881_inband_caps,
+ .config_init = bcm84881_config_init,
+ .probe = bcm84881_probe,
+ .get_features = bcm84881_get_features,
+ .config_aneg = bcm84881_config_aneg,
+ .aneg_done = bcm84881_aneg_done,
+ .read_status = bcm84881_read_status,
+ .link_change_notify = bcm84881_link_change_notify,
+ },
};
module_phy_driver(bcm84881_drivers);
@@ -264,9 +357,11 @@ module_phy_driver(bcm84881_drivers);
/* FIXME: module auto-loading for Clause 45 PHYs seems non-functional */
static const struct mdio_device_id __maybe_unused bcm84881_tbl[] = {
{ 0xae025150, 0xfffffff0 },
+ { 0x35905081, 0xfffffff0 },
+ { 0x359050a1, 0xfffffff0 },
{ },
};
MODULE_AUTHOR("Russell King");
-MODULE_DESCRIPTION("Broadcom BCM84881 PHY driver");
+MODULE_DESCRIPTION("Broadcom BCM84881/BCM84891/BCM84892 PHY driver");
MODULE_DEVICE_TABLE(mdio, bcm84881_tbl);
MODULE_LICENSE("GPL");
--
2.47.3
next reply other threads:[~2026-03-24 15:26 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-24 15:25 Daniel Wagner [this message]
2026-03-24 15:49 ` [PATCH net-next] net: phy: bcm84881: add BCM84891/BCM84892 support Russell King (Oracle)
2026-03-24 18:54 ` Daniel Wagner
2026-03-24 19:18 ` Andrew Lunn
2026-03-24 19:42 ` Daniel Wagner
2026-03-24 20:01 ` Russell King (Oracle)
2026-03-24 21:59 ` Daniel Wagner
2026-03-24 22:53 ` Russell King (Oracle)
2026-03-24 19:32 ` Russell King (Oracle)
2026-03-24 15:52 ` Andrew Lunn
2026-03-24 19:06 ` [PATCH net-next v2] " Daniel Wagner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260324152503.1522071-2-wagner.daniel.t@gmail.com \
--to=wagner.daniel.t@gmail.com \
--cc=andrew@lunn.ch \
--cc=bcm-kernel-feedback-list@broadcom.com \
--cc=florian.fainelli@broadcom.com \
--cc=hkallweit1@gmail.com \
--cc=linux@armlinux.org.uk \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox