Merge tag 'leds_for_4.15rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anasz...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Nov 2017 02:09:31 +0000 (18:09 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 15 Nov 2017 02:09:31 +0000 (18:09 -0800)
Pull LED updates from Jacek Anaszewski:
 "New LED class driver:
   - add a driver for PC Engines APU/APU2 LEDs

  New LED trigger:
   - add a system activity LED trigger

  LED core improvements:
   - replace flags bit shift with BIT() macros

  Convert timers to use timer_setup() in:
   - led-core
   - ledtrig-activity
   - ledtrig-heartbeat
   - ledtrig-transient

  LED class drivers fixes:
   - lp55xx: fix spelling mistake: 'cound' -> 'could'
   - tca6507: Remove unnecessary reg check
   - pca955x: Don't invert requested value in pca955x_gpio_set_value()

  LED documentation improvements:
   - update 00-INDEX file"

* tag 'leds_for_4.15rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/j.anaszewski/linux-leds:
  leds: Add driver for PC Engines APU/APU2 LEDs
  leds: lp55xx: fix spelling mistake: 'cound' -> 'could'
  leds: Convert timers to use timer_setup()
  Documentation: leds: Update 00-INDEX file
  leds: tca6507: Remove unnecessary reg check
  leds: ledtrig-heartbeat: Convert timers to use timer_setup()
  leds: Replace flags bit shift with BIT() macros
  leds: pca955x: Don't invert requested value in pca955x_gpio_set_value()
  leds: ledtrig-activity: Add a system activity LED trigger

14 files changed:
Documentation/leds/00-INDEX
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/led-core.c
drivers/leds/leds-apu.c [new file with mode: 0644]
drivers/leds/leds-lp5523.c
drivers/leds/leds-pca955x.c
drivers/leds/leds-tca6507.c
drivers/leds/trigger/Kconfig
drivers/leds/trigger/Makefile
drivers/leds/trigger/ledtrig-activity.c [new file with mode: 0644]
drivers/leds/trigger/ledtrig-heartbeat.c
drivers/leds/trigger/ledtrig-transient.c
include/linux/leds.h

index b4ef1f34e25faafe544971f619019d748538e4de..ae626b29a7402352c94b7c9beea69ea96e6a25ec 100644 (file)
@@ -4,6 +4,10 @@ leds-blinkm.txt
        - Driver for BlinkM LED-devices.
 leds-class.txt
        - documents LED handling under Linux.
+leds-class-flash.txt
+       - documents flash LED handling under Linux.
+leds-lm3556.txt
+       - notes on how to use the leds-lm3556 driver.
 leds-lp3944.txt
        - notes on how to use the leds-lp3944 driver.
 leds-lp5521.txt
@@ -16,7 +20,13 @@ leds-lp55xx.txt
        - description about lp55xx common driver.
 leds-lm3556.txt
        - notes on how to use the leds-lm3556 driver.
+leds-mlxcpld.txt
+       - notes on how to use the leds-mlxcpld driver.
 ledtrig-oneshot.txt
        - One-shot LED trigger for both sporadic and dense events.
 ledtrig-transient.txt
        - LED Transient Trigger, one shot timer activation.
+ledtrig-usbport.txt
+       - notes on how to use the drivers/usb/core/ledtrig-usbport.c trigger.
+uleds.txt
+       - notes on how to use the uleds driver.
index 52ea34e337cd299b33811b91ce7823dadb0de996..318a28fd58feccf9f73814be372227bfb6456ba1 100644 (file)
@@ -57,6 +57,16 @@ config LEDS_AAT1290
        depends on PINCTRL
        help
         This option enables support for the LEDs on the AAT1290.
+config LEDS_APU
+       tristate "Front panel LED support for PC Engines APU/APU2 boards"
+       depends on LEDS_CLASS
+       depends on X86 && DMI
+       help
+         This driver makes the PC Engines APU/APU2 front panel LEDs
+         accessible from userspace programs through the LED subsystem.
+
+         To compile this driver as a module, choose M here: the
+         module will be called leds-apu.
 
 config LEDS_AS3645A
        tristate "AS3645A LED flash controller support"
index 35980450db9b1e2c215178a56b62dc51d09d919a..a2a6b5a4f86d120e9d1857c6a6c196f91d3f717e 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_LEDS_TRIGGERS)             += led-triggers.o
 # LED Platform Drivers
 obj-$(CONFIG_LEDS_88PM860X)            += leds-88pm860x.o
 obj-$(CONFIG_LEDS_AAT1290)             += leds-aat1290.o
+obj-$(CONFIG_LEDS_APU)                 += leds-apu.o
 obj-$(CONFIG_LEDS_AS3645A)             += leds-as3645a.o
 obj-$(CONFIG_LEDS_BCM6328)             += leds-bcm6328.o
 obj-$(CONFIG_LEDS_BCM6358)             += leds-bcm6358.o
index ef13604454133e4d3c5acb7ab2bb3c492cec490c..fd83c7f77a95d4998cff58be3d78ba467faddeab 100644 (file)
@@ -45,9 +45,9 @@ static int __led_set_brightness_blocking(struct led_classdev *led_cdev,
        return led_cdev->brightness_set_blocking(led_cdev, value);
 }
 
-static void led_timer_function(unsigned long data)
+static void led_timer_function(struct timer_list *t)
 {
-       struct led_classdev *led_cdev = (void *)data;
+       struct led_classdev *led_cdev = from_timer(led_cdev, t, blink_timer);
        unsigned long brightness;
        unsigned long delay;
 
@@ -178,8 +178,7 @@ void led_init_core(struct led_classdev *led_cdev)
 {
        INIT_WORK(&led_cdev->set_brightness_work, set_brightness_delayed);
 
-       setup_timer(&led_cdev->blink_timer, led_timer_function,
-                   (unsigned long)led_cdev);
+       timer_setup(&led_cdev->blink_timer, led_timer_function, 0);
 }
 EXPORT_SYMBOL_GPL(led_init_core);
 
diff --git a/drivers/leds/leds-apu.c b/drivers/leds/leds-apu.c
new file mode 100644 (file)
index 0000000..74820aa
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * drivers/leds/leds-apu.c
+ * Copyright (C) 2017 Alan Mizrahi, alan at mizrahi dot com dot ve
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the names of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") version 2 as published by the Free
+ * Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#define APU1_FCH_ACPI_MMIO_BASE 0xFED80000
+#define APU1_FCH_GPIO_BASE      (APU1_FCH_ACPI_MMIO_BASE + 0x01BD)
+#define APU1_LEDON              0x08
+#define APU1_LEDOFF             0xC8
+#define APU1_NUM_GPIO           3
+#define APU1_IOSIZE             sizeof(u8)
+
+#define APU2_FCH_ACPI_MMIO_BASE 0xFED80000
+#define APU2_FCH_GPIO_BASE      (APU2_FCH_ACPI_MMIO_BASE + 0x1500)
+#define APU2_GPIO_BIT_WRITE     22
+#define APU2_APU2_NUM_GPIO      4
+#define APU2_IOSIZE             sizeof(u32)
+
+/* LED access parameters */
+struct apu_param {
+       void __iomem *addr; /* for ioread/iowrite */
+};
+
+/* LED private data */
+struct apu_led_priv {
+       struct led_classdev cdev;
+       struct apu_param param;
+};
+#define cdev_to_priv(c) container_of(c, struct apu_led_priv, cdev)
+
+/* LED profile */
+struct apu_led_profile {
+       const char *name;
+       enum led_brightness brightness;
+       unsigned long offset; /* for devm_ioremap */
+};
+
+/* Supported platform types */
+enum apu_led_platform_types {
+       APU1_LED_PLATFORM,
+       APU2_LED_PLATFORM,
+};
+
+struct apu_led_pdata {
+       struct platform_device *pdev;
+       struct apu_led_priv *pled;
+       const struct apu_led_profile *profile;
+       enum apu_led_platform_types platform;
+       int num_led_instances;
+       int iosize; /* for devm_ioremap() */
+       spinlock_t lock;
+};
+
+static struct apu_led_pdata *apu_led;
+
+static const struct apu_led_profile apu1_led_profile[] = {
+       { "apu:green:1", LED_ON,  APU1_FCH_GPIO_BASE + 0 * APU1_IOSIZE },
+       { "apu:green:2", LED_OFF, APU1_FCH_GPIO_BASE + 1 * APU1_IOSIZE },
+       { "apu:green:3", LED_OFF, APU1_FCH_GPIO_BASE + 2 * APU1_IOSIZE },
+};
+
+static const struct apu_led_profile apu2_led_profile[] = {
+       { "apu2:green:1", LED_ON,  APU2_FCH_GPIO_BASE + 68 * APU2_IOSIZE },
+       { "apu2:green:2", LED_OFF, APU2_FCH_GPIO_BASE + 69 * APU2_IOSIZE },
+       { "apu2:green:3", LED_OFF, APU2_FCH_GPIO_BASE + 70 * APU2_IOSIZE },
+};
+
+static const struct dmi_system_id apu_led_dmi_table[] __initconst = {
+       {
+               .ident = "apu",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+                       DMI_MATCH(DMI_PRODUCT_NAME, "APU")
+               }
+       },
+       {
+               .ident = "apu2",
+               .matches = {
+                       DMI_MATCH(DMI_SYS_VENDOR, "PC Engines"),
+                       DMI_MATCH(DMI_BOARD_NAME, "APU2")
+               }
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(dmi, apu_led_dmi_table);
+
+static void apu1_led_brightness_set(struct led_classdev *led, enum led_brightness value)
+{
+       struct apu_led_priv *pled = cdev_to_priv(led);
+
+       spin_lock(&apu_led->lock);
+       iowrite8(value ? APU1_LEDON : APU1_LEDOFF, pled->param.addr);
+       spin_unlock(&apu_led->lock);
+}
+
+static void apu2_led_brightness_set(struct led_classdev *led, enum led_brightness value)
+{
+       struct apu_led_priv *pled = cdev_to_priv(led);
+       u32 value_new;
+
+       spin_lock(&apu_led->lock);
+
+       value_new = ioread32(pled->param.addr);
+
+       if (value)
+               value_new &= ~BIT(APU2_GPIO_BIT_WRITE);
+       else
+               value_new |= BIT(APU2_GPIO_BIT_WRITE);
+
+       iowrite32(value_new, pled->param.addr);
+
+       spin_unlock(&apu_led->lock);
+}
+
+static int apu_led_config(struct device *dev, struct apu_led_pdata *apuld)
+{
+       int i;
+       int err;
+
+       apu_led->pled = devm_kzalloc(dev,
+               sizeof(struct apu_led_priv) * apu_led->num_led_instances,
+               GFP_KERNEL);
+
+       if (!apu_led->pled)
+               return -ENOMEM;
+
+       for (i = 0; i < apu_led->num_led_instances; i++) {
+               struct apu_led_priv *pled = &apu_led->pled[i];
+               struct led_classdev *led_cdev = &pled->cdev;
+
+               led_cdev->name = apu_led->profile[i].name;
+               led_cdev->brightness = apu_led->profile[i].brightness;
+               led_cdev->max_brightness = 1;
+               led_cdev->flags = LED_CORE_SUSPENDRESUME;
+               if (apu_led->platform == APU1_LED_PLATFORM)
+                       led_cdev->brightness_set = apu1_led_brightness_set;
+               else if (apu_led->platform == APU2_LED_PLATFORM)
+                       led_cdev->brightness_set = apu2_led_brightness_set;
+
+               pled->param.addr = devm_ioremap(dev,
+                               apu_led->profile[i].offset, apu_led->iosize);
+               if (!pled->param.addr) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+
+               err = led_classdev_register(dev, led_cdev);
+               if (err)
+                       goto error;
+
+               led_cdev->brightness_set(led_cdev, apu_led->profile[i].brightness);
+       }
+
+       return 0;
+
+error:
+       while (i-- > 0)
+               led_classdev_unregister(&apu_led->pled[i].cdev);
+
+       return err;
+}
+
+static int __init apu_led_probe(struct platform_device *pdev)
+{
+       apu_led = devm_kzalloc(&pdev->dev, sizeof(*apu_led), GFP_KERNEL);
+
+       if (!apu_led)
+               return -ENOMEM;
+
+       apu_led->pdev = pdev;
+
+       if (dmi_match(DMI_BOARD_NAME, "APU")) {
+               apu_led->profile = apu1_led_profile;
+               apu_led->platform = APU1_LED_PLATFORM;
+               apu_led->num_led_instances = ARRAY_SIZE(apu1_led_profile);
+               apu_led->iosize = APU1_IOSIZE;
+       } else if (dmi_match(DMI_BOARD_NAME, "APU2")) {
+               apu_led->profile = apu2_led_profile;
+               apu_led->platform = APU2_LED_PLATFORM;
+               apu_led->num_led_instances = ARRAY_SIZE(apu2_led_profile);
+               apu_led->iosize = APU2_IOSIZE;
+       }
+
+       spin_lock_init(&apu_led->lock);
+       return apu_led_config(&pdev->dev, apu_led);
+}
+
+static struct platform_driver apu_led_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+       },
+};
+
+static int __init apu_led_init(void)
+{
+       struct platform_device *pdev;
+       int err;
+
+       if (!dmi_match(DMI_SYS_VENDOR, "PC Engines")) {
+               pr_err("No PC Engines board detected\n");
+               return -ENODEV;
+       }
+       if (!(dmi_match(DMI_PRODUCT_NAME, "APU") || dmi_match(DMI_PRODUCT_NAME, "APU2"))) {
+               pr_err("Unknown PC Engines board: %s\n",
+                               dmi_get_system_info(DMI_PRODUCT_NAME));
+               return -ENODEV;
+       }
+
+       pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0);
+       if (IS_ERR(pdev)) {
+               pr_err("Device allocation failed\n");
+               return PTR_ERR(pdev);
+       }
+
+       err = platform_driver_probe(&apu_led_driver, apu_led_probe);
+       if (err) {
+               pr_err("Probe platform driver failed\n");
+               platform_device_unregister(pdev);
+       }
+
+       return err;
+}
+
+static void __exit apu_led_exit(void)
+{
+       int i;
+
+       for (i = 0; i < apu_led->num_led_instances; i++)
+               led_classdev_unregister(&apu_led->pled[i].cdev);
+
+       platform_device_unregister(apu_led->pdev);
+       platform_driver_unregister(&apu_led_driver);
+}
+
+module_init(apu_led_init);
+module_exit(apu_led_exit);
+
+MODULE_AUTHOR("Alan Mizrahi");
+MODULE_DESCRIPTION("PC Engines APU family LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:leds_apu");
index 924e50aefb0039ec88f4dfcec1ff78d4ac8f42b8..52b6f529e27802e441cc146fd500092d4032deaf 100644 (file)
@@ -323,7 +323,7 @@ static int lp5523_init_program_engine(struct lp55xx_chip *chip)
 
        if (status != LP5523_ENG_STATUS_MASK) {
                dev_err(&chip->cl->dev,
-                       "cound not configure LED engine, status = 0x%.2x\n",
+                       "could not configure LED engine, status = 0x%.2x\n",
                        status);
                ret = -1;
        }
index 905729191d3e9b4fab2a6141a621d03482cea395..78183f90820eac0e424315ba75302dac62a48ca7 100644 (file)
 #define PCA955X_LS_BLINK0      0x2     /* Blink at PWM0 rate */
 #define PCA955X_LS_BLINK1      0x3     /* Blink at PWM1 rate */
 
+#define PCA955X_GPIO_INPUT     LED_OFF
+#define PCA955X_GPIO_HIGH      LED_OFF
+#define PCA955X_GPIO_LOW       LED_FULL
+
 enum pca955x_type {
        pca9550,
        pca9551,
@@ -329,9 +333,9 @@ static int pca955x_set_value(struct gpio_chip *gc, unsigned int offset,
        struct pca955x_led *led = &pca955x->leds[offset];
 
        if (val)
-               return pca955x_led_set(&led->led_cdev, LED_FULL);
-       else
-               return pca955x_led_set(&led->led_cdev, LED_OFF);
+               return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_HIGH);
+
+       return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_LOW);
 }
 
 static void pca955x_gpio_set_value(struct gpio_chip *gc, unsigned int offset,
@@ -355,8 +359,11 @@ static int pca955x_gpio_get_value(struct gpio_chip *gc, unsigned int offset)
 static int pca955x_gpio_direction_input(struct gpio_chip *gc,
                                        unsigned int offset)
 {
-       /* To use as input ensure pin is not driven */
-       return pca955x_set_value(gc, offset, 0);
+       struct pca955x *pca955x = gpiochip_get_data(gc);
+       struct pca955x_led *led = &pca955x->leds[offset];
+
+       /* To use as input ensure pin is not driven. */
+       return pca955x_led_set(&led->led_cdev, PCA955X_GPIO_INPUT);
 }
 
 static int pca955x_gpio_direction_output(struct gpio_chip *gc,
index 45222a7f4f75636d628596d90d9bdb29acf0b453..c12c16fb1b9ce811f3dd7401bfff43eb0f9bc62c 100644 (file)
@@ -715,7 +715,7 @@ tca6507_led_dt_init(struct i2c_client *client)
                if (of_property_match_string(child, "compatible", "gpio") >= 0)
                        led.flags |= TCA6507_MAKE_GPIO;
                ret = of_property_read_u32(child, "reg", &reg);
-               if (ret != 0 || reg < 0 || reg >= NUM_LEDS)
+               if (ret != 0 || reg >= NUM_LEDS)
                        continue;
 
                tca_leds[reg] = led;
index 3f9ddb9fafa77f1f1fe5535c5e947838761e4f35..bb090216b4dc338d421e14f5714ca615f26276c6 100644 (file)
@@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
 
          If unsure, say N.
 
+config LEDS_TRIGGER_ACTIVITY
+       tristate "LED activity Trigger"
+       depends on LEDS_TRIGGERS
+       help
+         This allows LEDs to be controlled by a immediate CPU usage.
+         The flash frequency and duty cycle varies from faint flashes to
+         intense brightness depending on the instant CPU load.
+         If unsure, say N.
+
 config LEDS_TRIGGER_GPIO
        tristate "LED GPIO Trigger"
        depends on LEDS_TRIGGERS
index 9f2e868811e29d587cf82b25d42025990112d80f..4a8b6cff776171756e0e9cf4d8593be4b2963718 100644 (file)
@@ -7,6 +7,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)    += ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)   += ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)                += ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_CPU)         += ledtrig-cpu.o
+obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)    += ledtrig-activity.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)  += ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)   += ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)      += ledtrig-camera.o
diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
new file mode 100644 (file)
index 0000000..5081894
--- /dev/null
@@ -0,0 +1,275 @@
+/*
+ * Activity LED trigger
+ *
+ * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
+ * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/reboot.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include "../leds.h"
+
+static int panic_detected;
+
+struct activity_data {
+       struct timer_list timer;
+       struct led_classdev *led_cdev;
+       u64 last_used;
+       u64 last_boot;
+       int time_left;
+       int state;
+       int invert;
+};
+
+static void led_activity_function(struct timer_list *t)
+{
+       struct activity_data *activity_data = from_timer(activity_data, t,
+                                                        timer);
+       struct led_classdev *led_cdev = activity_data->led_cdev;
+       struct timespec boot_time;
+       unsigned int target;
+       unsigned int usage;
+       int delay;
+       u64 curr_used;
+       u64 curr_boot;
+       s32 diff_used;
+       s32 diff_boot;
+       int cpus;
+       int i;
+
+       if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
+               led_cdev->blink_brightness = led_cdev->new_blink_brightness;
+
+       if (unlikely(panic_detected)) {
+               /* full brightness in case of panic */
+               led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness);
+               return;
+       }
+
+       get_monotonic_boottime(&boot_time);
+
+       cpus = 0;
+       curr_used = 0;
+
+       for_each_possible_cpu(i) {
+               curr_used += kcpustat_cpu(i).cpustat[CPUTIME_USER]
+                         +  kcpustat_cpu(i).cpustat[CPUTIME_NICE]
+                         +  kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]
+                         +  kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]
+                         +  kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
+               cpus++;
+       }
+
+       /* We come here every 100ms in the worst case, so that's 100M ns of
+        * cumulated time. By dividing by 2^16, we get the time resolution
+        * down to 16us, ensuring we won't overflow 32-bit computations below
+        * even up to 3k CPUs, while keeping divides cheap on smaller systems.
+        */
+       curr_boot = timespec_to_ns(&boot_time) * cpus;
+       diff_boot = (curr_boot - activity_data->last_boot) >> 16;
+       diff_used = (curr_used - activity_data->last_used) >> 16;
+       activity_data->last_boot = curr_boot;
+       activity_data->last_used = curr_used;
+
+       if (diff_boot <= 0 || diff_used < 0)
+               usage = 0;
+       else if (diff_used >= diff_boot)
+               usage = 100;
+       else
+               usage = 100 * diff_used / diff_boot;
+
+       /*
+        * Now we know the total boot_time multiplied by the number of CPUs, and
+        * the total idle+wait time for all CPUs. We'll compare how they evolved
+        * since last call. The % of overall CPU usage is :
+        *
+        *      1 - delta_idle / delta_boot
+        *
+        * What we want is that when the CPU usage is zero, the LED must blink
+        * slowly with very faint flashes that are detectable but not disturbing
+        * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
+        * blinking frequency to increase up to the point where the load is
+        * enough to saturate one core in multi-core systems or 50% in single
+        * core systems. At this point it should reach 10 Hz with a 10/90 duty
+        * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
+        * remains stable (10 Hz) and only the duty cycle increases to report
+        * the activity, up to the point where we have 90ms ON, 10ms OFF when
+        * all cores are saturated. It's important that the LED never stays in
+        * a steady state so that it's easy to distinguish an idle or saturated
+        * machine from a hung one.
+        *
+        * This gives us :
+        *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
+        *     (10ms ON, 90ms OFF)
+        *   - below target :
+        *      ON_ms  = 10
+        *      OFF_ms = 90 + (1 - usage/target) * 900
+        *   - above target :
+        *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
+        *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
+        *
+        * In order to keep a good responsiveness, we cap the sleep time to
+        * 100 ms and keep track of the sleep time left. This allows us to
+        * quickly change it if needed.
+        */
+
+       activity_data->time_left -= 100;
+       if (activity_data->time_left <= 0) {
+               activity_data->time_left = 0;
+               activity_data->state = !activity_data->state;
+               led_set_brightness_nosleep(led_cdev,
+                       (activity_data->state ^ activity_data->invert) ?
+                       led_cdev->blink_brightness : LED_OFF);
+       }
+
+       target = (cpus > 1) ? (100 / cpus) : 50;
+
+       if (usage < target)
+               delay = activity_data->state ?
+                       10 :                        /* ON  */
+                       990 - 900 * usage / target; /* OFF */
+       else
+               delay = activity_data->state ?
+                       10 + 80 * (usage - target) / (100 - target) : /* ON  */
+                       90 - 80 * (usage - target) / (100 - target);  /* OFF */
+
+
+       if (!activity_data->time_left || delay <= activity_data->time_left)
+               activity_data->time_left = delay;
+
+       delay = min_t(int, activity_data->time_left, 100);
+       mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t led_invert_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct activity_data *activity_data = led_cdev->trigger_data;
+
+       return sprintf(buf, "%u\n", activity_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct activity_data *activity_data = led_cdev->trigger_data;
+       unsigned long state;
+       int ret;
+
+       ret = kstrtoul(buf, 0, &state);
+       if (ret)
+               return ret;
+
+       activity_data->invert = !!state;
+
+       return size;
+}
+
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+
+static void activity_activate(struct led_classdev *led_cdev)
+{
+       struct activity_data *activity_data;
+       int rc;
+
+       activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
+       if (!activity_data)
+               return;
+
+       led_cdev->trigger_data = activity_data;
+       rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+       if (rc) {
+               kfree(led_cdev->trigger_data);
+               return;
+       }
+
+       activity_data->led_cdev = led_cdev;
+       timer_setup(&activity_data->timer, led_activity_function, 0);
+       if (!led_cdev->blink_brightness)
+               led_cdev->blink_brightness = led_cdev->max_brightness;
+       led_activity_function(&activity_data->timer);
+       set_bit(LED_BLINK_SW, &led_cdev->work_flags);
+       led_cdev->activated = true;
+}
+
+static void activity_deactivate(struct led_classdev *led_cdev)
+{
+       struct activity_data *activity_data = led_cdev->trigger_data;
+
+       if (led_cdev->activated) {
+               del_timer_sync(&activity_data->timer);
+               device_remove_file(led_cdev->dev, &dev_attr_invert);
+               kfree(activity_data);
+               clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
+               led_cdev->activated = false;
+       }
+}
+
+static struct led_trigger activity_led_trigger = {
+       .name       = "activity",
+       .activate   = activity_activate,
+       .deactivate = activity_deactivate,
+};
+
+static int activity_reboot_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+       led_trigger_unregister(&activity_led_trigger);
+       return NOTIFY_DONE;
+}
+
+static int activity_panic_notifier(struct notifier_block *nb,
+                                   unsigned long code, void *unused)
+{
+       panic_detected = 1;
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block activity_reboot_nb = {
+       .notifier_call = activity_reboot_notifier,
+};
+
+static struct notifier_block activity_panic_nb = {
+       .notifier_call = activity_panic_notifier,
+};
+
+static int __init activity_init(void)
+{
+       int rc = led_trigger_register(&activity_led_trigger);
+
+       if (!rc) {
+               atomic_notifier_chain_register(&panic_notifier_list,
+                                              &activity_panic_nb);
+               register_reboot_notifier(&activity_reboot_nb);
+       }
+       return rc;
+}
+
+static void __exit activity_exit(void)
+{
+       unregister_reboot_notifier(&activity_reboot_nb);
+       atomic_notifier_chain_unregister(&panic_notifier_list,
+                                        &activity_panic_nb);
+       led_trigger_unregister(&activity_led_trigger);
+}
+
+module_init(activity_init);
+module_exit(activity_exit);
+
+MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
+MODULE_DESCRIPTION("Activity LED trigger");
+MODULE_LICENSE("GPL");
index e95ea65380c864b5aef90c9eec0bb000b4b51086..f0896de410b87c700c1200c32eb44f8d907bde02 100644 (file)
 static int panic_heartbeats;
 
 struct heartbeat_trig_data {
+       struct led_classdev *led_cdev;
        unsigned int phase;
        unsigned int period;
        struct timer_list timer;
        unsigned int invert;
 };
 
-static void led_heartbeat_function(unsigned long data)
+static void led_heartbeat_function(struct timer_list *t)
 {
-       struct led_classdev *led_cdev = (struct led_classdev *) data;
-       struct heartbeat_trig_data *heartbeat_data = led_cdev->trigger_data;
+       struct heartbeat_trig_data *heartbeat_data =
+               from_timer(heartbeat_data, t, timer);
+       struct led_classdev *led_cdev;
        unsigned long brightness = LED_OFF;
        unsigned long delay = 0;
 
+       led_cdev = heartbeat_data->led_cdev;
+
        if (unlikely(panic_heartbeats)) {
                led_set_brightness_nosleep(led_cdev, LED_OFF);
                return;
@@ -127,18 +131,18 @@ static void heartbeat_trig_activate(struct led_classdev *led_cdev)
                return;
 
        led_cdev->trigger_data = heartbeat_data;
+       heartbeat_data->led_cdev = led_cdev;
        rc = device_create_file(led_cdev->dev, &dev_attr_invert);
        if (rc) {
                kfree(led_cdev->trigger_data);
                return;
        }
 
-       setup_timer(&heartbeat_data->timer,
-                   led_heartbeat_function, (unsigned long) led_cdev);
+       timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
        heartbeat_data->phase = 0;
        if (!led_cdev->blink_brightness)
                led_cdev->blink_brightness = led_cdev->max_brightness;
-       led_heartbeat_function(heartbeat_data->timer.data);
+       led_heartbeat_function(&heartbeat_data->timer);
        set_bit(LED_BLINK_SW, &led_cdev->work_flags);
        led_cdev->activated = true;
 }
index 7e6011bd36465517fc6840e7eae2b818aa0c9b12..7acce64b692ad4d0826fe18856cd525b76fc0c07 100644 (file)
@@ -33,12 +33,14 @@ struct transient_trig_data {
        int restore_state;
        unsigned long duration;
        struct timer_list timer;
+       struct led_classdev *led_cdev;
 };
 
-static void transient_timer_function(unsigned long data)
+static void transient_timer_function(struct timer_list *t)
 {
-       struct led_classdev *led_cdev = (struct led_classdev *) data;
-       struct transient_trig_data *transient_data = led_cdev->trigger_data;
+       struct transient_trig_data *transient_data =
+               from_timer(transient_data, t, timer);
+       struct led_classdev *led_cdev = transient_data->led_cdev;
 
        transient_data->activate = 0;
        led_set_brightness_nosleep(led_cdev, transient_data->restore_state);
@@ -169,6 +171,7 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
                return;
        }
        led_cdev->trigger_data = tdata;
+       tdata->led_cdev = led_cdev;
 
        rc = device_create_file(led_cdev->dev, &dev_attr_activate);
        if (rc)
@@ -182,8 +185,7 @@ static void transient_trig_activate(struct led_classdev *led_cdev)
        if (rc)
                goto err_out_state;
 
-       setup_timer(&tdata->timer, transient_timer_function,
-                   (unsigned long) led_cdev);
+       timer_setup(&tdata->timer, transient_timer_function, 0);
        led_cdev->activated = true;
 
        return;
index bf6db4fe895bcd67e04ee65e8f76ea104af6299f..5579c64c8fd62e63d4e6a28dfd29fc6418dfcfe9 100644 (file)
@@ -40,16 +40,16 @@ struct led_classdev {
        int                      flags;
 
        /* Lower 16 bits reflect status */
-#define LED_SUSPENDED          (1 << 0)
-#define LED_UNREGISTERING      (1 << 1)
+#define LED_SUSPENDED          BIT(0)
+#define LED_UNREGISTERING      BIT(1)
        /* Upper 16 bits reflect control information */
-#define LED_CORE_SUSPENDRESUME (1 << 16)
-#define LED_SYSFS_DISABLE      (1 << 17)
-#define LED_DEV_CAP_FLASH      (1 << 18)
-#define LED_HW_PLUGGABLE       (1 << 19)
-#define LED_PANIC_INDICATOR    (1 << 20)
-#define LED_BRIGHT_HW_CHANGED  (1 << 21)
-#define LED_RETAIN_AT_SHUTDOWN (1 << 22)
+#define LED_CORE_SUSPENDRESUME BIT(16)
+#define LED_SYSFS_DISABLE      BIT(17)
+#define LED_DEV_CAP_FLASH      BIT(18)
+#define LED_HW_PLUGGABLE       BIT(19)
+#define LED_PANIC_INDICATOR    BIT(20)
+#define LED_BRIGHT_HW_CHANGED  BIT(21)
+#define LED_RETAIN_AT_SHUTDOWN BIT(22)
 
        /* set_brightness_work / blink_timer flags, atomic, private. */
        unsigned long           work_flags;