1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (C) 2024 Linutronix GmbH */
3
4 #include <linux/bits.h>
5 #include <linux/leds.h>
6 #include <linux/netdevice.h>
7 #include <linux/pm_runtime.h>
8 #include <uapi/linux/uleds.h>
9
10 #include "igc.h"
11
12 #define IGC_NUM_LEDS 3
13
14 #define IGC_LEDCTL_LED0_MODE_SHIFT 0
15 #define IGC_LEDCTL_LED0_MODE_MASK GENMASK(3, 0)
16 #define IGC_LEDCTL_LED0_BLINK BIT(7)
17 #define IGC_LEDCTL_LED1_MODE_SHIFT 8
18 #define IGC_LEDCTL_LED1_MODE_MASK GENMASK(11, 8)
19 #define IGC_LEDCTL_LED1_BLINK BIT(15)
20 #define IGC_LEDCTL_LED2_MODE_SHIFT 16
21 #define IGC_LEDCTL_LED2_MODE_MASK GENMASK(19, 16)
22 #define IGC_LEDCTL_LED2_BLINK BIT(23)
23
24 #define IGC_LEDCTL_MODE_ON 0x00
25 #define IGC_LEDCTL_MODE_OFF 0x01
26 #define IGC_LEDCTL_MODE_LINK_10 0x05
27 #define IGC_LEDCTL_MODE_LINK_100 0x06
28 #define IGC_LEDCTL_MODE_LINK_1000 0x07
29 #define IGC_LEDCTL_MODE_LINK_2500 0x08
30 #define IGC_LEDCTL_MODE_ACTIVITY 0x0b
31
32 #define IGC_SUPPORTED_MODES \
33 (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK_1000) | \
34 BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_10) | \
35 BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))
36
37 #define IGC_ACTIVITY_MODES \
38 (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))
39
40 struct igc_led_classdev {
41 struct net_device *netdev;
42 struct led_classdev led;
43 int index;
44 };
45
46 #define lcdev_to_igc_ldev(lcdev) \
47 container_of(lcdev, struct igc_led_classdev, led)
48
igc_led_select(struct igc_adapter * adapter,int led,u32 * mask,u32 * shift,u32 * blink)49 static void igc_led_select(struct igc_adapter *adapter, int led,
50 u32 *mask, u32 *shift, u32 *blink)
51 {
52 switch (led) {
53 case 0:
54 *mask = IGC_LEDCTL_LED0_MODE_MASK;
55 *shift = IGC_LEDCTL_LED0_MODE_SHIFT;
56 *blink = IGC_LEDCTL_LED0_BLINK;
57 break;
58 case 1:
59 *mask = IGC_LEDCTL_LED1_MODE_MASK;
60 *shift = IGC_LEDCTL_LED1_MODE_SHIFT;
61 *blink = IGC_LEDCTL_LED1_BLINK;
62 break;
63 case 2:
64 *mask = IGC_LEDCTL_LED2_MODE_MASK;
65 *shift = IGC_LEDCTL_LED2_MODE_SHIFT;
66 *blink = IGC_LEDCTL_LED2_BLINK;
67 break;
68 default:
69 *mask = *shift = *blink = 0;
70 netdev_err(adapter->netdev, "Unknown LED %d selected!\n", led);
71 }
72 }
73
igc_led_set(struct igc_adapter * adapter,int led,u32 mode,bool blink)74 static void igc_led_set(struct igc_adapter *adapter, int led, u32 mode,
75 bool blink)
76 {
77 u32 shift, mask, blink_bit, ledctl;
78 struct igc_hw *hw = &adapter->hw;
79
80 igc_led_select(adapter, led, &mask, &shift, &blink_bit);
81
82 pm_runtime_get_sync(&adapter->pdev->dev);
83 mutex_lock(&adapter->led_mutex);
84
85 /* Set mode */
86 ledctl = rd32(IGC_LEDCTL);
87 ledctl &= ~mask;
88 ledctl |= mode << shift;
89
90 /* Configure blinking */
91 if (blink)
92 ledctl |= blink_bit;
93 else
94 ledctl &= ~blink_bit;
95 wr32(IGC_LEDCTL, ledctl);
96
97 mutex_unlock(&adapter->led_mutex);
98 pm_runtime_put(&adapter->pdev->dev);
99 }
100
igc_led_get(struct igc_adapter * adapter,int led)101 static u32 igc_led_get(struct igc_adapter *adapter, int led)
102 {
103 u32 shift, mask, blink_bit, ledctl;
104 struct igc_hw *hw = &adapter->hw;
105
106 igc_led_select(adapter, led, &mask, &shift, &blink_bit);
107
108 pm_runtime_get_sync(&adapter->pdev->dev);
109 mutex_lock(&adapter->led_mutex);
110 ledctl = rd32(IGC_LEDCTL);
111 mutex_unlock(&adapter->led_mutex);
112 pm_runtime_put(&adapter->pdev->dev);
113
114 return (ledctl & mask) >> shift;
115 }
116
igc_led_brightness_set_blocking(struct led_classdev * led_cdev,enum led_brightness brightness)117 static int igc_led_brightness_set_blocking(struct led_classdev *led_cdev,
118 enum led_brightness brightness)
119 {
120 struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
121 struct igc_adapter *adapter = netdev_priv(ldev->netdev);
122 u32 mode;
123
124 if (brightness)
125 mode = IGC_LEDCTL_MODE_ON;
126 else
127 mode = IGC_LEDCTL_MODE_OFF;
128
129 netdev_dbg(adapter->netdev, "Set brightness for LED %d to mode %u!\n",
130 ldev->index, mode);
131
132 igc_led_set(adapter, ldev->index, mode, false);
133
134 return 0;
135 }
136
igc_led_hw_control_is_supported(struct led_classdev * led_cdev,unsigned long flags)137 static int igc_led_hw_control_is_supported(struct led_classdev *led_cdev,
138 unsigned long flags)
139 {
140 if (flags & ~IGC_SUPPORTED_MODES)
141 return -EOPNOTSUPP;
142
143 /* If Tx and Rx selected, activity can be offloaded unless some other
144 * mode is selected as well.
145 */
146 if ((flags & BIT(TRIGGER_NETDEV_TX)) &&
147 (flags & BIT(TRIGGER_NETDEV_RX)) &&
148 !(flags & ~IGC_ACTIVITY_MODES))
149 return 0;
150
151 /* Single Rx or Tx activity is not supported. */
152 if (flags & IGC_ACTIVITY_MODES)
153 return -EOPNOTSUPP;
154
155 /* Only one mode can be active at a given time. */
156 if (flags & (flags - 1))
157 return -EOPNOTSUPP;
158
159 return 0;
160 }
161
igc_led_hw_control_set(struct led_classdev * led_cdev,unsigned long flags)162 static int igc_led_hw_control_set(struct led_classdev *led_cdev,
163 unsigned long flags)
164 {
165 struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
166 struct igc_adapter *adapter = netdev_priv(ldev->netdev);
167 u32 mode = IGC_LEDCTL_MODE_OFF;
168 bool blink = false;
169
170 if (flags & BIT(TRIGGER_NETDEV_LINK_10))
171 mode = IGC_LEDCTL_MODE_LINK_10;
172 if (flags & BIT(TRIGGER_NETDEV_LINK_100))
173 mode = IGC_LEDCTL_MODE_LINK_100;
174 if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
175 mode = IGC_LEDCTL_MODE_LINK_1000;
176 if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
177 mode = IGC_LEDCTL_MODE_LINK_2500;
178 if ((flags & BIT(TRIGGER_NETDEV_TX)) &&
179 (flags & BIT(TRIGGER_NETDEV_RX)))
180 mode = IGC_LEDCTL_MODE_ACTIVITY;
181
182 netdev_dbg(adapter->netdev, "Set HW control for LED %d to mode %u!\n",
183 ldev->index, mode);
184
185 /* blink is recommended for activity */
186 if (mode == IGC_LEDCTL_MODE_ACTIVITY)
187 blink = true;
188
189 igc_led_set(adapter, ldev->index, mode, blink);
190
191 return 0;
192 }
193
igc_led_hw_control_get(struct led_classdev * led_cdev,unsigned long * flags)194 static int igc_led_hw_control_get(struct led_classdev *led_cdev,
195 unsigned long *flags)
196 {
197 struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
198 struct igc_adapter *adapter = netdev_priv(ldev->netdev);
199 u32 mode;
200
201 mode = igc_led_get(adapter, ldev->index);
202
203 switch (mode) {
204 case IGC_LEDCTL_MODE_ACTIVITY:
205 *flags = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
206 break;
207 case IGC_LEDCTL_MODE_LINK_10:
208 *flags = BIT(TRIGGER_NETDEV_LINK_10);
209 break;
210 case IGC_LEDCTL_MODE_LINK_100:
211 *flags = BIT(TRIGGER_NETDEV_LINK_100);
212 break;
213 case IGC_LEDCTL_MODE_LINK_1000:
214 *flags = BIT(TRIGGER_NETDEV_LINK_1000);
215 break;
216 case IGC_LEDCTL_MODE_LINK_2500:
217 *flags = BIT(TRIGGER_NETDEV_LINK_2500);
218 break;
219 }
220
221 return 0;
222 }
223
igc_led_hw_control_get_device(struct led_classdev * led_cdev)224 static struct device *igc_led_hw_control_get_device(struct led_classdev *led_cdev)
225 {
226 struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
227
228 return &ldev->netdev->dev;
229 }
230
igc_led_get_name(struct igc_adapter * adapter,int index,char * buf,size_t buf_len)231 static void igc_led_get_name(struct igc_adapter *adapter, int index, char *buf,
232 size_t buf_len)
233 {
234 snprintf(buf, buf_len, "igc-%x%x-led%d",
235 pci_domain_nr(adapter->pdev->bus),
236 pci_dev_id(adapter->pdev), index);
237 }
238
igc_setup_ldev(struct igc_led_classdev * ldev,struct net_device * netdev,int index)239 static int igc_setup_ldev(struct igc_led_classdev *ldev,
240 struct net_device *netdev, int index)
241 {
242 struct igc_adapter *adapter = netdev_priv(netdev);
243 struct led_classdev *led_cdev = &ldev->led;
244 char led_name[LED_MAX_NAME_SIZE];
245
246 ldev->netdev = netdev;
247 ldev->index = index;
248
249 igc_led_get_name(adapter, index, led_name, LED_MAX_NAME_SIZE);
250 led_cdev->name = led_name;
251 led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
252 led_cdev->max_brightness = 1;
253 led_cdev->brightness_set_blocking = igc_led_brightness_set_blocking;
254 led_cdev->hw_control_trigger = "netdev";
255 led_cdev->hw_control_is_supported = igc_led_hw_control_is_supported;
256 led_cdev->hw_control_set = igc_led_hw_control_set;
257 led_cdev->hw_control_get = igc_led_hw_control_get;
258 led_cdev->hw_control_get_device = igc_led_hw_control_get_device;
259
260 return led_classdev_register(&netdev->dev, led_cdev);
261 }
262
igc_led_setup(struct igc_adapter * adapter)263 int igc_led_setup(struct igc_adapter *adapter)
264 {
265 struct net_device *netdev = adapter->netdev;
266 struct igc_led_classdev *leds;
267 int i, err;
268
269 mutex_init(&adapter->led_mutex);
270
271 leds = kcalloc(IGC_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
272 if (!leds)
273 return -ENOMEM;
274
275 for (i = 0; i < IGC_NUM_LEDS; i++) {
276 err = igc_setup_ldev(leds + i, netdev, i);
277 if (err)
278 goto err;
279 }
280
281 adapter->leds = leds;
282
283 return 0;
284
285 err:
286 for (i--; i >= 0; i--)
287 led_classdev_unregister(&((leds + i)->led));
288
289 kfree(leds);
290 return err;
291 }
292
igc_led_free(struct igc_adapter * adapter)293 void igc_led_free(struct igc_adapter *adapter)
294 {
295 struct igc_led_classdev *leds = adapter->leds;
296 int i;
297
298 for (i = 0; i < IGC_NUM_LEDS; i++)
299 led_classdev_unregister(&((leds + i)->led));
300
301 kfree(leds);
302 }
303