// SPDX-License-Identifier: GPL-2.0 /* LED driver for Aquantia PHY * * Author: Daniel Golle */ #include #include "aquantia.h" int aqr_phy_led_brightness_set(struct phy_device *phydev, u8 index, enum led_brightness value) { if (index >= AQR_MAX_LEDS) return -EINVAL; return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index), VEND1_GLOBAL_LED_PROV_LINK_MASK | VEND1_GLOBAL_LED_PROV_FORCE_ON | VEND1_GLOBAL_LED_PROV_RX_ACT | VEND1_GLOBAL_LED_PROV_TX_ACT, value ? VEND1_GLOBAL_LED_PROV_FORCE_ON : 0); } static const unsigned long 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)); int aqr_phy_led_hw_is_supported(struct phy_device *phydev, u8 index, unsigned long rules) { if (index >= AQR_MAX_LEDS) return -EINVAL; /* All combinations of the supported triggers are allowed */ if (rules & ~supported_triggers) return -EOPNOTSUPP; return 0; } int aqr_phy_led_hw_control_get(struct phy_device *phydev, u8 index, unsigned long *rules) { int val; if (index >= AQR_MAX_LEDS) return -EINVAL; val = phy_read_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index)); if (val < 0) return val; *rules = 0; if (val & VEND1_GLOBAL_LED_PROV_LINK100) *rules |= BIT(TRIGGER_NETDEV_LINK_100); if (val & VEND1_GLOBAL_LED_PROV_LINK1000) *rules |= BIT(TRIGGER_NETDEV_LINK_1000); if (val & VEND1_GLOBAL_LED_PROV_LINK2500) *rules |= BIT(TRIGGER_NETDEV_LINK_2500); if (val & VEND1_GLOBAL_LED_PROV_LINK5000) *rules |= BIT(TRIGGER_NETDEV_LINK_5000); if (val & VEND1_GLOBAL_LED_PROV_LINK10000) *rules |= BIT(TRIGGER_NETDEV_LINK_10000); if (val & VEND1_GLOBAL_LED_PROV_RX_ACT) *rules |= BIT(TRIGGER_NETDEV_RX); if (val & VEND1_GLOBAL_LED_PROV_TX_ACT) *rules |= BIT(TRIGGER_NETDEV_TX); return 0; } int aqr_phy_led_hw_control_set(struct phy_device *phydev, u8 index, unsigned long rules) { u16 val = 0; if (index >= AQR_MAX_LEDS) return -EINVAL; if (rules & (BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK))) val |= VEND1_GLOBAL_LED_PROV_LINK100; if (rules & (BIT(TRIGGER_NETDEV_LINK_1000) | BIT(TRIGGER_NETDEV_LINK))) val |= VEND1_GLOBAL_LED_PROV_LINK1000; if (rules & (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK))) val |= VEND1_GLOBAL_LED_PROV_LINK2500; if (rules & (BIT(TRIGGER_NETDEV_LINK_5000) | BIT(TRIGGER_NETDEV_LINK))) val |= VEND1_GLOBAL_LED_PROV_LINK5000; if (rules & (BIT(TRIGGER_NETDEV_LINK_10000) | BIT(TRIGGER_NETDEV_LINK))) val |= VEND1_GLOBAL_LED_PROV_LINK10000; if (rules & BIT(TRIGGER_NETDEV_RX)) val |= VEND1_GLOBAL_LED_PROV_RX_ACT; if (rules & BIT(TRIGGER_NETDEV_TX)) val |= VEND1_GLOBAL_LED_PROV_TX_ACT; return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_PROV(index), VEND1_GLOBAL_LED_PROV_LINK_MASK | VEND1_GLOBAL_LED_PROV_FORCE_ON | VEND1_GLOBAL_LED_PROV_RX_ACT | VEND1_GLOBAL_LED_PROV_TX_ACT, val); } int aqr_phy_led_active_low_set(struct phy_device *phydev, int index, bool enable) { return phy_modify_mmd(phydev, MDIO_MMD_VEND1, AQR_LED_DRIVE(index), VEND1_GLOBAL_LED_DRIVE_VDD, enable); } int aqr_phy_led_polarity_set(struct phy_device *phydev, int index, unsigned long modes) { struct aqr107_priv *priv = phydev->priv; bool active_low = false; u32 mode; if (index >= AQR_MAX_LEDS) return -EINVAL; for_each_set_bit(mode, &modes, __PHY_LED_MODES_NUM) { switch (mode) { case PHY_LED_ACTIVE_LOW: active_low = true; break; default: return -EINVAL; } } /* Save LED driver vdd state to restore on SW reset */ if (active_low) priv->leds_active_low |= BIT(index); return aqr_phy_led_active_low_set(phydev, index, active_low); }