diff options
Diffstat (limited to 'drivers/net/ethernet/realtek/r8169_leds.c')
-rw-r--r-- | drivers/net/ethernet/realtek/r8169_leds.c | 106 |
1 files changed, 106 insertions, 0 deletions
diff --git a/drivers/net/ethernet/realtek/r8169_leds.c b/drivers/net/ethernet/realtek/r8169_leds.c index 78078eca3c43..7c5dc9d0df85 100644 --- a/drivers/net/ethernet/realtek/r8169_leds.c +++ b/drivers/net/ethernet/realtek/r8169_leds.c @@ -18,7 +18,14 @@ #define RTL8168_LED_CTRL_LINK_100 BIT(1) #define RTL8168_LED_CTRL_LINK_10 BIT(0) +#define RTL8125_LED_CTRL_ACT BIT(9) +#define RTL8125_LED_CTRL_LINK_2500 BIT(5) +#define RTL8125_LED_CTRL_LINK_1000 BIT(3) +#define RTL8125_LED_CTRL_LINK_100 BIT(1) +#define RTL8125_LED_CTRL_LINK_10 BIT(0) + #define RTL8168_NUM_LEDS 3 +#define RTL8125_NUM_LEDS 4 struct r8169_led_classdev { struct led_classdev led; @@ -156,3 +163,102 @@ void rtl8168_init_leds(struct net_device *ndev) for (i = 0; i < RTL8168_NUM_LEDS; i++) rtl8168_setup_ldev(leds + i, ndev, i); } + +static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + + if (!r8169_trigger_mode_is_valid(flags)) { + /* Switch LED off to indicate that mode isn't supported */ + rtl8125_set_led_mode(tp, ldev->index, 0); + return -EOPNOTSUPP; + } + + return 0; +} + +static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev, + unsigned long flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + u16 mode = 0; + + if (flags & BIT(TRIGGER_NETDEV_LINK_10)) + mode |= RTL8125_LED_CTRL_LINK_10; + if (flags & BIT(TRIGGER_NETDEV_LINK_100)) + mode |= RTL8125_LED_CTRL_LINK_100; + if (flags & BIT(TRIGGER_NETDEV_LINK_1000)) + mode |= RTL8125_LED_CTRL_LINK_1000; + if (flags & BIT(TRIGGER_NETDEV_LINK_2500)) + mode |= RTL8125_LED_CTRL_LINK_2500; + if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX))) + mode |= RTL8125_LED_CTRL_ACT; + + return rtl8125_set_led_mode(tp, ldev->index, mode); +} + +static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev, + unsigned long *flags) +{ + struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev); + struct rtl8169_private *tp = netdev_priv(ldev->ndev); + int mode; + + mode = rtl8125_get_led_mode(tp, ldev->index); + if (mode < 0) + return mode; + + if (mode & RTL8125_LED_CTRL_LINK_10) + *flags |= BIT(TRIGGER_NETDEV_LINK_10); + if (mode & RTL8125_LED_CTRL_LINK_100) + *flags |= BIT(TRIGGER_NETDEV_LINK_100); + if (mode & RTL8125_LED_CTRL_LINK_1000) + *flags |= BIT(TRIGGER_NETDEV_LINK_1000); + if (mode & RTL8125_LED_CTRL_LINK_2500) + *flags |= BIT(TRIGGER_NETDEV_LINK_2500); + if (mode & RTL8125_LED_CTRL_ACT) + *flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX); + + return 0; +} + +static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev, + struct net_device *ndev, int index) +{ + struct rtl8169_private *tp = netdev_priv(ndev); + struct led_classdev *led_cdev = &ldev->led; + char led_name[LED_MAX_NAME_SIZE]; + + ldev->ndev = ndev; + ldev->index = index; + + r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE); + led_cdev->name = led_name; + led_cdev->hw_control_trigger = "netdev"; + led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN; + led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported; + led_cdev->hw_control_set = rtl8125_led_hw_control_set; + led_cdev->hw_control_get = rtl8125_led_hw_control_get; + led_cdev->hw_control_get_device = r8169_led_hw_control_get_device; + + /* ignore errors */ + devm_led_classdev_register(&ndev->dev, led_cdev); +} + +void rtl8125_init_leds(struct net_device *ndev) +{ + /* bind resource mgmt to netdev */ + struct device *dev = &ndev->dev; + struct r8169_led_classdev *leds; + int i; + + leds = devm_kcalloc(dev, RTL8125_NUM_LEDS, sizeof(*leds), GFP_KERNEL); + if (!leds) + return; + + for (i = 0; i < RTL8125_NUM_LEDS; i++) + rtl8125_setup_led_ldev(leds + i, ndev, i); +} |