lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20251106200309.1096131-1-prabhakar.mahadev-lad.rj@bp.renesas.com>
Date: Thu,  6 Nov 2025 20:03:09 +0000
From: Prabhakar <prabhakar.csengg@...il.com>
To: Andrew Lunn <andrew@...n.ch>,
	Heiner Kallweit <hkallweit1@...il.com>,
	Russell King <linux@...linux.org.uk>,
	"David S. Miller" <davem@...emloft.net>,
	Eric Dumazet <edumazet@...gle.com>,
	Jakub Kicinski <kuba@...nel.org>,
	Paolo Abeni <pabeni@...hat.com>,
	Horatiu Vultur <horatiu.vultur@...rochip.com>,
	Geert Uytterhoeven <geert+renesas@...der.be>,
	Vladimir Oltean <vladimir.oltean@....com>,
	Vadim Fedorenko <vadim.fedorenko@...ux.dev>,
	Maxime Chevallier <maxime.chevallier@...tlin.com>
Cc: netdev@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	linux-renesas-soc@...r.kernel.org,
	Prabhakar <prabhakar.csengg@...il.com>,
	Biju Das <biju.das.jz@...renesas.com>,
	Fabrizio Castro <fabrizio.castro.jz@...esas.com>,
	Lad Prabhakar <prabhakar.mahadev-lad.rj@...renesas.com>
Subject: [PATCH net-next] net: phy: mscc: Add support for PHY LEDs on VSC8541

From: Lad Prabhakar <prabhakar.mahadev-lad.rj@...renesas.com>

Add a minimal LED controller implementation supporting common use cases
with the 'netdev' trigger.

The driver now defaults to VSC8531_LINK_ACTIVITY at initialization and
allows users to configure LED behavior through the LED subsystem. Support
for controlling LED behavior is also added.

The LED Behavior (register 30) bits [0:1] control the combine feature:
0: Combine enabled (link/activity, duplex/collision)
1: Combine disabled (link only, duplex only)

This feature is now managed based on the RX/TX rules. If both RX and TX
are disabled, the combine feature is turned off; otherwise, it remains
enabled.

Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@...renesas.com>
---
 drivers/net/phy/mscc/mscc.h      |   4 +
 drivers/net/phy/mscc/mscc_main.c | 223 ++++++++++++++++++++++++++++++-
 2 files changed, 222 insertions(+), 5 deletions(-)

diff --git a/drivers/net/phy/mscc/mscc.h b/drivers/net/phy/mscc/mscc.h
index 2eef5956b9cc..65c9d7bd9315 100644
--- a/drivers/net/phy/mscc/mscc.h
+++ b/drivers/net/phy/mscc/mscc.h
@@ -85,6 +85,10 @@ enum rgmii_clock_delay {
 #define LED_MODE_SEL_MASK(x)		  (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
 #define LED_MODE_SEL(x, mode)		  (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
 
+#define MSCC_PHY_LED_BEHAVIOR		  30
+#define LED_COMBINE_DIS_MASK(x)		  BIT(x)
+#define LED_COMBINE_DIS(x, dis)		  (((dis) ? 1 : 0) << (x))
+
 #define MSCC_EXT_PAGE_CSR_CNTL_17	  17
 #define MSCC_EXT_PAGE_CSR_CNTL_18	  18
 
diff --git a/drivers/net/phy/mscc/mscc_main.c b/drivers/net/phy/mscc/mscc_main.c
index 8678ebf89cca..0c4e368527b5 100644
--- a/drivers/net/phy/mscc/mscc_main.c
+++ b/drivers/net/phy/mscc/mscc_main.c
@@ -173,23 +173,43 @@ static void vsc85xx_get_stats(struct phy_device *phydev,
 		data[i] = vsc85xx_get_stat(phydev, i);
 }
 
-static int vsc85xx_led_cntl_set(struct phy_device *phydev,
-				u8 led_num,
-				u8 mode)
+static int vsc85xx_led_cntl_set_lock_unlock(struct phy_device *phydev,
+					    u8 led_num,
+					    u8 mode, bool lock)
 {
 	int rc;
 	u16 reg_val;
 
-	mutex_lock(&phydev->lock);
+	if (lock)
+		mutex_lock(&phydev->lock);
 	reg_val = phy_read(phydev, MSCC_PHY_LED_MODE_SEL);
 	reg_val &= ~LED_MODE_SEL_MASK(led_num);
 	reg_val |= LED_MODE_SEL(led_num, (u16)mode);
 	rc = phy_write(phydev, MSCC_PHY_LED_MODE_SEL, reg_val);
-	mutex_unlock(&phydev->lock);
+	if (lock)
+		mutex_unlock(&phydev->lock);
 
 	return rc;
 }
 
+static int vsc85xx_led_cntl_set(struct phy_device *phydev, u8 led_num,
+				u8 mode)
+{
+	return vsc85xx_led_cntl_set_lock_unlock(phydev, led_num, mode, true);
+}
+
+static int vsc8541_led_combine_disable_set(struct phy_device *phydev, u8 led_num,
+					   bool combine_disable)
+{
+	u16 reg_val;
+
+	reg_val = phy_read(phydev, MSCC_PHY_LED_BEHAVIOR);
+	reg_val &= ~LED_COMBINE_DIS_MASK(led_num);
+	reg_val |= LED_COMBINE_DIS(led_num, combine_disable);
+
+	return phy_write(phydev, MSCC_PHY_LED_BEHAVIOR, reg_val);
+}
+
 static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix)
 {
 	u16 reg_val;
@@ -2218,6 +2238,174 @@ static int vsc85xx_config_inband(struct phy_device *phydev, unsigned int modes)
 				reg_val);
 }
 
+static int vsc8541_led_brightness_set(struct phy_device *phydev,
+				      u8 index, enum led_brightness value)
+{
+	struct vsc8531_private *vsc8531 = phydev->priv;
+
+	if (index >= vsc8531->nleds)
+		return -EINVAL;
+
+	return vsc85xx_led_cntl_set_lock_unlock(phydev, index, value == LED_OFF ?
+				    VSC8531_FORCE_LED_OFF : VSC8531_FORCE_LED_ON, false);
+}
+
+static int vsc8541_led_hw_is_supported(struct phy_device *phydev, u8 index,
+				       unsigned long rules)
+{
+	struct vsc8531_private *vsc8531 = phydev->priv;
+	static const unsigned long supported = BIT(TRIGGER_NETDEV_LINK) |
+					       BIT(TRIGGER_NETDEV_LINK_1000) |
+					       BIT(TRIGGER_NETDEV_LINK_100) |
+					       BIT(TRIGGER_NETDEV_LINK_10) |
+					       BIT(TRIGGER_NETDEV_RX) |
+					       BIT(TRIGGER_NETDEV_TX);
+
+	if (index >= vsc8531->nleds)
+		return -EINVAL;
+
+	if (rules & ~supported)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
+static int vsc8541_led_hw_control_get(struct phy_device *phydev, u8 index,
+				      unsigned long *rules)
+{
+	struct vsc8531_private *vsc8531 = phydev->priv;
+	u16 reg;
+
+	if (index >= vsc8531->nleds)
+		return -EINVAL;
+
+	reg = phy_read(phydev, MSCC_PHY_LED_MODE_SEL) & LED_MODE_SEL_MASK(index);
+	reg >>= LED_MODE_SEL_POS(index);
+	switch (reg) {
+	case VSC8531_LINK_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_1000_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_1000) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_100_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_100) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_10_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_10) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_100_1000_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_100) |
+			 BIT(TRIGGER_NETDEV_LINK_1000) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_10_1000_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_10) |
+			 BIT(TRIGGER_NETDEV_LINK_1000) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_LINK_10_100_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_LINK_10) |
+			 BIT(TRIGGER_NETDEV_LINK_100) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	case VSC8531_ACTIVITY:
+		*rules = BIT(TRIGGER_NETDEV_LINK) |
+			 BIT(TRIGGER_NETDEV_RX) |
+			 BIT(TRIGGER_NETDEV_TX);
+		break;
+
+	default:
+		*rules = 0;
+		break;
+	}
+
+	return 0;
+}
+
+static int vsc8541_led_hw_control_set(struct phy_device *phydev, u8 index,
+				      unsigned long rules)
+{
+	struct vsc8531_private *vsc8531 = phydev->priv;
+	bool combine_disable = false;
+	u16 mode = VSC8531_LINK_ACTIVITY;
+	bool has_rx, has_tx;
+	int ret;
+
+	if (index >= vsc8531->nleds)
+		return -EINVAL;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK))
+		mode = VSC8531_LINK_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_10))
+		mode = VSC8531_LINK_10_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_100))
+		mode = VSC8531_LINK_100_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_1000))
+		mode = VSC8531_LINK_1000_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_100) &&
+	    rules & BIT(TRIGGER_NETDEV_LINK_1000))
+		mode = VSC8531_LINK_100_1000_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_10) &&
+	    rules & BIT(TRIGGER_NETDEV_LINK_1000))
+		mode = VSC8531_LINK_10_1000_ACTIVITY;
+
+	if (rules & BIT(TRIGGER_NETDEV_LINK_10) &&
+	    rules & BIT(TRIGGER_NETDEV_LINK_100))
+		mode = VSC8531_LINK_10_100_ACTIVITY;
+
+	/*
+	 * The VSC8541 PHY provides an option to control LED behavior. By
+	 * default, the LEDx combine function is enabled, meaning the LED
+	 * will be on when there is link/activity or duplex/collision. If
+	 * the combine function is disabled, the LED will be on only for
+	 * link or duplex.
+	 *
+	 * To control this behavior, we check the selected rules. If both
+	 * RX and TX activity are not selected, the LED combine function
+	 * is disabled; otherwise, it remains enabled.
+	 */
+	has_rx = !!(rules & BIT(TRIGGER_NETDEV_RX));
+	has_tx = !!(rules & BIT(TRIGGER_NETDEV_TX));
+	if (!has_rx && !has_tx)
+		combine_disable = true;
+
+	ret = vsc8541_led_combine_disable_set(phydev, index, combine_disable);
+	if (ret < 0)
+		return ret;
+
+	return vsc85xx_led_cntl_set_lock_unlock(phydev, index, mode, false);
+}
+
 static int vsc8514_probe(struct phy_device *phydev)
 {
 	struct vsc8531_private *vsc8531;
@@ -2322,6 +2510,7 @@ static int vsc85xx_probe(struct phy_device *phydev)
 	int rate_magic;
 	u32 default_mode[2] = {VSC8531_LINK_1000_ACTIVITY,
 	   VSC8531_LINK_100_ACTIVITY};
+	int phy_id;
 
 	rate_magic = vsc85xx_edge_rate_magic_get(phydev);
 	if (rate_magic < 0)
@@ -2343,6 +2532,26 @@ static int vsc85xx_probe(struct phy_device *phydev)
 	if (!vsc8531->stats)
 		return -ENOMEM;
 
+	phy_id = phydev->drv->phy_id & phydev->drv->phy_id_mask;
+	if (phy_id == PHY_ID_VSC8541) {
+		struct device_node *np;
+
+		/*
+		 * Check for LED configuration in device tree if available
+		 * or fall back to default `vsc8531,led-x-mode` DT properties.
+		 */
+		np = of_get_child_by_name(phydev->mdio.dev.of_node, "leds");
+		if (np) {
+			of_node_put(np);
+
+			/* default to link activity */
+			for (unsigned int i = 0; i < vsc8531->nleds; i++)
+				vsc8531->leds_mode[i] = VSC8531_LINK_ACTIVITY;
+
+			return 0;
+		}
+	}
+
 	return vsc85xx_dt_led_modes_get(phydev, default_mode);
 }
 
@@ -2548,6 +2757,10 @@ static struct phy_driver vsc85xx_driver[] = {
 	.get_sset_count = &vsc85xx_get_sset_count,
 	.get_strings    = &vsc85xx_get_strings,
 	.get_stats      = &vsc85xx_get_stats,
+	.led_brightness_set = vsc8541_led_brightness_set,
+	.led_hw_is_supported = vsc8541_led_hw_is_supported,
+	.led_hw_control_get = vsc8541_led_hw_control_get,
+	.led_hw_control_set = vsc8541_led_hw_control_set,
 },
 {
 	.phy_id		= PHY_ID_VSC8552,
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ