public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892
@ 2026-04-01 11:49 Daniel Wagner
  2026-04-02  1:23 ` Andrew Lunn
  2026-04-03  1:10 ` patchwork-bot+netdevbpf
  0 siblings, 2 replies; 3+ messages in thread
From: Daniel Wagner @ 2026-04-01 11:49 UTC (permalink / raw)
  To: netdev
  Cc: Florian Fainelli, bcm-kernel-feedback-list, Andrew Lunn,
	Heiner Kallweit, Russell King, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Daniel Wagner

Expose LED1 and LED2 pins via the PHY LED framework. Each pin has a
source mask (MASK_LOW + MASK_EXT registers) selecting which hardware
events light it, plus a CTL field in the shared 0xA83B register
(RMW; LED4 is firmware-controlled per the datasheet).

Hardware can offload per-speed link triggers (1000/2500/5000/10000),
RX/TX activity, and force-on. LINK_100 is accepted only alongside
LINK_1000: source bit 4 lights at both speeds and 100-alone isn't
representable, so the unrepresentable case falls to software.

The chip has five LED pins; only LED1/LED2 are exposed here as those
are the only ones characterized on tested hardware. LED4 is firmware-
controlled regardless of strap configuration.

Tested on TRENDnet TEG-S750 (LED1/LED2 wired to an antiparallel
bicolor LED): brightness_set via sysfs; netdev trigger offloaded=1
with amber lit at 100M/1G/2.5G and green lit at 10G via respective
link_* modes; LED off immediately on cable unplug with no software
involvement.

Signed-off-by: Daniel Wagner <wagner.daniel.t@gmail.com>
---
 drivers/net/phy/bcm84881.c | 156 +++++++++++++++++++++++++++++++++++++
 1 file changed, 156 insertions(+)

diff --git a/drivers/net/phy/bcm84881.c b/drivers/net/phy/bcm84881.c
index f114212dd3..2ae70dcf82 100644
--- a/drivers/net/phy/bcm84881.c
+++ b/drivers/net/phy/bcm84881.c
@@ -20,6 +20,33 @@ enum {
 	MDIO_AN_C22 = 0xffe0,
 };
 
+/* BCM8489x LED controller (BCM84891L datasheet 2.4.1.58). Each pin has
+ * CTL bits in 0xA83B (stride 3: 2-bit CTL + 1-bit OE_N) plus MASK_LOW/
+ * MASK_EXT source selects. LED4 is firmware-controlled; always RMW.
+ */
+#define BCM8489X_LED_CTL		0xa83b
+#define BCM8489X_LED_CTL_ON(i)		(0x2 << ((i) * 3))
+#define BCM8489X_LED_CTL_MASK(i)	(0x3 << ((i) * 3))
+
+#define BCM8489X_LED_SRC_RX		BIT(1)
+#define BCM8489X_LED_SRC_TX		BIT(2)
+#define BCM8489X_LED_SRC_1000		BIT(3)	/* high only at 1000 */
+#define BCM8489X_LED_SRC_100_1000	BIT(4)	/* high at 100 and 1000 */
+#define BCM8489X_LED_SRC_FORCE		BIT(5)	/* always-1 source */
+#define BCM8489X_LED_SRC_10G		BIT(7)
+#define BCM8489X_LED_SRCX_2500		BIT(2)
+#define BCM8489X_LED_SRCX_5000		BIT(3)
+
+#define BCM8489X_MAX_LEDS		2
+
+static const struct {
+	u16 mask_low;
+	u16 mask_ext;
+} bcm8489x_led_regs[BCM8489X_MAX_LEDS] = {
+	{ 0xa82c, 0xa8ef },	/* LED1 */
+	{ 0xa82f, 0xa8f0 },	/* LED2 */
+};
+
 static int bcm84881_wait_init(struct phy_device *phydev)
 {
 	int val;
@@ -69,6 +96,127 @@ static int bcm8489x_config_init(struct phy_device *phydev)
 				  MDIO_CTRL1_LPOWER);
 }
 
+static int bcm8489x_led_write(struct phy_device *phydev, u8 index,
+			      u16 low, u16 ext)
+{
+	int ret;
+
+	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
+			    bcm8489x_led_regs[index].mask_low, low);
+	if (ret)
+		return ret;
+	ret = phy_write_mmd(phydev, MDIO_MMD_PMAPMD,
+			    bcm8489x_led_regs[index].mask_ext, ext);
+	if (ret)
+		return ret;
+	return phy_modify_mmd(phydev, MDIO_MMD_PMAPMD, BCM8489X_LED_CTL,
+			      BCM8489X_LED_CTL_MASK(index),
+			      (low | ext) ? BCM8489X_LED_CTL_ON(index) : 0);
+}
+
+static int bcm8489x_led_brightness_set(struct phy_device *phydev,
+				       u8 index, enum led_brightness value)
+{
+	if (index >= BCM8489X_MAX_LEDS)
+		return -EINVAL;
+
+	return bcm8489x_led_write(phydev, index,
+				  value ? BCM8489X_LED_SRC_FORCE : 0, 0);
+}
+
+static const unsigned long bcm8489x_supported_triggers =
+	BIT(TRIGGER_NETDEV_LINK) |
+	BIT(TRIGGER_NETDEV_LINK_100) |
+	BIT(TRIGGER_NETDEV_LINK_1000) |
+	BIT(TRIGGER_NETDEV_LINK_2500) |
+	BIT(TRIGGER_NETDEV_LINK_5000) |
+	BIT(TRIGGER_NETDEV_LINK_10000) |
+	BIT(TRIGGER_NETDEV_RX) |
+	BIT(TRIGGER_NETDEV_TX);
+
+static int bcm8489x_led_hw_is_supported(struct phy_device *phydev, u8 index,
+					unsigned long rules)
+{
+	if (index >= BCM8489X_MAX_LEDS)
+		return -EINVAL;
+
+	if (rules & ~bcm8489x_supported_triggers)
+		return -EOPNOTSUPP;
+
+	/* Source bit 4 lights at both 100 and 1000; "100 only" isn't
+	 * representable in hardware. Accept LINK_100 only alongside
+	 * LINK_1000 or LINK so the offload is precise.
+	 */
+	if ((rules & BIT(TRIGGER_NETDEV_LINK_100)) &&
+	    !(rules & (BIT(TRIGGER_NETDEV_LINK_1000) |
+		       BIT(TRIGGER_NETDEV_LINK))))
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int bcm8489x_led_hw_control_set(struct phy_device *phydev, u8 index,
+				       unsigned long rules)
+{
+	u16 low = 0, ext = 0;
+
+	if (index >= BCM8489X_MAX_LEDS)
+		return -EINVAL;
+
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK)))
+		low |= BCM8489X_LED_SRC_100_1000;
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK)))
+		low |= BCM8489X_LED_SRC_1000;
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK)))
+		ext |= BCM8489X_LED_SRCX_2500;
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK)))
+		ext |= BCM8489X_LED_SRCX_5000;
+	if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK)))
+		low |= BCM8489X_LED_SRC_10G;
+	if (rules & BIT(TRIGGER_NETDEV_RX))
+		low |= BCM8489X_LED_SRC_RX;
+	if (rules & BIT(TRIGGER_NETDEV_TX))
+		low |= BCM8489X_LED_SRC_TX;
+
+	return bcm8489x_led_write(phydev, index, low, ext);
+}
+
+static int bcm8489x_led_hw_control_get(struct phy_device *phydev, u8 index,
+				       unsigned long *rules)
+{
+	int low, ext;
+
+	if (index >= BCM8489X_MAX_LEDS)
+		return -EINVAL;
+
+	low = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+			   bcm8489x_led_regs[index].mask_low);
+	if (low < 0)
+		return low;
+	ext = phy_read_mmd(phydev, MDIO_MMD_PMAPMD,
+			   bcm8489x_led_regs[index].mask_ext);
+	if (ext < 0)
+		return ext;
+
+	*rules = 0;
+	if (low & BCM8489X_LED_SRC_100_1000)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_100);
+	if (low & BCM8489X_LED_SRC_1000)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_1000);
+	if (ext & BCM8489X_LED_SRCX_2500)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_2500);
+	if (ext & BCM8489X_LED_SRCX_5000)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_5000);
+	if (low & BCM8489X_LED_SRC_10G)
+		*rules |= BIT(TRIGGER_NETDEV_LINK_10000);
+	if (low & BCM8489X_LED_SRC_RX)
+		*rules |= BIT(TRIGGER_NETDEV_RX);
+	if (low & BCM8489X_LED_SRC_TX)
+		*rules |= BIT(TRIGGER_NETDEV_TX);
+
+	return 0;
+}
+
 static int bcm84881_probe(struct phy_device *phydev)
 {
 	/* This driver requires PMAPMD and AN blocks */
@@ -290,6 +438,10 @@ static struct phy_driver bcm84881_drivers[] = {
 		.config_aneg	= bcm84881_config_aneg,
 		.aneg_done	= bcm84881_aneg_done,
 		.read_status	= bcm84881_read_status,
+		.led_brightness_set = bcm8489x_led_brightness_set,
+		.led_hw_is_supported = bcm8489x_led_hw_is_supported,
+		.led_hw_control_set = bcm8489x_led_hw_control_set,
+		.led_hw_control_get = bcm8489x_led_hw_control_get,
 	}, {
 		PHY_ID_MATCH_MODEL(0x359050a0),
 		.name		= "Broadcom BCM84892",
@@ -300,6 +452,10 @@ static struct phy_driver bcm84881_drivers[] = {
 		.config_aneg	= bcm84881_config_aneg,
 		.aneg_done	= bcm84881_aneg_done,
 		.read_status	= bcm84881_read_status,
+		.led_brightness_set = bcm8489x_led_brightness_set,
+		.led_hw_is_supported = bcm8489x_led_hw_is_supported,
+		.led_hw_control_set = bcm8489x_led_hw_control_set,
+		.led_hw_control_get = bcm8489x_led_hw_control_get,
 	},
 };
 
-- 
2.47.3


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892
  2026-04-01 11:49 [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892 Daniel Wagner
@ 2026-04-02  1:23 ` Andrew Lunn
  2026-04-03  1:10 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: Andrew Lunn @ 2026-04-02  1:23 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: netdev, Florian Fainelli, bcm-kernel-feedback-list,
	Heiner Kallweit, Russell King, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni

On Wed, Apr 01, 2026 at 12:49:31PM +0100, Daniel Wagner wrote:
> Expose LED1 and LED2 pins via the PHY LED framework. Each pin has a
> source mask (MASK_LOW + MASK_EXT registers) selecting which hardware
> events light it, plus a CTL field in the shared 0xA83B register
> (RMW; LED4 is firmware-controlled per the datasheet).
> 
> Hardware can offload per-speed link triggers (1000/2500/5000/10000),
> RX/TX activity, and force-on. LINK_100 is accepted only alongside
> LINK_1000: source bit 4 lights at both speeds and 100-alone isn't
> representable, so the unrepresentable case falls to software.
> 
> The chip has five LED pins; only LED1/LED2 are exposed here as those
> are the only ones characterized on tested hardware. LED4 is firmware-
> controlled regardless of strap configuration.
> 
> Tested on TRENDnet TEG-S750 (LED1/LED2 wired to an antiparallel
> bicolor LED): brightness_set via sysfs; netdev trigger offloaded=1
> with amber lit at 100M/1G/2.5G and green lit at 10G via respective
> link_* modes; LED off immediately on cable unplug with no software
> involvement.
> 
> Signed-off-by: Daniel Wagner <wagner.daniel.t@gmail.com>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892
  2026-04-01 11:49 [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892 Daniel Wagner
  2026-04-02  1:23 ` Andrew Lunn
@ 2026-04-03  1:10 ` patchwork-bot+netdevbpf
  1 sibling, 0 replies; 3+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-04-03  1:10 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: netdev, florian.fainelli, bcm-kernel-feedback-list, andrew,
	hkallweit1, linux, davem, edumazet, kuba, pabeni

Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Wed,  1 Apr 2026 12:49:31 +0100 you wrote:
> Expose LED1 and LED2 pins via the PHY LED framework. Each pin has a
> source mask (MASK_LOW + MASK_EXT registers) selecting which hardware
> events light it, plus a CTL field in the shared 0xA83B register
> (RMW; LED4 is firmware-controlled per the datasheet).
> 
> Hardware can offload per-speed link triggers (1000/2500/5000/10000),
> RX/TX activity, and force-on. LINK_100 is accepted only alongside
> LINK_1000: source bit 4 lights at both speeds and 100-alone isn't
> representable, so the unrepresentable case falls to software.
> 
> [...]

Here is the summary with links:
  - [net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892
    https://git.kernel.org/netdev/net-next/c/7eaff1eff003

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-04-03  1:10 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-01 11:49 [PATCH net-next] net: phy: bcm84881: add LED framework support for BCM84891/BCM84892 Daniel Wagner
2026-04-02  1:23 ` Andrew Lunn
2026-04-03  1:10 ` patchwork-bot+netdevbpf

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox