Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ea57870

Browse files
shifty91Paolo Abeni
authored andcommitted
igc: Add support for LEDs on i225/i226
Add support for LEDs on i225/i226. The LEDs can be controlled via sysfs from user space using the netdev trigger. The LEDs are named as igc-<bus><device>-<led> to be easily identified. Offloading link speed and activity are supported. Other modes are simulated in software by using on/off. Tested on Intel i225. Signed-off-by: Kurt Kanzenbach <[email protected]> Reviewed-by: Andrew Lunn <[email protected]> Tested-by: Naama Meir <[email protected]> Signed-off-by: Tony Nguyen <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
1 parent e8d8aca commit ea57870

File tree

6 files changed

+301
-0
lines changed

6 files changed

+301
-0
lines changed

drivers/net/ethernet/intel/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ config IGC
368368

369369
To compile this driver as a module, choose M here. The module
370370
will be called igc.
371+
372+
config IGC_LEDS
373+
def_bool LEDS_TRIGGER_NETDEV
374+
depends on IGC && LEDS_CLASS
375+
help
376+
Optional support for controlling the NIC LED's with the netdev
377+
LED trigger.
378+
371379
config IDPF
372380
tristate "Intel(R) Infrastructure Data Path Function Support"
373381
depends on PCI_MSI

drivers/net/ethernet/intel/igc/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77

88
obj-$(CONFIG_IGC) += igc.o
9+
igc-$(CONFIG_IGC_LEDS) += igc_leds.o
910

1011
igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \
1112
igc_diag.o igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o igc_xdp.o

drivers/net/ethernet/intel/igc/igc.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,9 @@ struct igc_adapter {
295295
struct timespec64 start;
296296
struct timespec64 period;
297297
} perout[IGC_N_PEROUT];
298+
299+
/* LEDs */
300+
struct mutex led_mutex;
298301
};
299302

300303
void igc_up(struct igc_adapter *adapter);
@@ -720,6 +723,8 @@ void igc_ptp_tx_hang(struct igc_adapter *adapter);
720723
void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts);
721724
void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter);
722725

726+
int igc_led_setup(struct igc_adapter *adapter);
727+
723728
#define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring))
724729

725730
#define IGC_TXD_DCMD (IGC_ADVTXD_DCMD_EOP | IGC_ADVTXD_DCMD_RS)
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
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+
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+
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+
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+
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+
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+
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+
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+
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+
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+
239+
static void 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+
devm_led_classdev_register(&netdev->dev, led_cdev);
261+
}
262+
263+
int igc_led_setup(struct igc_adapter *adapter)
264+
{
265+
struct net_device *netdev = adapter->netdev;
266+
struct device *dev = &netdev->dev;
267+
struct igc_led_classdev *leds;
268+
int i;
269+
270+
mutex_init(&adapter->led_mutex);
271+
272+
leds = devm_kcalloc(dev, IGC_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
273+
if (!leds)
274+
return -ENOMEM;
275+
276+
for (i = 0; i < IGC_NUM_LEDS; i++)
277+
igc_setup_ldev(leds + i, netdev, i);
278+
279+
return 0;
280+
}

drivers/net/ethernet/intel/igc/igc_main.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6978,6 +6978,12 @@ static int igc_probe(struct pci_dev *pdev,
69786978

69796979
pm_runtime_put_noidle(&pdev->dev);
69806980

6981+
if (IS_ENABLED(CONFIG_IGC_LEDS)) {
6982+
err = igc_led_setup(adapter);
6983+
if (err)
6984+
goto err_register;
6985+
}
6986+
69816987
return 0;
69826988

69836989
err_register:

drivers/net/ethernet/intel/igc/igc_regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#define IGC_MDIC 0x00020 /* MDI Control - RW */
1313
#define IGC_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
1414
#define IGC_VET 0x00038 /* VLAN Ether Type - RW */
15+
#define IGC_LEDCTL 0x00E00 /* LED Control - RW */
1516
#define IGC_I225_PHPM 0x00E14 /* I225 PHY Power Management */
1617
#define IGC_GPHY_VERSION 0x0001E /* I225 gPHY Firmware Version */
1718

0 commit comments

Comments
 (0)