diff mbox series

net: phy: dp83867: Add support for hardware blinking LEDs

Message ID 20230907084731.2181381-1-s.hauer@pengutronix.de (mailing list archive)
State Deferred
Delegated to: Netdev Maintainers
Headers show
Series net: phy: dp83867: Add support for hardware blinking LEDs | expand

Checks

Context Check Description
netdev/series_format warning Single patches do not need cover letters; Target tree name not specified in the subject
netdev/tree_selection success Guessed tree name to be net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 1330 this patch: 1330
netdev/cc_maintainers warning 4 maintainers not CCed: edumazet@google.com pabeni@redhat.com kuba@kernel.org davem@davemloft.net
netdev/build_clang success Errors and warnings before: 1353 this patch: 1353
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 1353 this patch: 1353
netdev/checkpatch warning WARNING: line length of 81 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 88 exceeds 80 columns WARNING: line length of 95 exceeds 80 columns
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Sascha Hauer Sept. 7, 2023, 8:47 a.m. UTC
This implements the led_hw_* hooks to support hardware blinking LEDs on
the DP83867 phy. The driver supports all LED modes that have a
corresponding TRIGGER_NETDEV_* define. Error and collision do not have
a TRIGGER_NETDEV_* define, so these modes are currently not supported.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
 drivers/net/phy/dp83867.c | 137 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 137 insertions(+)

Comments

Alexander Stein Sept. 8, 2023, 9:23 a.m. UTC | #1
Hi Sascha,

thanks for the patch.

Am Donnerstag, 7. September 2023, 10:47:31 CEST schrieb Sascha Hauer:
> This implements the led_hw_* hooks to support hardware blinking LEDs on
> the DP83867 phy. The driver supports all LED modes that have a
> corresponding TRIGGER_NETDEV_* define. Error and collision do not have
> a TRIGGER_NETDEV_* define, so these modes are currently not supported.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>

This works as intended so far. Unfortunately this driver and the PHY LED 
framework do not support active-low LEDs (yet).

Tested-by: Alexander Stein <alexander.stein@ew.tq-group.com> #TQMa8MxML/MBa8Mx

> ---
>  drivers/net/phy/dp83867.c | 137 ++++++++++++++++++++++++++++++++++++++
>  1 file changed, 137 insertions(+)
> 
> diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
> index e397e7d642d92..5f08f9d38bd7a 100644
> --- a/drivers/net/phy/dp83867.c
> +++ b/drivers/net/phy/dp83867.c
> @@ -159,6 +159,23 @@
>  #define DP83867_LED_DRV_EN(x)	BIT((x) * 4)
>  #define DP83867_LED_DRV_VAL(x)	BIT((x) * 4 + 1)
> 
> +#define DP83867_LED_FN(idx, val)	(((val) & 0xf) << ((idx) * 4))
> +#define DP83867_LED_FN_MASK(idx)	(0xf << ((idx) * 4))
> +#define DP83867_LED_FN_RX_ERR		0xe /* Receive Error */
> +#define DP83867_LED_FN_RX_TX_ERR	0xd /* Receive Error or Transmit 
Error */
> +#define DP83867_LED_FN_LINK_RX_TX	0xb /* Link established, blink for rx 
or
> tx activity */ +#define DP83867_LED_FN_FULL_DUPLEX	0xa /* Full 
duplex */
> +#define DP83867_LED_FN_LINK_100_1000_BT	0x9 /* 100/1000BT link 
established
> */ +#define DP83867_LED_FN_LINK_10_100_BT	0x8 /* 10/100BT link 
established
> */ +#define DP83867_LED_FN_LINK_10_BT	0x7 /* 10BT link established */
> +#define DP83867_LED_FN_LINK_100_BTX	0x6 /* 100 BTX link established */
> +#define DP83867_LED_FN_LINK_1000_BT	0x5 /* 1000 BT link established */
> +#define DP83867_LED_FN_COLLISION	0x4 /* Collision detected */
> +#define DP83867_LED_FN_RX		0x3 /* Receive activity */
> +#define DP83867_LED_FN_TX		0x2 /* Transmit activity */
> +#define DP83867_LED_FN_RX_TX		0x1 /* Receive or Transmit 
activity */
> +#define DP83867_LED_FN_LINK		0x0 /* Link established */
> +
>  enum {
>  	DP83867_PORT_MIRROING_KEEP,
>  	DP83867_PORT_MIRROING_EN,
> @@ -1018,6 +1035,123 @@ dp83867_led_brightness_set(struct phy_device
> *phydev, val);
>  }
> 
> +static int dp83867_led_mode(u8 index, unsigned long rules)
> +{
> +	if (index >= DP83867_LED_COUNT)
> +		return -EINVAL;
> +
> +	switch (rules) {
> +	case BIT(TRIGGER_NETDEV_LINK):
> +		return DP83867_LED_FN_LINK;
> +	case BIT(TRIGGER_NETDEV_LINK_10):
> +		return DP83867_LED_FN_LINK_10_BT;
> +	case BIT(TRIGGER_NETDEV_LINK_100):
> +		return DP83867_LED_FN_LINK_100_BTX;
> +	case BIT(TRIGGER_NETDEV_FULL_DUPLEX):
> +		return DP83867_LED_FN_FULL_DUPLEX;
> +	case BIT(TRIGGER_NETDEV_TX):
> +		return DP83867_LED_FN_TX;
> +	case BIT(TRIGGER_NETDEV_RX):
> +		return DP83867_LED_FN_RX;
> +	case BIT(TRIGGER_NETDEV_LINK_1000):
> +		return DP83867_LED_FN_LINK_1000_BT;
> +	case BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX):
> +		return DP83867_LED_FN_RX_TX;
> +	case BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000):
> +		return DP83867_LED_FN_LINK_100_1000_BT;
> +	case BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100):
> +		return DP83867_LED_FN_LINK_10_100_BT;
> +	case BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) |
> BIT(TRIGGER_NETDEV_RX): +		return DP83867_LED_FN_LINK_RX_TX;
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +static int dp83867_led_hw_is_supported(struct phy_device *phydev, u8 index,
> +				       unsigned long rules)
> +{
> +	int ret;
> +
> +	ret = dp83867_led_mode(index, rules);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int dp83867_led_hw_control_set(struct phy_device *phydev, u8 index,
> +				      unsigned long rules)
> +{
> +	int mode, ret;
> +
> +	mode = dp83867_led_mode(index, rules);
> +	if (mode < 0)
> +		return mode;
> +
> +	ret = phy_modify(phydev, DP83867_LEDCR1, DP83867_LED_FN_MASK(index),
> +			 DP83867_LED_FN(index, mode));
> +	if (ret)
> +		return ret;
> +
> +	return phy_modify(phydev, DP83867_LEDCR2, DP83867_LED_DRV_EN(index), 
0);
> +}
> +
> +static int dp83867_led_hw_control_get(struct phy_device *phydev, u8 index,
> +				      unsigned long *rules)
> +{
> +	int val;
> +
> +	val = phy_read(phydev, DP83867_LEDCR1);
> +	if (val < 0)
> +		return val;
> +
> +	val &= DP83867_LED_FN_MASK(index);
> +	val >>= index * 4;
> +
> +	switch (val) {
> +	case DP83867_LED_FN_LINK:
> +		*rules = BIT(TRIGGER_NETDEV_LINK);
> +		break;
> +	case DP83867_LED_FN_LINK_10_BT:
> +		*rules = BIT(TRIGGER_NETDEV_LINK_10);
> +		break;
> +	case DP83867_LED_FN_LINK_100_BTX:
> +		*rules = BIT(TRIGGER_NETDEV_LINK_100);
> +		break;
> +	case DP83867_LED_FN_FULL_DUPLEX:
> +		*rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX);
> +		break;
> +	case DP83867_LED_FN_TX:
> +		*rules = BIT(TRIGGER_NETDEV_TX);
> +		break;
> +	case DP83867_LED_FN_RX:
> +		*rules = BIT(TRIGGER_NETDEV_RX);
> +		break;
> +	case DP83867_LED_FN_LINK_1000_BT:
> +		*rules = BIT(TRIGGER_NETDEV_LINK_1000);
> +		break;
> +	case DP83867_LED_FN_RX_TX:
> +		*rules = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
> +		break;
> +	case DP83867_LED_FN_LINK_100_1000_BT:
> +		*rules = BIT(TRIGGER_NETDEV_LINK_100) | 
BIT(TRIGGER_NETDEV_LINK_1000);
> +		break;
> +	case DP83867_LED_FN_LINK_10_100_BT:
> +		*rules = BIT(TRIGGER_NETDEV_LINK_10) | 
BIT(TRIGGER_NETDEV_LINK_100);
> +		break;
> +	case DP83867_LED_FN_LINK_RX_TX:
> +		*rules = BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) 
|
> +			 BIT(TRIGGER_NETDEV_RX);
> +		break;
> +	default:
> +		*rules = 0;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
>  static struct phy_driver dp83867_driver[] = {
>  	{
>  		.phy_id		= DP83867_PHY_ID,
> @@ -1047,6 +1181,9 @@ static struct phy_driver dp83867_driver[] = {
>  		.set_loopback	= dp83867_loopback,
> 
>  		.led_brightness_set = dp83867_led_brightness_set,
> +		.led_hw_is_supported = dp83867_led_hw_is_supported,
> +		.led_hw_control_set = dp83867_led_hw_control_set,
> +		.led_hw_control_get = dp83867_led_hw_control_get,
>  	},
>  };
>  module_phy_driver(dp83867_driver);
Andrew Lunn Sept. 9, 2023, 2:27 a.m. UTC | #2
> This works as intended so far. Unfortunately this driver and the PHY LED 
> framework do not support active-low LEDs (yet).

Polarity is something which i've seen a few PHY devices have. It also
seems like a core LED concept, not something specific to PHY LEDs. So
i think this needs to be partially addressed in the LED core.

  Andrew
Andrew Lunn Sept. 9, 2023, 2:30 a.m. UTC | #3
On Thu, Sep 07, 2023 at 10:47:31AM +0200, Sascha Hauer wrote:
> This implements the led_hw_* hooks to support hardware blinking LEDs on
> the DP83867 phy. The driver supports all LED modes that have a
> corresponding TRIGGER_NETDEV_* define. Error and collision do not have
> a TRIGGER_NETDEV_* define, so these modes are currently not supported.
> 
> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>

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

    Andrew
Russell King (Oracle) Sept. 9, 2023, 7:15 a.m. UTC | #4
On Sat, Sep 09, 2023 at 04:27:31AM +0200, Andrew Lunn wrote:
> > This works as intended so far. Unfortunately this driver and the PHY LED 
> > framework do not support active-low LEDs (yet).
> 
> Polarity is something which i've seen a few PHY devices have. It also
> seems like a core LED concept, not something specific to PHY LEDs. So
> i think this needs to be partially addressed in the LED core.

However, doesn't the LED layer deals with LED brightness, not by logic
state? It certainly looks that way, and it's left up to the drivers
themselves to deal with any polarity inversion - which makes sense if
the core is just concerned about brightness.

Introducing inversion in the core means drivers will be passed a
brightness of "100" for off and "0" for on which, do you not think,
starts to get rather silly?
Andrew Lunn Sept. 9, 2023, 1:20 p.m. UTC | #5
On Sat, Sep 09, 2023 at 08:15:37AM +0100, Russell King (Oracle) wrote:
> On Sat, Sep 09, 2023 at 04:27:31AM +0200, Andrew Lunn wrote:
> > > This works as intended so far. Unfortunately this driver and the PHY LED 
> > > framework do not support active-low LEDs (yet).
> > 
> > Polarity is something which i've seen a few PHY devices have. It also
> > seems like a core LED concept, not something specific to PHY LEDs. So
> > i think this needs to be partially addressed in the LED core.
> 
> However, doesn't the LED layer deals with LED brightness, not by logic
> state? It certainly looks that way, and it's left up to the drivers
> themselves to deal with any polarity inversion - which makes sense if
> the core is just concerned about brightness.
> 
> Introducing inversion in the core means drivers will be passed a
> brightness of "100" for off and "0" for on which, do you not think,
> starts to get rather silly?

Documentation/devicetree/bindings/leds/leds-pwm.yaml has:

      active-low:
        description:
          For PWMs where the LED is wired to supply rather than ground.
        type: boolean

leds-pwm-multicolor.yaml and leds-bcm6358.txt uses the same
property. There is also one case of: irled/ir-spi-led.yaml

  led-active-low:
    type: boolean
    description:
      Output is negated with a NOT gate.

The implementation of the properties is repeated in each driver. So we
probably need to add the parsing of this property in phy_device.c, and
add a led_config() op to struct phy_device to pass polarity
information to the driver.

	Andrew
Jakub Kicinski Oct. 3, 2023, 1:46 p.m. UTC | #6
On Thu,  7 Sep 2023 10:47:31 +0200 Sascha Hauer wrote:
> This implements the led_hw_* hooks to support hardware blinking LEDs on
> the DP83867 phy. The driver supports all LED modes that have a
> corresponding TRIGGER_NETDEV_* define. Error and collision do not have
> a TRIGGER_NETDEV_* define, so these modes are currently not supported.

FWIW I think this patch got marked as Deferred in patchwork because it
was posted mid-merge window. Could you repost?
diff mbox series

Patch

diff --git a/drivers/net/phy/dp83867.c b/drivers/net/phy/dp83867.c
index e397e7d642d92..5f08f9d38bd7a 100644
--- a/drivers/net/phy/dp83867.c
+++ b/drivers/net/phy/dp83867.c
@@ -159,6 +159,23 @@ 
 #define DP83867_LED_DRV_EN(x)	BIT((x) * 4)
 #define DP83867_LED_DRV_VAL(x)	BIT((x) * 4 + 1)
 
+#define DP83867_LED_FN(idx, val)	(((val) & 0xf) << ((idx) * 4))
+#define DP83867_LED_FN_MASK(idx)	(0xf << ((idx) * 4))
+#define DP83867_LED_FN_RX_ERR		0xe /* Receive Error */
+#define DP83867_LED_FN_RX_TX_ERR	0xd /* Receive Error or Transmit Error */
+#define DP83867_LED_FN_LINK_RX_TX	0xb /* Link established, blink for rx or tx activity */
+#define DP83867_LED_FN_FULL_DUPLEX	0xa /* Full duplex */
+#define DP83867_LED_FN_LINK_100_1000_BT	0x9 /* 100/1000BT link established */
+#define DP83867_LED_FN_LINK_10_100_BT	0x8 /* 10/100BT link established */
+#define DP83867_LED_FN_LINK_10_BT	0x7 /* 10BT link established */
+#define DP83867_LED_FN_LINK_100_BTX	0x6 /* 100 BTX link established */
+#define DP83867_LED_FN_LINK_1000_BT	0x5 /* 1000 BT link established */
+#define DP83867_LED_FN_COLLISION	0x4 /* Collision detected */
+#define DP83867_LED_FN_RX		0x3 /* Receive activity */
+#define DP83867_LED_FN_TX		0x2 /* Transmit activity */
+#define DP83867_LED_FN_RX_TX		0x1 /* Receive or Transmit activity */
+#define DP83867_LED_FN_LINK		0x0 /* Link established */
+
 enum {
 	DP83867_PORT_MIRROING_KEEP,
 	DP83867_PORT_MIRROING_EN,
@@ -1018,6 +1035,123 @@  dp83867_led_brightness_set(struct phy_device *phydev,
 			  val);
 }
 
+static int dp83867_led_mode(u8 index, unsigned long rules)
+{
+	if (index >= DP83867_LED_COUNT)
+		return -EINVAL;
+
+	switch (rules) {
+	case BIT(TRIGGER_NETDEV_LINK):
+		return DP83867_LED_FN_LINK;
+	case BIT(TRIGGER_NETDEV_LINK_10):
+		return DP83867_LED_FN_LINK_10_BT;
+	case BIT(TRIGGER_NETDEV_LINK_100):
+		return DP83867_LED_FN_LINK_100_BTX;
+	case BIT(TRIGGER_NETDEV_FULL_DUPLEX):
+		return DP83867_LED_FN_FULL_DUPLEX;
+	case BIT(TRIGGER_NETDEV_TX):
+		return DP83867_LED_FN_TX;
+	case BIT(TRIGGER_NETDEV_RX):
+		return DP83867_LED_FN_RX;
+	case BIT(TRIGGER_NETDEV_LINK_1000):
+		return DP83867_LED_FN_LINK_1000_BT;
+	case BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX):
+		return DP83867_LED_FN_RX_TX;
+	case BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000):
+		return DP83867_LED_FN_LINK_100_1000_BT;
+	case BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100):
+		return DP83867_LED_FN_LINK_10_100_BT;
+	case BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX):
+		return DP83867_LED_FN_LINK_RX_TX;
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+static int dp83867_led_hw_is_supported(struct phy_device *phydev, u8 index,
+				       unsigned long rules)
+{
+	int ret;
+
+	ret = dp83867_led_mode(index, rules);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int dp83867_led_hw_control_set(struct phy_device *phydev, u8 index,
+				      unsigned long rules)
+{
+	int mode, ret;
+
+	mode = dp83867_led_mode(index, rules);
+	if (mode < 0)
+		return mode;
+
+	ret = phy_modify(phydev, DP83867_LEDCR1, DP83867_LED_FN_MASK(index),
+			 DP83867_LED_FN(index, mode));
+	if (ret)
+		return ret;
+
+	return phy_modify(phydev, DP83867_LEDCR2, DP83867_LED_DRV_EN(index), 0);
+}
+
+static int dp83867_led_hw_control_get(struct phy_device *phydev, u8 index,
+				      unsigned long *rules)
+{
+	int val;
+
+	val = phy_read(phydev, DP83867_LEDCR1);
+	if (val < 0)
+		return val;
+
+	val &= DP83867_LED_FN_MASK(index);
+	val >>= index * 4;
+
+	switch (val) {
+	case DP83867_LED_FN_LINK:
+		*rules = BIT(TRIGGER_NETDEV_LINK);
+		break;
+	case DP83867_LED_FN_LINK_10_BT:
+		*rules = BIT(TRIGGER_NETDEV_LINK_10);
+		break;
+	case DP83867_LED_FN_LINK_100_BTX:
+		*rules = BIT(TRIGGER_NETDEV_LINK_100);
+		break;
+	case DP83867_LED_FN_FULL_DUPLEX:
+		*rules = BIT(TRIGGER_NETDEV_FULL_DUPLEX);
+		break;
+	case DP83867_LED_FN_TX:
+		*rules = BIT(TRIGGER_NETDEV_TX);
+		break;
+	case DP83867_LED_FN_RX:
+		*rules = BIT(TRIGGER_NETDEV_RX);
+		break;
+	case DP83867_LED_FN_LINK_1000_BT:
+		*rules = BIT(TRIGGER_NETDEV_LINK_1000);
+		break;
+	case DP83867_LED_FN_RX_TX:
+		*rules = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
+		break;
+	case DP83867_LED_FN_LINK_100_1000_BT:
+		*rules = BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_1000);
+		break;
+	case DP83867_LED_FN_LINK_10_100_BT:
+		*rules = BIT(TRIGGER_NETDEV_LINK_10) | BIT(TRIGGER_NETDEV_LINK_100);
+		break;
+	case DP83867_LED_FN_LINK_RX_TX:
+		*rules = BIT(TRIGGER_NETDEV_LINK) | BIT(TRIGGER_NETDEV_TX) |
+			 BIT(TRIGGER_NETDEV_RX);
+		break;
+	default:
+		*rules = 0;
+		break;
+	}
+
+	return 0;
+}
+
 static struct phy_driver dp83867_driver[] = {
 	{
 		.phy_id		= DP83867_PHY_ID,
@@ -1047,6 +1181,9 @@  static struct phy_driver dp83867_driver[] = {
 		.set_loopback	= dp83867_loopback,
 
 		.led_brightness_set = dp83867_led_brightness_set,
+		.led_hw_is_supported = dp83867_led_hw_is_supported,
+		.led_hw_control_set = dp83867_led_hw_control_set,
+		.led_hw_control_get = dp83867_led_hw_control_get,
 	},
 };
 module_phy_driver(dp83867_driver);