Subject: [net-next PATCH v4 2/4] net: phy: add support for PHY LEDs polarity modes

From: Christian Marangi <[email-masked]>
Date: Fri, 15 Dec 2023 22:22:42 +0100

Add support for PHY LEDs polarity modes. Some device might require
special polarity mode for the LED to correctly work and those mode
doesn't reflect what the PHY sets by default.

An example is a PHY device that set LED to active high but the attached
LEDs require to be active low to correctly work (and turn on when
actually requested)

PHY driver needs to declare .led_polarity_set() to configure LED
polarity. Index of the LED is passed and the polarity mode in the enum.

If a polarity is not set in DT and .led_polarity_set() is declared,
PHY_LED_POLARITY_DEFAULT is passed as polarity mode to let the PHY
driver decide a default polarity mode for the attached LEDs.

This is needed for PHY that sets active high on reset and the common
configuration is LEDs with active low polarity.

Signed-off-by: Christian Marangi <[email-masked]>
---
Changes v4:
- Drop for global active-low
- Rework to polarity option (for marvell10g series support)
Changes v3:
- Out of RFC
Changes v2:
- Add this patch

 drivers/net/phy/phy_device.c | 45 ++++++++++++++++++++++++++++++++++++
 include/linux/phy.h          | 25 ++++++++++++++++++++
 2 files changed, 70 insertions(+)

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index d8e9335d415c..b35b7a8717cc 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -43,6 +43,13 @@ MODULE_DESCRIPTION("PHY library");
 MODULE_AUTHOR("Andy Fleming");
 MODULE_LICENSE("GPL");
 
+static const char * const phy_led_polarity_mode_strings[] = {
+	[PHY_LED_POLARITY_ACTIVE_LOW] = "active-low",
+	[PHY_LED_POLARITY_ACTIVE_HIGH] = "active-high",
+	[PHY_LED_POLARITY_ACTIVE_LOW_TRISTATED] = "active-low-tristated",
+	[PHY_LED_POLARITY_ACTIVE_HIGH_TRISTATED] = "active-low-tristated",
+};
+
 __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_basic_features) __ro_after_init;
 EXPORT_SYMBOL_GPL(phy_basic_features);
 
@@ -3086,6 +3093,40 @@ static void phy_leds_unregister(struct phy_device *phydev)
 	}
 }
 
+static int of_phy_set_led_polarity(struct phy_device *phydev,
+				   struct device_node *led, u32 index)
+{
+	const char *polarity_str;
+	int i, err;
+
+	err = of_property_read_string(led, "polarity", &polarity_str);
+	if (err) {
+		if (err != -EINVAL)
+			return err;
+
+		/* Nothing to do, polarity setting not supported */
+		if (!phydev->drv->led_polarity_set)
+			return 0;
+
+		/* Apply default polarity if supported */
+		return phydev->drv->led_polarity_set(phydev, index,
+						     PHY_LED_POLARITY_DEFAULT);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(phy_led_polarity_mode_strings); i++)
+		if (!strcmp(phy_led_polarity_mode_strings[i], polarity_str)) {
+			if (!phydev->drv->led_polarity_set) {
+				phydev_warn(phydev, "Ignoring LED polarity in DT. Setting polarity not supported\n");
+				return 0;
+			}
+
+			return phydev->drv->led_polarity_set(phydev, index, i);
+		}
+
+	/* Unknown polarity mode declared */
+	return -EINVAL;
+}
+
 static int of_phy_led(struct phy_device *phydev,
 		      struct device_node *led)
 {
@@ -3109,6 +3150,10 @@ static int of_phy_led(struct phy_device *phydev,
 	if (index > U8_MAX)
 		return -EINVAL;
 
+	err = of_phy_set_led_polarity(phydev, led, index);
+	if (err)
+		return err;
+
 	phyled->index = index;
 	if (phydev->drv->led_brightness_set)
 		cdev->brightness_set_blocking = phy_led_set_brightness;
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 6e7ebcc50b85..88ff4195bc4f 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -852,6 +852,16 @@ struct phy_plca_status {
 	bool pst;
 };
 
+enum phy_led_polarity_modes {
+	PHY_LED_POLARITY_ACTIVE_LOW,
+	PHY_LED_POLARITY_ACTIVE_HIGH,
+	PHY_LED_POLARITY_ACTIVE_LOW_TRISTATED,
+	PHY_LED_POLARITY_ACTIVE_HIGH_TRISTATED,
+
+	/* PHY driver apply a default value */
+	PHY_LED_POLARITY_DEFAULT,
+};
+
 /**
  * struct phy_led: An LED driven by the PHY
  *
@@ -1145,6 +1155,21 @@ struct phy_driver {
 	int (*led_hw_control_get)(struct phy_device *dev, u8 index,
 				  unsigned long *rules);
 
+	/**
+	 * @led_polarity_set: Set the LED polarity mode
+	 * @dev: PHY device which has the LED
+	 * @index: Which LED of the PHY device
+	 * @polarity_mode: LED polarity mode from enum
+	 *
+	 * Set PHY to requested LED polarity mode.
+	 *
+	 * If polarity mode PHY_LED_POLARITY_DEFAULT is passed,
+	 * PHY driver should apply a default LED polarity mode.
+	 *
+	 * Returns 0, or an error code.
+	 */
+	int (*led_polarity_set)(struct phy_device *dev, int index,
+				enum phy_led_polarity_modes polarity_mode);
 };
 #define to_phy_driver(d) container_of(to_mdio_common_driver(d),		\
 				      struct phy_driver, mdiodrv)
-- 
2.40.1