| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (C) 2024 Linutronix GmbH */
- #include <linux/bits.h>
- #include <linux/leds.h>
- #include <linux/netdevice.h>
- #include <linux/pm_runtime.h>
- #include <uapi/linux/uleds.h>
- #include "igc.h"
- #define IGC_NUM_LEDS 3
- #define IGC_LEDCTL_LED0_MODE_SHIFT 0
- #define IGC_LEDCTL_LED0_MODE_MASK GENMASK(3, 0)
- #define IGC_LEDCTL_LED0_BLINK BIT(7)
- #define IGC_LEDCTL_LED1_MODE_SHIFT 8
- #define IGC_LEDCTL_LED1_MODE_MASK GENMASK(11, 8)
- #define IGC_LEDCTL_LED1_BLINK BIT(15)
- #define IGC_LEDCTL_LED2_MODE_SHIFT 16
- #define IGC_LEDCTL_LED2_MODE_MASK GENMASK(19, 16)
- #define IGC_LEDCTL_LED2_BLINK BIT(23)
- #define IGC_LEDCTL_MODE_ON 0x00
- #define IGC_LEDCTL_MODE_OFF 0x01
- #define IGC_LEDCTL_MODE_LINK_10 0x05
- #define IGC_LEDCTL_MODE_LINK_100 0x06
- #define IGC_LEDCTL_MODE_LINK_1000 0x07
- #define IGC_LEDCTL_MODE_LINK_2500 0x08
- #define IGC_LEDCTL_MODE_ACTIVITY 0x0b
- #define IGC_SUPPORTED_MODES \
- (BIT(TRIGGER_NETDEV_LINK_2500) | BIT(TRIGGER_NETDEV_LINK_1000) | \
- BIT(TRIGGER_NETDEV_LINK_100) | BIT(TRIGGER_NETDEV_LINK_10) | \
- BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))
- #define IGC_ACTIVITY_MODES \
- (BIT(TRIGGER_NETDEV_RX) | BIT(TRIGGER_NETDEV_TX))
- struct igc_led_classdev {
- struct net_device *netdev;
- struct led_classdev led;
- int index;
- };
- #define lcdev_to_igc_ldev(lcdev) \
- container_of(lcdev, struct igc_led_classdev, led)
- static void igc_led_select(struct igc_adapter *adapter, int led,
- u32 *mask, u32 *shift, u32 *blink)
- {
- switch (led) {
- case 0:
- *mask = IGC_LEDCTL_LED0_MODE_MASK;
- *shift = IGC_LEDCTL_LED0_MODE_SHIFT;
- *blink = IGC_LEDCTL_LED0_BLINK;
- break;
- case 1:
- *mask = IGC_LEDCTL_LED1_MODE_MASK;
- *shift = IGC_LEDCTL_LED1_MODE_SHIFT;
- *blink = IGC_LEDCTL_LED1_BLINK;
- break;
- case 2:
- *mask = IGC_LEDCTL_LED2_MODE_MASK;
- *shift = IGC_LEDCTL_LED2_MODE_SHIFT;
- *blink = IGC_LEDCTL_LED2_BLINK;
- break;
- default:
- *mask = *shift = *blink = 0;
- netdev_err(adapter->netdev, "Unknown LED %d selected!\n", led);
- }
- }
- static void igc_led_set(struct igc_adapter *adapter, int led, u32 mode,
- bool blink)
- {
- u32 shift, mask, blink_bit, ledctl;
- struct igc_hw *hw = &adapter->hw;
- igc_led_select(adapter, led, &mask, &shift, &blink_bit);
- pm_runtime_get_sync(&adapter->pdev->dev);
- mutex_lock(&adapter->led_mutex);
- /* Set mode */
- ledctl = rd32(IGC_LEDCTL);
- ledctl &= ~mask;
- ledctl |= mode << shift;
- /* Configure blinking */
- if (blink)
- ledctl |= blink_bit;
- else
- ledctl &= ~blink_bit;
- wr32(IGC_LEDCTL, ledctl);
- mutex_unlock(&adapter->led_mutex);
- pm_runtime_put(&adapter->pdev->dev);
- }
- static u32 igc_led_get(struct igc_adapter *adapter, int led)
- {
- u32 shift, mask, blink_bit, ledctl;
- struct igc_hw *hw = &adapter->hw;
- igc_led_select(adapter, led, &mask, &shift, &blink_bit);
- pm_runtime_get_sync(&adapter->pdev->dev);
- mutex_lock(&adapter->led_mutex);
- ledctl = rd32(IGC_LEDCTL);
- mutex_unlock(&adapter->led_mutex);
- pm_runtime_put(&adapter->pdev->dev);
- return (ledctl & mask) >> shift;
- }
- static int igc_led_brightness_set_blocking(struct led_classdev *led_cdev,
- enum led_brightness brightness)
- {
- struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
- struct igc_adapter *adapter = netdev_priv(ldev->netdev);
- u32 mode;
- if (brightness)
- mode = IGC_LEDCTL_MODE_ON;
- else
- mode = IGC_LEDCTL_MODE_OFF;
- netdev_dbg(adapter->netdev, "Set brightness for LED %d to mode %u!\n",
- ldev->index, mode);
- igc_led_set(adapter, ldev->index, mode, false);
- return 0;
- }
- static int igc_led_hw_control_is_supported(struct led_classdev *led_cdev,
- unsigned long flags)
- {
- if (flags & ~IGC_SUPPORTED_MODES)
- return -EOPNOTSUPP;
- /* If Tx and Rx selected, activity can be offloaded unless some other
- * mode is selected as well.
- */
- if ((flags & BIT(TRIGGER_NETDEV_TX)) &&
- (flags & BIT(TRIGGER_NETDEV_RX)) &&
- !(flags & ~IGC_ACTIVITY_MODES))
- return 0;
- /* Single Rx or Tx activity is not supported. */
- if (flags & IGC_ACTIVITY_MODES)
- return -EOPNOTSUPP;
- /* Only one mode can be active at a given time. */
- if (flags & (flags - 1))
- return -EOPNOTSUPP;
- return 0;
- }
- static int igc_led_hw_control_set(struct led_classdev *led_cdev,
- unsigned long flags)
- {
- struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
- struct igc_adapter *adapter = netdev_priv(ldev->netdev);
- u32 mode = IGC_LEDCTL_MODE_OFF;
- bool blink = false;
- if (flags & BIT(TRIGGER_NETDEV_LINK_10))
- mode = IGC_LEDCTL_MODE_LINK_10;
- if (flags & BIT(TRIGGER_NETDEV_LINK_100))
- mode = IGC_LEDCTL_MODE_LINK_100;
- if (flags & BIT(TRIGGER_NETDEV_LINK_1000))
- mode = IGC_LEDCTL_MODE_LINK_1000;
- if (flags & BIT(TRIGGER_NETDEV_LINK_2500))
- mode = IGC_LEDCTL_MODE_LINK_2500;
- if ((flags & BIT(TRIGGER_NETDEV_TX)) &&
- (flags & BIT(TRIGGER_NETDEV_RX)))
- mode = IGC_LEDCTL_MODE_ACTIVITY;
- netdev_dbg(adapter->netdev, "Set HW control for LED %d to mode %u!\n",
- ldev->index, mode);
- /* blink is recommended for activity */
- if (mode == IGC_LEDCTL_MODE_ACTIVITY)
- blink = true;
- igc_led_set(adapter, ldev->index, mode, blink);
- return 0;
- }
- static int igc_led_hw_control_get(struct led_classdev *led_cdev,
- unsigned long *flags)
- {
- struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
- struct igc_adapter *adapter = netdev_priv(ldev->netdev);
- u32 mode;
- mode = igc_led_get(adapter, ldev->index);
- switch (mode) {
- case IGC_LEDCTL_MODE_ACTIVITY:
- *flags = BIT(TRIGGER_NETDEV_TX) | BIT(TRIGGER_NETDEV_RX);
- break;
- case IGC_LEDCTL_MODE_LINK_10:
- *flags = BIT(TRIGGER_NETDEV_LINK_10);
- break;
- case IGC_LEDCTL_MODE_LINK_100:
- *flags = BIT(TRIGGER_NETDEV_LINK_100);
- break;
- case IGC_LEDCTL_MODE_LINK_1000:
- *flags = BIT(TRIGGER_NETDEV_LINK_1000);
- break;
- case IGC_LEDCTL_MODE_LINK_2500:
- *flags = BIT(TRIGGER_NETDEV_LINK_2500);
- break;
- }
- return 0;
- }
- static struct device *igc_led_hw_control_get_device(struct led_classdev *led_cdev)
- {
- struct igc_led_classdev *ldev = lcdev_to_igc_ldev(led_cdev);
- return &ldev->netdev->dev;
- }
- static void igc_led_get_name(struct igc_adapter *adapter, int index, char *buf,
- size_t buf_len)
- {
- snprintf(buf, buf_len, "igc-%x%x-led%d",
- pci_domain_nr(adapter->pdev->bus),
- pci_dev_id(adapter->pdev), index);
- }
- static int igc_setup_ldev(struct igc_led_classdev *ldev,
- struct net_device *netdev, int index)
- {
- struct igc_adapter *adapter = netdev_priv(netdev);
- struct led_classdev *led_cdev = &ldev->led;
- char led_name[LED_MAX_NAME_SIZE];
- ldev->netdev = netdev;
- ldev->index = index;
- igc_led_get_name(adapter, index, led_name, LED_MAX_NAME_SIZE);
- led_cdev->name = led_name;
- led_cdev->flags |= LED_RETAIN_AT_SHUTDOWN;
- led_cdev->max_brightness = 1;
- led_cdev->brightness_set_blocking = igc_led_brightness_set_blocking;
- led_cdev->hw_control_trigger = "netdev";
- led_cdev->hw_control_is_supported = igc_led_hw_control_is_supported;
- led_cdev->hw_control_set = igc_led_hw_control_set;
- led_cdev->hw_control_get = igc_led_hw_control_get;
- led_cdev->hw_control_get_device = igc_led_hw_control_get_device;
- return led_classdev_register(&netdev->dev, led_cdev);
- }
- int igc_led_setup(struct igc_adapter *adapter)
- {
- struct net_device *netdev = adapter->netdev;
- struct igc_led_classdev *leds;
- int i, err;
- mutex_init(&adapter->led_mutex);
- leds = kcalloc(IGC_NUM_LEDS, sizeof(*leds), GFP_KERNEL);
- if (!leds)
- return -ENOMEM;
- for (i = 0; i < IGC_NUM_LEDS; i++) {
- err = igc_setup_ldev(leds + i, netdev, i);
- if (err)
- goto err;
- }
- adapter->leds = leds;
- return 0;
- err:
- for (i--; i >= 0; i--)
- led_classdev_unregister(&((leds + i)->led));
- kfree(leds);
- return err;
- }
- void igc_led_free(struct igc_adapter *adapter)
- {
- struct igc_led_classdev *leds = adapter->leds;
- int i;
- for (i = 0; i < IGC_NUM_LEDS; i++)
- led_classdev_unregister(&((leds + i)->led));
- kfree(leds);
- }
|