brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · 3929b25 Raw
303 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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			313 14#define IGC_LEDCTL_LED0_MODE_SHIFT	015#define IGC_LEDCTL_LED0_MODE_MASK	GENMASK(3, 0)16#define IGC_LEDCTL_LED0_BLINK		BIT(7)17#define IGC_LEDCTL_LED1_MODE_SHIFT	818#define IGC_LEDCTL_LED1_MODE_MASK	GENMASK(11, 8)19#define IGC_LEDCTL_LED1_BLINK		BIT(15)20#define IGC_LEDCTL_LED2_MODE_SHIFT	1621#define IGC_LEDCTL_LED2_MODE_MASK	GENMASK(19, 16)22#define IGC_LEDCTL_LED2_BLINK		BIT(23)23 24#define IGC_LEDCTL_MODE_ON		0x0025#define IGC_LEDCTL_MODE_OFF		0x0126#define IGC_LEDCTL_MODE_LINK_10		0x0527#define IGC_LEDCTL_MODE_LINK_100	0x0628#define IGC_LEDCTL_MODE_LINK_1000	0x0729#define IGC_LEDCTL_MODE_LINK_2500	0x0830#define IGC_LEDCTL_MODE_ACTIVITY	0x0b31 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 40struct 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 49static 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 74static 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	else94		ledctl &= ~blink_bit;95	wr32(IGC_LEDCTL, ledctl);96 97	mutex_unlock(&adapter->led_mutex);98	pm_runtime_put(&adapter->pdev->dev);99}100 101static 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 117static 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	else127		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 137static 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 other144	 * 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 162static 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 194static 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 224static 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 231static 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 239static 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 263int 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 285err:286	for (i--; i >= 0; i--)287		led_classdev_unregister(&((leds + i)->led));288 289	kfree(leds);290	return err;291}292 293void 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