Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* 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		3
 28#define RTL8125_NUM_LEDS		4
 29
 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}