Add support for hardware-controlled leds on the dap8211r phy. The chip exposes three led that can be programmed to react to link, speed, duplex, and tx/rx activity. Implement callbacks for led_hw_is_supported(), led_hw_control_set() led_hw_control_get(). Signed-off-by: Artem Shimko --- drivers/net/phy/dap8211r.c | 181 +++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/drivers/net/phy/dap8211r.c b/drivers/net/phy/dap8211r.c index 4eb50c26397f..2e371c231d32 100644 --- a/drivers/net/phy/dap8211r.c +++ b/drivers/net/phy/dap8211r.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -57,6 +58,33 @@ */ #define DAP8211R_DEFAULT_DEL_SEL 0xD +#define DAP8211R_LED_ID_0 0 +#define DAP8211R_LED_ID_1 1 +#define DAP8211R_LED_ID_2 2 +#define DAP8211R_LED_0 0xA00C +#define DAP8211R_LED_1 0xA00D +#define DAP8211R_LED_2 0xA00E +#define DAP8211R_LED_ACT_BLINK BIT(13) +#define DAP8211R_LED_FULL_DUPLEX BIT(12) +#define DAP8211R_LED_HALF_DUPLEX BIT(11) +#define DAP8211R_LED_TX BIT(10) +#define DAP8211R_LED_RX BIT(9) +#define DAP8211R_LED_LINK_1000 BIT(6) +#define DAP8211R_LED_LINK_100 BIT(5) +#define DAP8211R_LED_LINK_10 BIT(4) + +#define DAP8211R_LED_LINK (DAP8211R_LED_LINK_10 | \ + DAP8211R_LED_LINK_100 | \ + DAP8211R_LED_LINK_1000) + +#define DAP8211R_LED_SUPPORTED_TRG (BIT(TRIGGER_NETDEV_LINK_10) | \ + BIT(TRIGGER_NETDEV_LINK_100) | \ + BIT(TRIGGER_NETDEV_LINK_1000) | \ + BIT(TRIGGER_NETDEV_TX) | \ + BIT(TRIGGER_NETDEV_RX) | \ + BIT(TRIGGER_NETDEV_HALF_DUPLEX) | \ + BIT(TRIGGER_NETDEV_FULL_DUPLEX)) + static const int dap8211r_internal_delay[] = {0, 150, 300, 450, 600, 750, 900, 1050, 1200, 1350, 1500, 1650, 1800, 1950, 2100, 2250}; @@ -142,6 +170,156 @@ static int dap8211r_modify_ext(struct phy_device *phydev, u16 reg, u16 mask, u16 return ret; } +/** + * dap8211r_led_hw_is_supported() - Check if the requested LED rules are supported + * @phydev: PHY device structure + * @index: LED index + * @rules: Bitmask of requested netdev trigger rules + * + * Validates the requested combination of rules which can be represented in the + * hardware register. + * + * Return: 0 on success, -EINVAL for an invalid LED index, + * or -EOPNOTSUPP if any requested rule is not supported by the PHY + */ +static int dap8211r_led_hw_is_supported(struct phy_device *phydev, u8 index, unsigned long rules) +{ + switch (index) { + case DAP8211R_LED_ID_0: + fallthrough; + case DAP8211R_LED_ID_1: + fallthrough; + case DAP8211R_LED_ID_2: + break; + default: + return -EINVAL; + } + + return (rules & ~DAP8211R_LED_SUPPORTED_TRG) ? -EOPNOTSUPP : 0; +} + +/** + * dap8211r_led_hw_control_set() - Program the LED control register + * @phydev: PHY device structure + * @index: LED index + * @rules: Bitmask of requested netdev trigger rules + * + * Translates the requested trigger rules into the corresponding bit + * positions of the LED control register and writes the resulting value + * to the PHY. + * + * Note that the ACT_BLINK bit (13) must be set together with TX/RX rules, + * otherwise the LED will not blink on traffic if no other rule (such as + * link or duplex) is active. + * + * Return: 0 on success, -EINVAL for an invalid LED index, or a negative error code + */ +static int dap8211r_led_hw_control_set(struct phy_device *phydev, u8 index, unsigned long rules) +{ + u16 led_mode = 0, led_reg; + + switch (index) { + case DAP8211R_LED_ID_0: + led_reg = DAP8211R_LED_0; + break; + case DAP8211R_LED_ID_1: + led_reg = DAP8211R_LED_1; + break; + case DAP8211R_LED_ID_2: + led_reg = DAP8211R_LED_2; + break; + default: + return -EINVAL; + } + + if (test_bit(TRIGGER_NETDEV_LINK, &rules)) + led_mode |= DAP8211R_LED_LINK; + + if (test_bit(TRIGGER_NETDEV_LINK_10, &rules)) + led_mode |= DAP8211R_LED_LINK_10; + + if (test_bit(TRIGGER_NETDEV_LINK_100, &rules)) + led_mode |= DAP8211R_LED_LINK_100; + + if (test_bit(TRIGGER_NETDEV_LINK_1000, &rules)) + led_mode |= DAP8211R_LED_LINK_1000; + + if (test_bit(TRIGGER_NETDEV_RX, &rules)) + led_mode |= DAP8211R_LED_RX | DAP8211R_LED_ACT_BLINK; + + if (test_bit(TRIGGER_NETDEV_TX, &rules)) + led_mode |= DAP8211R_LED_TX | DAP8211R_LED_ACT_BLINK; + + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &rules)) + led_mode |= DAP8211R_LED_HALF_DUPLEX; + + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &rules)) + led_mode |= DAP8211R_LED_FULL_DUPLEX; + + if (!led_mode) + return -EOPNOTSUPP; + + return dap8211r_write_ext(phydev, led_reg, led_mode); +} + +/** + * dap8211r_led_hw_control_get() - Read the current LED configuration + * @phydev: PHY device structure + * @index: LED index + * @rules: Pointer to a bitmask where the current rules will be stored + * + * Reads the LED control register and translates the hardware bit + * positions back into the corresponding netdev trigger rules bitmask. + * + * Return: 0 on success, -EINVAL for an invalid LED index, or a negative error code + */ +static int dap8211r_led_hw_control_get(struct phy_device *phydev, u8 index, unsigned long *rules) +{ + u16 led_reg; + int led_mode = 0; + + switch (index) { + case DAP8211R_LED_ID_0: + led_reg = DAP8211R_LED_0; + break; + case DAP8211R_LED_ID_1: + led_reg = DAP8211R_LED_1; + break; + case DAP8211R_LED_ID_2: + led_reg = DAP8211R_LED_2; + break; + default: + return -EINVAL; + } + + led_mode = dap8211r_read_ext(phydev, led_reg); + if (led_mode < 0) + return led_mode; + + if (led_mode & DAP8211R_LED_LINK_10) + __set_bit(TRIGGER_NETDEV_LINK_10, rules); + + if (led_mode & DAP8211R_LED_LINK_100) + __set_bit(TRIGGER_NETDEV_LINK_100, rules); + + if (led_mode & DAP8211R_LED_LINK_1000) + __set_bit(TRIGGER_NETDEV_LINK_1000, rules); + + if (led_mode & DAP8211R_LED_RX) + __set_bit(TRIGGER_NETDEV_RX, rules); + + if (led_mode & DAP8211R_LED_TX) + __set_bit(TRIGGER_NETDEV_TX, rules); + + if (led_mode & DAP8211R_LED_HALF_DUPLEX) + __set_bit(TRIGGER_NETDEV_HALF_DUPLEX, rules); + + if (led_mode & DAP8211R_LED_FULL_DUPLEX) + __set_bit(TRIGGER_NETDEV_FULL_DUPLEX, rules); + + return 0; +} + /** * dap8211r_config_init() - Initialize PHY * @phydev: PHY device structure @@ -230,6 +408,9 @@ static struct phy_driver dap8211r_driver[] = { .config_aneg = genphy_config_aneg, .suspend = genphy_suspend, .resume = genphy_resume, + .led_hw_is_supported = dap8211r_led_hw_is_supported, + .led_hw_control_set = dap8211r_led_hw_control_set, + .led_hw_control_get = dap8211r_led_hw_control_get, }, }; module_phy_driver(dap8211r_driver); -- 2.43.0