brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · e10bee7 Raw
276 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/* r8169_leds.c: Realtek 8169/8168/8101/8125 ethernet driver.3 *4 * Copyright (c) 2023 Heiner Kallweit <hkallweit1@gmail.com>5 *6 * See MAINTAINERS file for support contact information.7 */8 9#include <linux/leds.h>10#include <linux/netdevice.h>11#include <uapi/linux/uleds.h>12 13#include "r8169.h"14 15#define RTL8168_LED_CTRL_OPTION2	BIT(15)16#define RTL8168_LED_CTRL_ACT		BIT(3)17#define RTL8168_LED_CTRL_LINK_1000	BIT(2)18#define RTL8168_LED_CTRL_LINK_100	BIT(1)19#define RTL8168_LED_CTRL_LINK_10	BIT(0)20 21#define RTL8125_LED_CTRL_ACT		BIT(9)22#define RTL8125_LED_CTRL_LINK_2500	BIT(5)23#define RTL8125_LED_CTRL_LINK_1000	BIT(3)24#define RTL8125_LED_CTRL_LINK_100	BIT(1)25#define RTL8125_LED_CTRL_LINK_10	BIT(0)26 27#define RTL8168_NUM_LEDS		328#define RTL8125_NUM_LEDS		429 30struct r8169_led_classdev {31	struct led_classdev led;32	struct net_device *ndev;33	int index;34};35 36#define lcdev_to_r8169_ldev(lcdev) container_of(lcdev, struct r8169_led_classdev, led)37 38static bool r8169_trigger_mode_is_valid(unsigned long flags)39{40	bool rx, tx;41 42	if (flags & BIT(TRIGGER_NETDEV_HALF_DUPLEX))43		return false;44	if (flags & BIT(TRIGGER_NETDEV_FULL_DUPLEX))45		return false;46 47	rx = flags & BIT(TRIGGER_NETDEV_RX);48	tx = flags & BIT(TRIGGER_NETDEV_TX);49 50	return rx == tx;51}52 53static int rtl8168_led_hw_control_is_supported(struct led_classdev *led_cdev,54					       unsigned long flags)55{56	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);57	struct rtl8169_private *tp = netdev_priv(ldev->ndev);58	int shift = ldev->index * 4;59 60	if (!r8169_trigger_mode_is_valid(flags)) {61		/* Switch LED off to indicate that mode isn't supported */62		rtl8168_led_mod_ctrl(tp, 0x000f << shift, 0);63		return -EOPNOTSUPP;64	}65 66	return 0;67}68 69static int rtl8168_led_hw_control_set(struct led_classdev *led_cdev,70				      unsigned long flags)71{72	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);73	struct rtl8169_private *tp = netdev_priv(ldev->ndev);74	int shift = ldev->index * 4;75	u16 mode = 0;76 77	if (flags & BIT(TRIGGER_NETDEV_LINK_10))78		mode |= RTL8168_LED_CTRL_LINK_10;79	if (flags & BIT(TRIGGER_NETDEV_LINK_100))80		mode |= RTL8168_LED_CTRL_LINK_100;81	if (flags & BIT(TRIGGER_NETDEV_LINK_1000))82		mode |= RTL8168_LED_CTRL_LINK_1000;83	if (flags & BIT(TRIGGER_NETDEV_TX))84		mode |= RTL8168_LED_CTRL_ACT;85 86	return rtl8168_led_mod_ctrl(tp, 0x000f << shift, mode << shift);87}88 89static int rtl8168_led_hw_control_get(struct led_classdev *led_cdev,90				      unsigned long *flags)91{92	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);93	struct rtl8169_private *tp = netdev_priv(ldev->ndev);94	int shift = ldev->index * 4;95	int mode;96 97	mode = rtl8168_get_led_mode(tp);98	if (mode < 0)99		return mode;100 101	if (mode & RTL8168_LED_CTRL_OPTION2) {102		rtl8168_led_mod_ctrl(tp, RTL8168_LED_CTRL_OPTION2, 0);103		netdev_notice(ldev->ndev, "Deactivating unsupported Option2 LED mode\n");104	}105 106	mode = (mode >> shift) & 0x000f;107 108	if (mode & RTL8168_LED_CTRL_ACT)109		*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);110 111	if (mode & RTL8168_LED_CTRL_LINK_10)112		*flags |= BIT(TRIGGER_NETDEV_LINK_10);113	if (mode & RTL8168_LED_CTRL_LINK_100)114		*flags |= BIT(TRIGGER_NETDEV_LINK_100);115	if (mode & RTL8168_LED_CTRL_LINK_1000)116		*flags |= BIT(TRIGGER_NETDEV_LINK_1000);117 118	return 0;119}120 121static struct device *122	r8169_led_hw_control_get_device(struct led_classdev *led_cdev)123{124	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);125 126	return &ldev->ndev->dev;127}128 129static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev,130			       struct net_device *ndev, int index)131{132	struct rtl8169_private *tp = netdev_priv(ndev);133	struct led_classdev *led_cdev = &ldev->led;134	char led_name[LED_MAX_NAME_SIZE];135 136	ldev->ndev = ndev;137	ldev->index = index;138 139	r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);140	led_cdev->name = led_name;141	led_cdev->hw_control_trigger = "netdev";142	led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;143	led_cdev->hw_control_is_supported = rtl8168_led_hw_control_is_supported;144	led_cdev->hw_control_set = rtl8168_led_hw_control_set;145	led_cdev->hw_control_get = rtl8168_led_hw_control_get;146	led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;147 148	/* ignore errors */149	led_classdev_register(&ndev->dev, led_cdev);150}151 152struct r8169_led_classdev *rtl8168_init_leds(struct net_device *ndev)153{154	struct r8169_led_classdev *leds;155	int i;156 157	leds = kcalloc(RTL8168_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);158	if (!leds)159		return NULL;160 161	for (i = 0; i < RTL8168_NUM_LEDS; i++)162		rtl8168_setup_ldev(leds + i, ndev, i);163 164	return leds;165}166 167static int rtl8125_led_hw_control_is_supported(struct led_classdev *led_cdev,168					       unsigned long flags)169{170	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);171	struct rtl8169_private *tp = netdev_priv(ldev->ndev);172 173	if (!r8169_trigger_mode_is_valid(flags)) {174		/* Switch LED off to indicate that mode isn't supported */175		rtl8125_set_led_mode(tp, ldev->index, 0);176		return -EOPNOTSUPP;177	}178 179	return 0;180}181 182static int rtl8125_led_hw_control_set(struct led_classdev *led_cdev,183				      unsigned long flags)184{185	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);186	struct rtl8169_private *tp = netdev_priv(ldev->ndev);187	u16 mode = 0;188 189	if (flags & BIT(TRIGGER_NETDEV_LINK_10))190		mode |= RTL8125_LED_CTRL_LINK_10;191	if (flags & BIT(TRIGGER_NETDEV_LINK_100))192		mode |= RTL8125_LED_CTRL_LINK_100;193	if (flags & BIT(TRIGGER_NETDEV_LINK_1000))194		mode |= RTL8125_LED_CTRL_LINK_1000;195	if (flags & BIT(TRIGGER_NETDEV_LINK_2500))196		mode |= RTL8125_LED_CTRL_LINK_2500;197	if (flags & (BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX)))198		mode |= RTL8125_LED_CTRL_ACT;199 200	return rtl8125_set_led_mode(tp, ldev->index, mode);201}202 203static int rtl8125_led_hw_control_get(struct led_classdev *led_cdev,204				      unsigned long *flags)205{206	struct r8169_led_classdev *ldev = lcdev_to_r8169_ldev(led_cdev);207	struct rtl8169_private *tp = netdev_priv(ldev->ndev);208	int mode;209 210	mode = rtl8125_get_led_mode(tp, ldev->index);211	if (mode < 0)212		return mode;213 214	if (mode & RTL8125_LED_CTRL_LINK_10)215		*flags |= BIT(TRIGGER_NETDEV_LINK_10);216	if (mode & RTL8125_LED_CTRL_LINK_100)217		*flags |= BIT(TRIGGER_NETDEV_LINK_100);218	if (mode & RTL8125_LED_CTRL_LINK_1000)219		*flags |= BIT(TRIGGER_NETDEV_LINK_1000);220	if (mode & RTL8125_LED_CTRL_LINK_2500)221		*flags |= BIT(TRIGGER_NETDEV_LINK_2500);222	if (mode & RTL8125_LED_CTRL_ACT)223		*flags |= BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);224 225	return 0;226}227 228static void rtl8125_setup_led_ldev(struct r8169_led_classdev *ldev,229				   struct net_device *ndev, int index)230{231	struct rtl8169_private *tp = netdev_priv(ndev);232	struct led_classdev *led_cdev = &ldev->led;233	char led_name[LED_MAX_NAME_SIZE];234 235	ldev->ndev = ndev;236	ldev->index = index;237 238	r8169_get_led_name(tp, index, led_name, LED_MAX_NAME_SIZE);239	led_cdev->name = led_name;240	led_cdev->hw_control_trigger = "netdev";241	led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;242	led_cdev->hw_control_is_supported = rtl8125_led_hw_control_is_supported;243	led_cdev->hw_control_set = rtl8125_led_hw_control_set;244	led_cdev->hw_control_get = rtl8125_led_hw_control_get;245	led_cdev->hw_control_get_device = r8169_led_hw_control_get_device;246 247	/* ignore errors */248	led_classdev_register(&ndev->dev, led_cdev);249}250 251struct r8169_led_classdev *rtl8125_init_leds(struct net_device *ndev)252{253	struct r8169_led_classdev *leds;254	int i;255 256	leds = kcalloc(RTL8125_NUM_LEDS + 1, sizeof(*leds), GFP_KERNEL);257	if (!leds)258		return NULL;259 260	for (i = 0; i < RTL8125_NUM_LEDS; i++)261		rtl8125_setup_led_ldev(leds + i, ndev, i);262 263	return leds;264}265 266void r8169_remove_leds(struct r8169_led_classdev *leds)267{268	if (!leds)269		return;270 271	for (struct r8169_led_classdev *l = leds; l->ndev; l++)272		led_classdev_unregister(&l->led);273 274	kfree(leds);275}276