OSDN Git Service

igc: Export LEDs
authorKurt Kanzenbach <kurt@linutronix.de>
Tue, 29 Jun 2021 04:43:32 +0000 (21:43 -0700)
committerTony Nguyen <anthony.l.nguyen@intel.com>
Fri, 16 Jul 2021 21:08:12 +0000 (14:08 -0700)
Each i225 has three LEDs. Export them via the LED class framework.

Each LED is controllable via sysfs. Example:

$ cd /sys/class/leds/igc_led0
$ cat brightness      # Current Mode
$ cat max_brightness  # 15
$ echo 0 > brightness # Mode 0
$ echo 1 > brightness # Mode 1

The brightness field here reflects the different LED modes ranging
from 0 to 15.

Signed-off-by: Kurt Kanzenbach <kurt@linutronix.de>
Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Tested-by: Dvora Fuxbrumer <dvorax.fuxbrumer@linux.intel.com>
Signed-off-by: Tony Nguyen <anthony.l.nguyen@intel.com>
drivers/net/ethernet/intel/Kconfig
drivers/net/ethernet/intel/igc/igc.h
drivers/net/ethernet/intel/igc/igc_defines.h
drivers/net/ethernet/intel/igc/igc_main.c
drivers/net/ethernet/intel/igc/igc_regs.h

index 82744a7..3639cf7 100644 (file)
@@ -335,6 +335,7 @@ config IGC
        tristate "Intel(R) Ethernet Controller I225-LM/I225-V support"
        default n
        depends on PCI
+       depends on LEDS_CLASS
        help
          This driver supports Intel(R) Ethernet Controller I225-LM/I225-V
          family of adapters.
index a0ecfe5..2df0fd2 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/ptp_clock_kernel.h>
 #include <linux/timecounter.h>
 #include <linux/net_tstamp.h>
+#include <linux/leds.h>
 
 #include "igc_hw.h"
 
@@ -239,8 +240,17 @@ struct igc_adapter {
                struct timespec64 start;
                struct timespec64 period;
        } perout[IGC_N_PEROUT];
+
+       /* LEDs */
+       struct mutex led_mutex;
+       struct led_classdev led0;
+       struct led_classdev led1;
+       struct led_classdev led2;
 };
 
+#define led_to_igc(ldev, led)                          \
+       container_of(ldev, struct igc_adapter, led)
+
 void igc_up(struct igc_adapter *adapter);
 void igc_down(struct igc_adapter *adapter);
 int igc_open(struct net_device *netdev);
index c631569..156c3ef 100644 (file)
 #define IGC_CTRL_SDP0_DIR      0x00400000  /* SDP0 Data direction */
 #define IGC_CTRL_SDP1_DIR      0x00800000  /* SDP1 Data direction */
 
+/* LED Control */
+#define IGC_LEDCTL_LED0_MODE_SHIFT     0
+#define IGC_LEDCTL_LED0_MODE_MASK      GENMASK(3, 0)
+#define IGC_LEDCTL_LED1_MODE_SHIFT     8
+#define IGC_LEDCTL_LED1_MODE_MASK      GENMASK(11, 8)
+#define IGC_LEDCTL_LED2_MODE_SHIFT     16
+#define IGC_LEDCTL_LED2_MODE_MASK      GENMASK(19, 16)
+
+#define IGC_CONNSW_AUTOSENSE_EN                0x1
+
 /* As per the EAS the maximum supported size is 9.5KB (9728 bytes) */
 #define MAX_JUMBO_FRAME_SIZE   0x2600
 
index 11385c3..100819d 100644 (file)
@@ -6130,6 +6130,134 @@ err_inval:
        return -EINVAL;
 }
 
+static void igc_select_led(struct igc_adapter *adapter, int led,
+                          u32 *mask, u32 *shift)
+{
+       switch (led) {
+       case 0:
+               *mask  = IGC_LEDCTL_LED0_MODE_MASK;
+               *shift = IGC_LEDCTL_LED0_MODE_SHIFT;
+               break;
+       case 1:
+               *mask  = IGC_LEDCTL_LED1_MODE_MASK;
+               *shift = IGC_LEDCTL_LED1_MODE_SHIFT;
+               break;
+       case 2:
+               *mask  = IGC_LEDCTL_LED2_MODE_MASK;
+               *shift = IGC_LEDCTL_LED2_MODE_SHIFT;
+               break;
+       default:
+               *mask = *shift = 0;
+               dev_err(&adapter->pdev->dev, "Unknown led %d selected!", led);
+       }
+}
+
+static void igc_led_set(struct igc_adapter *adapter, int led, u16 brightness)
+{
+       struct igc_hw *hw = &adapter->hw;
+       u32 shift, mask, ledctl;
+
+       igc_select_led(adapter, led, &mask, &shift);
+
+       mutex_lock(&adapter->led_mutex);
+       ledctl = rd32(IGC_LEDCTL);
+       ledctl &= ~mask;
+       ledctl |= brightness << shift;
+       wr32(IGC_LEDCTL, ledctl);
+       mutex_unlock(&adapter->led_mutex);
+}
+
+static enum led_brightness igc_led_get(struct igc_adapter *adapter, int led)
+{
+       struct igc_hw *hw = &adapter->hw;
+       u32 shift, mask, ledctl;
+
+       igc_select_led(adapter, led, &mask, &shift);
+
+       mutex_lock(&adapter->led_mutex);
+       ledctl = rd32(IGC_LEDCTL);
+       mutex_unlock(&adapter->led_mutex);
+
+       return (ledctl & mask) >> shift;
+}
+
+static void igc_led0_set(struct led_classdev *ldev, enum led_brightness b)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led0);
+
+       igc_led_set(adapter, 0, b);
+}
+
+static enum led_brightness igc_led0_get(struct led_classdev *ldev)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led0);
+
+       return igc_led_get(adapter, 0);
+}
+
+static void igc_led1_set(struct led_classdev *ldev, enum led_brightness b)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led1);
+
+       igc_led_set(adapter, 1, b);
+}
+
+static enum led_brightness igc_led1_get(struct led_classdev *ldev)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led1);
+
+       return igc_led_get(adapter, 1);
+}
+
+static void igc_led2_set(struct led_classdev *ldev, enum led_brightness b)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led2);
+
+       igc_led_set(adapter, 2, b);
+}
+
+static enum led_brightness igc_led2_get(struct led_classdev *ldev)
+{
+       struct igc_adapter *adapter = led_to_igc(ldev, led2);
+
+       return igc_led_get(adapter, 2);
+}
+
+static int igc_led_setup(struct igc_adapter *adapter)
+{
+       /* Setup */
+       mutex_init(&adapter->led_mutex);
+
+       adapter->led0.name           = "igc_led0";
+       adapter->led0.max_brightness = 15;
+       adapter->led0.brightness_set = igc_led0_set;
+       adapter->led0.brightness_get = igc_led0_get;
+
+       adapter->led1.name           = "igc_led1";
+       adapter->led1.max_brightness = 15;
+       adapter->led1.brightness_set = igc_led1_set;
+       adapter->led1.brightness_get = igc_led1_get;
+
+       adapter->led2.name           = "igc_led2";
+       adapter->led2.max_brightness = 15;
+       adapter->led2.brightness_set = igc_led2_set;
+       adapter->led2.brightness_get = igc_led2_get;
+
+       /* Register leds */
+       led_classdev_register(&adapter->pdev->dev, &adapter->led0);
+       led_classdev_register(&adapter->pdev->dev, &adapter->led1);
+       led_classdev_register(&adapter->pdev->dev, &adapter->led2);
+
+       return 0;
+}
+
+static void igc_led_destroy(struct igc_adapter *adapter)
+{
+       led_classdev_unregister(&adapter->led0);
+       led_classdev_unregister(&adapter->led1);
+       led_classdev_unregister(&adapter->led2);
+}
+
 /**
  * igc_probe - Device Initialization Routine
  * @pdev: PCI device information struct
@@ -6357,6 +6485,8 @@ static int igc_probe(struct pci_dev *pdev,
 
        pm_runtime_put_noidle(&pdev->dev);
 
+       igc_led_setup(adapter);
+
        return 0;
 
 err_register:
@@ -6398,6 +6528,8 @@ static void igc_remove(struct pci_dev *pdev)
 
        igc_ptp_stop(adapter);
 
+       igc_led_destroy(adapter);
+
        set_bit(__IGC_DOWN, &adapter->state);
 
        del_timer_sync(&adapter->watchdog_timer);
index 828c350..f6247b0 100644 (file)
@@ -10,6 +10,8 @@
 #define IGC_EECD               0x00010  /* EEPROM/Flash Control - RW */
 #define IGC_CTRL_EXT           0x00018  /* Extended Device Control - RW */
 #define IGC_MDIC               0x00020  /* MDI Control - RW */
+#define IGC_LEDCTL             0x00E00  /* LED Control - RW */
+#define IGC_MDICNFG            0x00E04  /* MDC/MDIO Configuration - RW */
 #define IGC_CONNSW             0x00034  /* Copper/Fiber switch control - RW */
 #define IGC_VET                        0x00038  /* VLAN Ether Type - RW */
 #define IGC_I225_PHPM          0x00E14  /* I225 PHY Power Management */