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 | |
30 | struct 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 | |
38 | static 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 | |
53 | static 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(dev: 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, mask: 0x000f << shift, val: 0); |
63 | return -EOPNOTSUPP; |
64 | } |
65 | |
66 | return 0; |
67 | } |
68 | |
69 | static 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(dev: 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, mask: 0x000f << shift, val: mode << shift); |
87 | } |
88 | |
89 | static 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(dev: 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, val: 0); |
103 | netdev_notice(dev: ldev->ndev, format: "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 | |
121 | static 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 | |
129 | static void rtl8168_setup_ldev(struct r8169_led_classdev *ldev, |
130 | struct net_device *ndev, int index) |
131 | { |
132 | struct rtl8169_private *tp = netdev_priv(dev: 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, idx: index, buf: 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(parent: &ndev->dev, led_cdev); |
150 | } |
151 | |
152 | struct 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, size: sizeof(*leds), GFP_KERNEL); |
158 | if (!leds) |
159 | return NULL; |
160 | |
161 | for (i = 0; i < RTL8168_NUM_LEDS; i++) |
162 | rtl8168_setup_ldev(ldev: leds + i, ndev, index: i); |
163 | |
164 | return leds; |
165 | } |
166 | |
167 | static 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(dev: 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, index: ldev->index, mode: 0); |
176 | return -EOPNOTSUPP; |
177 | } |
178 | |
179 | return 0; |
180 | } |
181 | |
182 | static 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(dev: 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, index: ldev->index, mode); |
201 | } |
202 | |
203 | static 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(dev: ldev->ndev); |
208 | int mode; |
209 | |
210 | mode = rtl8125_get_led_mode(tp, index: 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 | |
228 | static 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(dev: 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, idx: index, buf: 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(parent: &ndev->dev, led_cdev); |
249 | } |
250 | |
251 | struct 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, size: 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(ldev: leds + i, ndev, index: i); |
262 | |
263 | return leds; |
264 | } |
265 | |
266 | void 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(led_cdev: &l->led); |
273 | |
274 | kfree(objp: leds); |
275 | } |
276 |
Definitions
- r8169_led_classdev
- r8169_trigger_mode_is_valid
- rtl8168_led_hw_control_is_supported
- rtl8168_led_hw_control_set
- rtl8168_led_hw_control_get
- r8169_led_hw_control_get_device
- rtl8168_setup_ldev
- rtl8168_init_leds
- rtl8125_led_hw_control_is_supported
- rtl8125_led_hw_control_set
- rtl8125_led_hw_control_get
- rtl8125_setup_led_ldev
- rtl8125_init_leds
Improve your Profiling and Debugging skills
Find out more