Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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 <kurt@linutronix.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Tested-by: Naama Meir <naamax.meir@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
Link: https://lore.kernel.org/r/20240213184138.1483968-1-anthony.l.nguyen@intel.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

authored by

Kurt Kanzenbach and committed by
Paolo Abeni
ea578703 e8d8acad

+301
+8
drivers/net/ethernet/intel/Kconfig
··· 368 368 369 369 To compile this driver as a module, choose M here. The module 370 370 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 + 371 379 config IDPF 372 380 tristate "Intel(R) Infrastructure Data Path Function Support" 373 381 depends on PCI_MSI
+1
drivers/net/ethernet/intel/igc/Makefile
··· 6 6 # 7 7 8 8 obj-$(CONFIG_IGC) += igc.o 9 + igc-$(CONFIG_IGC_LEDS) += igc_leds.o 9 10 10 11 igc-objs := igc_main.o igc_mac.o igc_i225.o igc_base.o igc_nvm.o igc_phy.o \ 11 12 igc_diag.o igc_ethtool.o igc_ptp.o igc_dump.o igc_tsn.o igc_xdp.o
+5
drivers/net/ethernet/intel/igc/igc.h
··· 295 295 struct timespec64 start; 296 296 struct timespec64 period; 297 297 } perout[IGC_N_PEROUT]; 298 + 299 + /* LEDs */ 300 + struct mutex led_mutex; 298 301 }; 299 302 300 303 void igc_up(struct igc_adapter *adapter); ··· 722 719 void igc_ptp_tx_hang(struct igc_adapter *adapter); 723 720 void igc_ptp_read(struct igc_adapter *adapter, struct timespec64 *ts); 724 721 void igc_ptp_tx_tstamp_event(struct igc_adapter *adapter); 722 + 723 + int igc_led_setup(struct igc_adapter *adapter); 725 724 726 725 #define igc_rx_pg_size(_ring) (PAGE_SIZE << igc_rx_pg_order(_ring)) 727 726
+280
drivers/net/ethernet/intel/igc/igc_leds.c
··· 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 + }
+6
drivers/net/ethernet/intel/igc/igc_main.c
··· 6978 6978 6979 6979 pm_runtime_put_noidle(&pdev->dev); 6980 6980 6981 + if (IS_ENABLED(CONFIG_IGC_LEDS)) { 6982 + err = igc_led_setup(adapter); 6983 + if (err) 6984 + goto err_register; 6985 + } 6986 + 6981 6987 return 0; 6982 6988 6983 6989 err_register:
+1
drivers/net/ethernet/intel/igc/igc_regs.h
··· 12 12 #define IGC_MDIC 0x00020 /* MDI Control - RW */ 13 13 #define IGC_CONNSW 0x00034 /* Copper/Fiber switch control - RW */ 14 14 #define IGC_VET 0x00038 /* VLAN Ether Type - RW */ 15 + #define IGC_LEDCTL 0x00E00 /* LED Control - RW */ 15 16 #define IGC_I225_PHPM 0x00E14 /* I225 PHY Power Management */ 16 17 #define IGC_GPHY_VERSION 0x0001E /* I225 gPHY Firmware Version */ 17 18