diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index c9bc5a91ec83..42f758880ef8 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -564,15 +564,17 @@ static int netdev_trig_activate(struct led_classdev *led_cdev) /* Check if hw control is active by default on the LED. * Init already enabled mode in hw control. */ - if (supports_hw_control(led_cdev) && - !led_cdev->hw_control_get(led_cdev, &mode)) { + if (supports_hw_control(led_cdev)) { dev = led_cdev->hw_control_get_device(led_cdev); if (dev) { const char *name = dev_name(dev); set_device_name(trigger_data, name, strlen(name)); trigger_data->hw_control = true; - trigger_data->mode = mode; + + rc = led_cdev->hw_control_get(led_cdev, &mode); + if (!rc) + trigger_data->mode = mode; } } @@ -593,6 +595,8 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev) cancel_delayed_work_sync(&trigger_data->work); + led_set_brightness(led_cdev, LED_OFF); + dev_put(trigger_data->net_dev); kfree(trigger_data); diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index 43b6cb725551..eba652a4c1d8 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -2893,6 +2893,272 @@ static int m88e1318_led_blink_set(struct phy_device *phydev, u8 index, MII_88E1318S_PHY_LED_FUNC, reg); } +struct marvell_led_rules { + int mode; + unsigned long rules; +}; + +static const struct marvell_led_rules marvell_led0[] = { + { + .mode = 0, + .rules = BIT(TRIGGER_NETDEV_LINK), + }, + { + .mode = 1, + .rules = (BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 3, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 4, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 5, + .rules = BIT(TRIGGER_NETDEV_TX), + }, + { + .mode = 6, + .rules = BIT(TRIGGER_NETDEV_LINK), + }, + { + .mode = 7, + .rules = BIT(TRIGGER_NETDEV_LINK_1000), + }, + { + .mode = 8, + .rules = 0, + }, +}; + +static const struct marvell_led_rules marvell_led1[] = { + { + .mode = 1, + .rules = (BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 2, + .rules = (BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_RX)), + }, + { + .mode = 3, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 4, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 6, + .rules = (BIT(TRIGGER_NETDEV_LINK_100) | + BIT(TRIGGER_NETDEV_LINK_1000)), + }, + { + .mode = 7, + .rules = BIT(TRIGGER_NETDEV_LINK_100), + }, + { + .mode = 8, + .rules = 0, + }, +}; + +static const struct marvell_led_rules marvell_led2[] = { + { + .mode = 0, + .rules = BIT(TRIGGER_NETDEV_LINK), + }, + { + .mode = 1, + .rules = (BIT(TRIGGER_NETDEV_LINK) | + BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 3, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 4, + .rules = (BIT(TRIGGER_NETDEV_RX) | + BIT(TRIGGER_NETDEV_TX)), + }, + { + .mode = 5, + .rules = BIT(TRIGGER_NETDEV_TX), + }, + { + .mode = 6, + .rules = (BIT(TRIGGER_NETDEV_LINK_10) | + BIT(TRIGGER_NETDEV_LINK_1000)), + }, + { + .mode = 7, + .rules = BIT(TRIGGER_NETDEV_LINK_10), + }, + { + .mode = 8, + .rules = 0, + }, +}; + +static int marvell_find_led_mode(unsigned long rules, + const struct marvell_led_rules *marvell_rules, + int count, + int *mode) +{ + int i; + + for (i = 0; i < count; i++) { + if (marvell_rules[i].rules == rules) { + *mode = marvell_rules[i].mode; + return 0; + } + } + return -EOPNOTSUPP; +} + +static int marvell_get_led_mode(u8 index, unsigned long rules, int *mode) +{ + int ret; + + switch (index) { + case 0: + ret = marvell_find_led_mode(rules, marvell_led0, + ARRAY_SIZE(marvell_led0), mode); + break; + case 1: + ret = marvell_find_led_mode(rules, marvell_led1, + ARRAY_SIZE(marvell_led1), mode); + break; + case 2: + ret = marvell_find_led_mode(rules, marvell_led2, + ARRAY_SIZE(marvell_led2), mode); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int marvell_find_led_rules(unsigned long *rules, + const struct marvell_led_rules *marvell_rules, + int count, + int mode) +{ + int i; + + for (i = 0; i < count; i++) { + if (marvell_rules[i].mode == mode) { + *rules = marvell_rules[i].rules; + return 0; + } + } + return -EOPNOTSUPP; +} + +static int marvell_get_led_rules(u8 index, unsigned long *rules, int mode) +{ + int ret; + + switch (index) { + case 0: + ret = marvell_find_led_rules(rules, marvell_led0, + ARRAY_SIZE(marvell_led0), mode); + break; + case 1: + ret = marvell_find_led_rules(rules, marvell_led1, + ARRAY_SIZE(marvell_led1), mode); + break; + case 2: + ret = marvell_find_led_rules(rules, marvell_led2, + ARRAY_SIZE(marvell_led2), mode); + break; + default: + ret = -EOPNOTSUPP; + } + + return ret; +} + +static int m88e1318_led_hw_is_supported(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + int mode, ret; + + switch (index) { + case 0: + case 1: + case 2: + ret = marvell_get_led_mode(index, rules, &mode); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int m88e1318_led_hw_control_set(struct phy_device *phydev, u8 index, + unsigned long rules) +{ + int mode, ret, reg; + + switch (index) { + case 0: + case 1: + case 2: + ret = marvell_get_led_mode(index, rules, &mode); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, + MII_88E1318S_PHY_LED_FUNC); + if (reg < 0) + return reg; + + reg &= ~(0xf << (4 * index)); + reg |= mode << (4 * index); + return phy_write_paged(phydev, MII_MARVELL_LED_PAGE, + MII_88E1318S_PHY_LED_FUNC, reg); +} + +static int m88e1318_led_hw_control_get(struct phy_device *phydev, u8 index, + unsigned long *rules) +{ + int mode, reg; + + if (index > 2) + return -EINVAL; + + reg = phy_read_paged(phydev, MII_MARVELL_LED_PAGE, + MII_88E1318S_PHY_LED_FUNC); + if (reg < 0) + return reg; + + mode = (reg >> (4 * index)) & 0xf; + + return marvell_get_led_rules(index, rules, mode); +} + static int marvell_probe(struct phy_device *phydev) { struct marvell_priv *priv; @@ -3144,6 +3410,9 @@ static struct phy_driver marvell_drivers[] = { .get_stats = marvell_get_stats, .led_brightness_set = m88e1318_led_brightness_set, .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, { .phy_id = MARVELL_PHY_ID_88E1145, @@ -3252,6 +3521,9 @@ static struct phy_driver marvell_drivers[] = { .cable_test_get_status = marvell_vct7_cable_test_get_status, .led_brightness_set = m88e1318_led_brightness_set, .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, { .phy_id = MARVELL_PHY_ID_88E1540, @@ -3280,6 +3552,9 @@ static struct phy_driver marvell_drivers[] = { .cable_test_get_status = marvell_vct7_cable_test_get_status, .led_brightness_set = m88e1318_led_brightness_set, .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, { .phy_id = MARVELL_PHY_ID_88E1545, @@ -3308,6 +3583,9 @@ static struct phy_driver marvell_drivers[] = { .cable_test_get_status = marvell_vct7_cable_test_get_status, .led_brightness_set = m88e1318_led_brightness_set, .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, { .phy_id = MARVELL_PHY_ID_88E3016, @@ -3451,6 +3729,9 @@ static struct phy_driver marvell_drivers[] = { .set_tunable = m88e1540_set_tunable, .led_brightness_set = m88e1318_led_brightness_set, .led_blink_set = m88e1318_led_blink_set, + .led_hw_is_supported = m88e1318_led_hw_is_supported, + .led_hw_control_set = m88e1318_led_hw_control_set, + .led_hw_control_get = m88e1318_led_hw_control_get, }, }; diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 3e9909b30938..17cb3e07216a 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -3020,6 +3020,61 @@ static int phy_led_blink_set(struct led_classdev *led_cdev, return err; } +static __maybe_unused struct device * +phy_led_hw_control_get_device(struct led_classdev *led_cdev) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + + if (phydev->attached_dev) + return &phydev->attached_dev->dev; + return NULL; +} + +static int __maybe_unused +phy_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_control_get(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + +static int __maybe_unused +phy_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_control_set(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + +static __maybe_unused int phy_led_hw_is_supported(struct led_classdev *led_cdev, + unsigned long rules) +{ + struct phy_led *phyled = to_phy_led(led_cdev); + struct phy_device *phydev = phyled->phydev; + int err; + + mutex_lock(&phydev->lock); + err = phydev->drv->led_hw_is_supported(phydev, phyled->index, rules); + mutex_unlock(&phydev->lock); + + return err; +} + static void phy_leds_unregister(struct phy_device *phydev) { struct phy_led *phyled; @@ -3057,6 +3112,19 @@ static int of_phy_led(struct phy_device *phydev, cdev->brightness_set_blocking = phy_led_set_brightness; if (phydev->drv->led_blink_set) cdev->blink_set = phy_led_blink_set; + +#ifdef CONFIG_LEDS_TRIGGERS + if (phydev->drv->led_hw_is_supported && + phydev->drv->led_hw_control_set && + phydev->drv->led_hw_control_get) { + cdev->hw_control_is_supported = phy_led_hw_is_supported; + cdev->hw_control_set = phy_led_hw_control_set; + cdev->hw_control_get = phy_led_hw_control_get; + cdev->hw_control_trigger = "netdev"; + } + + cdev->hw_control_get_device = phy_led_hw_control_get_device; +#endif cdev->max_brightness = 1; init_data.devicename = dev_name(&phydev->mdio.dev); init_data.fwnode = of_fwnode_handle(led); diff --git a/include/linux/phy.h b/include/linux/phy.h index b963ce22e7c7..3c1ceedd1b77 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -1105,6 +1105,39 @@ struct phy_driver { int (*led_blink_set)(struct phy_device *dev, u8 index, unsigned long *delay_on, unsigned long *delay_off); + /** + * @led_hw_is_supported: Can the HW support the given rules. + * @dev: PHY device which has the LED + * @index: Which LED of the PHY device + * @rules The core is interested in these rules + * + * Return 0 if yes, -EOPNOTSUPP if not, or an error code. + */ + int (*led_hw_is_supported)(struct phy_device *dev, u8 index, + unsigned long rules); + /** + * @led_hw_control_set: Set the HW to control the LED + * @dev: PHY device which has the LED + * @index: Which LED of the PHY device + * @rules The rules used to control the LED + * + * Returns 0, or a an error code. + */ + int (*led_hw_control_set)(struct phy_device *dev, u8 index, + unsigned long rules); + /** + * @led_hw_control_get: Get how the HW is controlling the LED + * @dev: PHY device which has the LED + * @index: Which LED of the PHY device + * @rules Pointer to the rules used to control the LED + * + * Set *@rules to how the HW is currently blinking. Returns 0 + * on success, or a error code if the current blinking cannot + * be represented in rules, or some other error happens. + */ + int (*led_hw_control_get)(struct phy_device *dev, u8 index, + unsigned long *rules); + }; #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ struct phy_driver, mdiodrv)