This patch uses the new API for HW controlled LEDs to add support for probing and control of LEDs connected to an ethernet PHY chip. A PHY driver wishing to utilize this API needs to implement the methods in struct hw_controlled_led_ops and set the member led_ops in struct phy_driver to point to that structure. Signed-off-by: Marek Behún <marek.behun@xxxxxx> --- drivers/net/phy/phy_device.c | 103 +++++++++++++++++++++++++++++++++++ include/linux/phy.h | 4 ++ 2 files changed, 107 insertions(+) diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 38f56d39f1229..54d5c88e4d4b2 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -2820,6 +2820,103 @@ static bool phy_drv_supports_irq(struct phy_driver *phydrv) return phydrv->config_intr && phydrv->ack_interrupt; } +#if IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) + +/* PHY mutex lock wrappers for operations on PHY HW controlled LEDs, so that PHY drivers + * implementing these operations don't have to lock phydev->lock themselves. + */ +static int phy_led_init(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_init(dev, led); + mutex_unlock(&phydev->lock); + + return ret; +} + +static int phy_led_brightness_set(struct device *dev, struct hw_controlled_led *led, + enum led_brightness brightness) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_brightness_set(dev, led, brightness); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const char *phy_led_iter_hw_mode(struct device *dev, struct hw_controlled_led *led, + void **iter) +{ + struct phy_device *phydev = to_phy_device(dev); + const char *ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_iter_hw_mode(dev, led, iter); + mutex_unlock(&phydev->lock); + + return ret; +} + +static int phy_led_set_hw_mode(struct device *dev, struct hw_controlled_led *led, const char *mode) +{ + struct phy_device *phydev = to_phy_device(dev); + int ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_set_hw_mode(dev, led, mode); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const char *phy_led_get_hw_mode(struct device *dev, struct hw_controlled_led *led) +{ + struct phy_device *phydev = to_phy_device(dev); + const char *ret; + + mutex_lock(&phydev->lock); + ret = phydev->drv->led_ops->led_get_hw_mode(dev, led); + mutex_unlock(&phydev->lock); + + return ret; +} + +static const struct hw_controlled_led_ops phy_hw_controlled_led_ops = { + .led_init = phy_led_init, + .led_brightness_set = phy_led_brightness_set, + .led_iter_hw_mode = phy_led_iter_hw_mode, + .led_set_hw_mode = phy_led_set_hw_mode, + .led_get_hw_mode = phy_led_get_hw_mode, +}; + +static int of_phy_probe_leds(struct phy_device *phydev) +{ + char devicename[32]; + + if (!phydev->drv->led_ops) + return 0; + + snprintf(devicename, sizeof(devicename), "ethernet-phy%i", phydev->phyindex); + + return of_register_hw_controlled_leds(&phydev->mdio.dev, devicename, + &phy_hw_controlled_led_ops); +} + +#else /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + +static inline int of_phy_probe_leds(struct phy_device *phydev) +{ + return 0; +} + +#endif /* !IS_ENABLED(CONFIG_LEDS_HW_CONTROLLED) */ + /** * phy_probe - probe and init a PHY device * @dev: device to probe and init @@ -2922,6 +3019,12 @@ static int phy_probe(struct device *dev) mutex_unlock(&phydev->lock); + /* LEDs have to be registered with phydev mutex unlocked, because some operations can be + * called during registration that lock the mutex themselves + */ + if (!err) + of_phy_probe_leds(phydev); + return err; } diff --git a/include/linux/phy.h b/include/linux/phy.h index 52881e21ad951..8a4ab72c74dd4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -14,6 +14,7 @@ #include <linux/compiler.h> #include <linux/spinlock.h> #include <linux/ethtool.h> +#include <linux/leds-hw-controlled.h> #include <linux/linkmode.h> #include <linux/netlink.h> #include <linux/mdio.h> @@ -741,6 +742,9 @@ struct phy_driver { int (*set_loopback)(struct phy_device *dev, bool enable); int (*get_sqi)(struct phy_device *dev); int (*get_sqi_max)(struct phy_device *dev); + + /* PHY connected and controlled LEDs */ + const struct hw_controlled_led_ops *led_ops; }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv) -- 2.26.2