Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.14.15.
  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
 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	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
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	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
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 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
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}