diff options
author | John Bowler <jbowler@nslu2-linux.org> | 2006-02-05 06:09:57 +0000 |
---|---|---|
committer | OpenEmbedded Project <openembedded-devel@lists.openembedded.org> | 2006-02-05 06:09:57 +0000 |
commit | 58d8ffa2cfd134d912985cb9fe41727b74461ab0 (patch) | |
tree | 9e131f81151d533183204c1fdc6f5b5c95b38e9f /packages/linux/ixp4xx-kernel/2.6.15 | |
parent | 858581d2eaddc72696473470bc66bc0a3245aea2 (diff) |
ixp4xx-kernel: update to new LEDs code in 2.6.15.2
- the meanings of various fields in /sys/class/leds/* have changed,
slugos-init is updated to take account of these in 0.10
Diffstat (limited to 'packages/linux/ixp4xx-kernel/2.6.15')
-rw-r--r-- | packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch | 151 | ||||
-rw-r--r-- | packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch | 783 |
2 files changed, 651 insertions, 283 deletions
diff --git a/packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch b/packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch new file mode 100644 index 0000000000..c44ef62ee7 --- /dev/null +++ b/packages/linux/ixp4xx-kernel/2.6.15/950-leds-timer.patch @@ -0,0 +1,151 @@ +Fix for a bug in led-triggers.c plus an update to the +timer trigger code to allow for fractional frequency +values and to correct the evaluation of frequency so +that it is the actual frequency. + +Signed-off-by: John Bowler <jbowler@acm.org> + +--- linux-2.6.15/drivers/leds/led-triggers.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.15/drivers/leds/led-triggers.c 1970-01-01 00:00:00.000000000 +0000 +@@ -98,7 +98,7 @@ void led_trigger_event(struct led_trigge + if (!trigger) + return; + +- read_lock(&trigger->led_devs); ++ read_lock(&trigger->leddev_list_lock); + list_for_each(entry, &trigger->led_devs) { + struct led_device *led_dev; + +@@ -107,7 +107,7 @@ void led_trigger_event(struct led_trigge + led_set_brightness(led_dev, brightness); + write_unlock(&led_dev->lock); + } +- read_unlock(&trigger->led_devs); ++ read_unlock(&trigger->leddev_list_lock); + } + + /* Caller must ensure led_dev->lock held for write */ +--- linux-2.6.15/drivers/leds/ledtrig-timer.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.15/drivers/leds/ledtrig-timer.c 1970-01-01 00:00:00.000000000 +0000 +@@ -12,6 +12,7 @@ + */ + + #include <linux/config.h> ++#include <linux/ctype.h> + #include <linux/module.h> + #include <linux/kernel.h> + #include <linux/init.h> +@@ -61,21 +62,19 @@ static void led_timer_function(unsigned + static void led_timer_setdata(struct led_device *led_dev, unsigned long duty, unsigned long frequency) + { + struct timer_trig_data *timer_data = led_dev->trigger_data; +- signed long duty1; +- +- if (frequency > 500) +- frequency = 500; + + if (duty > 100) + duty = 100; + +- duty1 = duty - 50; +- + timer_data->duty = duty; + timer_data->frequency = frequency; + if (frequency != 0) { +- timer_data->delay_on = (50 - duty1) * 1000 / 50 / frequency; +- timer_data->delay_off = (50 + duty1) * 1000 / 50 / frequency; ++ timer_data->delay_on = duty * 10000U / frequency; ++ if (timer_data->delay_on == 0) ++ timer_data->delay_on = 1; ++ timer_data->delay_off = (100U-duty) * 10000U / frequency; ++ if (timer_data->delay_off == 0) ++ timer_data->delay_off = 1; + } + + mod_timer(&timer_data->timer, jiffies); +@@ -100,8 +99,8 @@ static ssize_t led_duty_store(struct cla + struct timer_trig_data *timer_data; + int ret = -EINVAL; + char *after; +- + unsigned long state = simple_strtoul(buf, &after, 10); ++ + if (after - buf > 0) { + ret = after - buf; + write_lock(&led_dev->lock); +@@ -118,32 +117,63 @@ static ssize_t led_frequency_show(struct + { + struct led_device *led_dev = dev->class_data; + struct timer_trig_data *timer_data; ++ unsigned long freq000; + + read_lock(&led_dev->lock); + timer_data = led_dev->trigger_data; +- sprintf(buf, "%lu\n", timer_data->frequency); ++ freq000 = timer_data->frequency; + read_unlock(&led_dev->lock); + ++ if (freq000 % 1000) ++ sprintf(buf, "%lu.%.3lu\n", freq000 / 1000, freq000 % 1000); ++ else ++ sprintf(buf, "%lu\n", freq000 / 1000); ++ + return strlen(buf) + 1; + } + + static ssize_t led_frequency_store(struct class_device *dev, const char *buf, size_t size) + { + struct led_device *led_dev = dev->class_data; +- struct timer_trig_data *timer_data; +- int ret = -EINVAL; +- char *after; ++ size_t rc = 0; ++ unsigned long freq000 = 0; ++ int have_digit = 0; ++ ++ while (rc < size && isspace(buf[rc])) ++ ++rc; ++ if (rc >= size) ++ return rc; ++ ++ /* number before decimal point */ ++ while (rc < size && isdigit(buf[rc])) ++ freq000 *= 10, freq000 += buf[rc++] - '0', have_digit = 1; ++ ++ if (rc < size && (buf[rc] == '.' || buf[rc] == ',')) { ++ int dp = 0; ++ ++rc; ++ while (rc < size && isdigit(buf[rc])) { ++ if (++dp <= 3) ++ freq000 *= 10, freq000 += buf[rc] - '0'; ++ ++rc; ++ have_digit = 1; ++ } ++ while (++dp <= 3) ++ freq000 *= 10; ++ } else ++ freq000 *= 1000; + +- unsigned long state = simple_strtoul(buf, &after, 10); +- if (after - buf > 0) { +- ret = after - buf; +- write_lock(&led_dev->lock); +- timer_data = led_dev->trigger_data; +- led_timer_setdata(led_dev, timer_data->duty, state); +- write_unlock(&led_dev->lock); ++ if (!have_digit) ++ return -EINVAL; ++ ++ write_lock(&led_dev->lock); ++ { ++ struct timer_trig_data *timer_data = led_dev->trigger_data; ++ led_timer_setdata(led_dev, timer_data->duty, freq000); + } ++ write_unlock(&led_dev->lock); + +- return ret; ++ ++ return rc; + } + + static CLASS_DEVICE_ATTR(duty, 0644, led_duty_show, led_duty_store); diff --git a/packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch b/packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch index adfd659ab6..b6b3897005 100644 --- a/packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch +++ b/packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch @@ -1,16 +1,12 @@ This patches the new LEDs code to add cpu activity and inactivity triggers to the timer triggers. The new triggers -flash an LED at a given rate when the CPU is active and -set it to on (cpu-idle) or off (cpu-activity trigger) when -the CPU is idle. - -The patch also adds a duty_cycle attribute to the LED -timer class, this allows control of the mark/space ratio -in the flash. Using duty cycles of about 50% far higher -flash rates become possible. +set the LED with the trigger to different states (on, +flashing or off) according to whether or not all CPUs +are idle. Signed-off-by: John Bowler <jbowler@acm.org> +diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/arch/arm/kernel/process.c linux-2.6.15.2/arch/arm/kernel/process.c --- linux-2.6.15/arch/arm/kernel/process.c 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.15/arch/arm/kernel/process.c 1970-01-01 00:00:00.000000000 +0000 @@ -27,6 +27,7 @@ @@ -21,20 +17,36 @@ Signed-off-by: John Bowler <jbowler@acm.org> #include <asm/system.h> #include <asm/io.h> -@@ -121,8 +122,10 @@ void cpu_idle(void) +@@ -81,6 +82,12 @@ void (*pm_power_off)(void); + EXPORT_SYMBOL(pm_power_off); + + /* ++ * CPU activity indicator. ++ */ ++void (*leds_idle)(int is_idle); ++EXPORT_SYMBOL(leds_idle); ++ ++/* + * This is our default idle handler. We need to disable + * interrupts here to ensure we don't miss a wakeup call. + */ +@@ -121,8 +128,12 @@ void cpu_idle(void) if (!idle) idle = default_idle; leds_event(led_idle_start); -+ leds_idle(1); ++ if (leds_idle) ++ leds_idle(1); while (!need_resched()) idle(); -+ leds_idle(0); ++ if (leds_idle) ++ leds_idle(0); leds_event(led_idle_end); preempt_enable_no_resched(); schedule(); +diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/Kconfig linux-2.6.15.2/drivers/leds/Kconfig --- linux-2.6.15/drivers/leds/Kconfig 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.15/drivers/leds/Kconfig 1970-01-01 00:00:00.000000000 +0000 -@@ -59,5 +59,14 @@ config LEDS_TRIGGER_TIMER +@@ -66,5 +66,14 @@ config LEDS_TRIGGER_TIMER This allows LEDs to be controlled by a programmable timer via sysfs. If unsure, say Y. @@ -49,309 +61,523 @@ Signed-off-by: John Bowler <jbowler@acm.org> + endmenu ---- linux-2.6.15/drivers/leds/led-triggers.c 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.15/drivers/leds/led-triggers.c 1970-01-01 00:00:00.000000000 +0000 -@@ -98,7 +98,7 @@ void led_trigger_event(struct led_trigge - if (!trigger) - return; +diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/Makefile linux-2.6.15.2/drivers/leds/Makefile +--- linux-2.6.15/drivers/leds/Makefile 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.15/drivers/leds/Makefile 1970-01-01 00:00:00.000000000 +0000 +@@ -13,3 +13,4 @@ obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o -- read_lock(&trigger->led_devs); -+ read_lock(&trigger->leddev_list_lock); - list_for_each(entry, &trigger->led_devs) { - struct led_device *led_dev; - -@@ -107,7 +107,7 @@ void led_trigger_event(struct led_trigge - leds_set_brightness(led_dev, brightness); - write_unlock(&led_dev->lock); - } -- read_unlock(&trigger->led_devs); -+ read_unlock(&trigger->leddev_list_lock); - } - - /* Caller must ensure led_dev->lock held for write */ ---- linux-2.6.15/drivers/leds/ledtrig-timer.c 1970-01-01 00:00:00.000000000 +0000 -+++ linux-2.6.15/drivers/leds/ledtrig-timer.c 1970-01-01 00:00:00.000000000 +0000 -@@ -24,46 +24,95 @@ - #include "leds.h" - - struct timer_trig_data { -- unsigned long frequency; /* frequency of blinking, in milliseconds */ -+ unsigned long frequency; /* length of space, in milliseconds */ -+ unsigned long duty_cycle; /* mark/space ratio as a percentage */ - struct timer_list timer; -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ unsigned long idle_brightness; -+ int is_idle; -+#endif + # LED Triggers + obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o ++obj-$(CONFIG_LEDS_TRIGGER_CPU_ACTIVITY) += ledtrig-cpu.o +diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/drivers/leds/ledtrig-cpu.c linux-2.6.15.2/drivers/leds/ledtrig-cpu.c +--- linux-2.6.15/drivers/leds/ledtrig-cpu.c 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.15/drivers/leds/ledtrig-cpu.c 1970-01-01 00:00:00.000000000 +0000 +@@ -0,0 +1,501 @@ ++/* ++ * LEDs CPU activity trigger ++ * ++ * Author: John Bowler <jbowler@acm.org> ++ * ++ * Copyright (c) 2006 John Bowler ++ * ++ * Permission is hereby granted, free of charge, to any ++ * person obtaining a copy of this software and associated ++ * documentation files (the "Software"), to deal in the ++ * Software without restriction, including without ++ * limitation the rights to use, copy, modify, merge, ++ * publish, distribute, sublicense, and/or sell copies of ++ * the Software, and to permit persons to whom the ++ * Software is furnished to do so, subject to the ++ * following conditions: ++ * ++ * The above copyright notice and this permission notice ++ * shall be included in all copies or substantial portions ++ * of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ++ * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED ++ * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A ++ * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT ++ * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ++ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR ++ * OTHER DEALINGS IN THE SOFTWARE. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/ctype.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/spinlock.h> ++#include <linux/timer.h> ++#include <linux/device.h> ++ ++#include <linux/leds.h> ++#include "leds.h" ++ ++//#include <linux/list.h> ++//#include <linux/sysdev.h> ++ ++ ++/* ++ * To simplify this the LED state is given for each case of ++ * CPU state - idle or active. The LED can be: ++ * ++ * off ++ * flash - slow for idle, fast (flicker) for active ++ * on ++ * ++ * This gives two useless states - off/off and on/on ++ */ ++typedef enum cpu_trigger_led_state { ++ cpu_led_off, ++ cpu_led_flash, ++ cpu_led_on, ++ cpu_led_invalid ++} cpu_trigger_led_state; ++ ++static const char *const cpu_trigger_names[] = { ++ "off", ++ "flash", ++ "on", ++ "invalid" +}; + -+enum timer_property { -+ TimerFrequency, -+ TimerDutyCycle - }; - - static void leds_timer_function(unsigned long data) - { - struct led_device *led_dev = (struct led_device *) data; -- struct timer_trig_data *timer_data = led_dev->trigger_data; -- unsigned long value = 0; -+ struct timer_trig_data *timer_data; -+ unsigned long value; - - write_lock(&led_dev->lock); - -+ timer_data = led_dev->trigger_data; -+ -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ if (timer_data->is_idle) { -+ /* LED solid (or or off), no timer. */ -+ value = timer_data->idle_brightness; -+ } else if (!timer_data->frequency) { -+ /* Put the LED in the non-idle state. */ -+ value = 100-timer_data->idle_brightness; -+ } -+#else - if (!timer_data->frequency) { -- leds_set_brightness(led_dev, 0); -- write_unlock(&led_dev->lock); -- return; -+ value = 0; - } -+#endif -+ else { -+ unsigned long timeout = timer_data->frequency; -+ -+ /* LED flashing - toggle the brightness. */ -+ value = led_dev->brightness ? 0 : 100; -+ -+ /* If this is the 'mark' adjust by the duty cycle. */ -+ if (value) -+ timeout = timeout * timer_data->duty_cycle / 100; -+ -+ timeout = msecs_to_jiffies(timeout); -+ if (!timeout) -+ timeout = 1; - -- if (!led_dev->brightness) -- value = 100; -+ mod_timer(&timer_data->timer, jiffies + timeout); ++/* Forward declaration - this is called back when an LED property ++ * is changed. ++ */ ++static void leds_cpu_trigger_state_change(void); ++ ++/* ++ * These constants define the actual mark/space of the flashing ++ * in jiffies. msecs_to_jiffies rounds up and is compile time ++ * evaluable for constant arguments. Writing the ?: stuff below ++ * this way ensures the compiler doesn't think it needs to ++ * compile in the math of msecs_to_jiffies. ++ * ++ * These values have been determined by experiment to work well ++ * for the ready/status LED on a LinkSys NSLU2 (light piped) and ++ * for the user LED on a Loft (Gateway Avila variant) board where ++ * the LED was directly visible. Light Output Varies Everywhere. ++ */ ++#define LEDS_CPU_ACTIVE_MARK msecs_to_jiffies(40) ++#define LEDS_CPU_IDLE_MARK msecs_to_jiffies(800) ++#define LEDS_CPU_ACTIVE_SPACE msecs_to_jiffies(60) ++#define LEDS_CPU_IDLE_SPACE msecs_to_jiffies(800) ++ ++ ++/* ++ * Individual LEDs ------------------------------------------------------------ ++ */ ++struct cpu_trigger_data { ++ cpu_trigger_led_state active; /* Behaviour when the CPU is active. */ ++ cpu_trigger_led_state idle; /* Behaviour when the CPU is idle. */ ++}; ++ ++/* ++ * LED state change - called when the state of a single LED might ++ * have changed. Returns true if the LED is blinking. The argument ++ * is the blink state - the brightness of the blinking LED. ++ */ ++static int leds_cpu_trigger_led_state_change(struct led_device *led, ++ int is_active, enum led_brightness brightness) ++{ ++ int is_blinking = 0; ++ ++ write_lock(&led->lock); ++ { ++ struct cpu_trigger_data *data = led->trigger_data; ++ ++ /* Find the new brightness for the LED, if the LED is ++ * set to flash then the brightness passed in is the ++ * required value. ++ */ ++ if (likely(data != 0)) ++ switch (is_active ? data->active : data->idle) { ++ case cpu_led_off: brightness = LED_OFF; break; ++ case cpu_led_flash: is_blinking = 1; break; ++ case cpu_led_on: brightness = LED_FULL; break; ++ } ++ else ++ brightness = is_active ? LED_FULL : LED_OFF; ++ ++ led_set_brightness(led, brightness); + } - - leds_set_brightness(led_dev, value); -- -- mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(timer_data->frequency)); - write_unlock(&led_dev->lock); - } - --static ssize_t leds_show_frequency(struct class_device *dev, char *buf) -+static ssize_t leds_show_prop(struct class_device *dev, char *buf, enum timer_property what) - { - struct led_device *led_dev = dev->class_data; - struct timer_trig_data *timer_data = led_dev->trigger_data; -+ unsigned long value = 0; - - read_lock(&led_dev->lock); -- sprintf(buf, "%lu\n", timer_data->frequency); -+ switch (what) ++ write_unlock(&led->lock); ++ ++ return is_blinking; ++} ++ ++/* ++ * sysfs properties, the property is output at an list of the ++ * values with the current setting enclosed in [] ++ */ ++static ssize_t leds_cpu_trigger_show_prop(struct class_device *dev, char *buf, ++ size_t where) ++{ ++ struct led_device *led = dev->class_data; ++ cpu_trigger_led_state item = cpu_led_invalid, i; ++ char *next; ++ ++ read_lock(&led->lock); + { -+ case TimerFrequency: value = timer_data->frequency; break; -+ case TimerDutyCycle: value = timer_data->duty_cycle; break; ++ if (likely(led->trigger_data != 0)) ++ item = *(const cpu_trigger_led_state*)( ++ led->trigger_data + where); + } - read_unlock(&led_dev->lock); - -+ sprintf(buf, "%lu\n", value); ++ read_unlock(&led->lock); + - return strlen(buf) + 1; - } - --static ssize_t leds_store_frequency(struct class_device *dev, const char *buf, size_t size) -+static ssize_t leds_show_frequency(struct class_device *dev, char *buf) ++ for (i=0, next=buf; i<cpu_led_invalid; ++i) { ++ const char *name = cpu_trigger_names[i]; ++ size_t len = strlen(name); ++ ++ if (i == item) ++ *next++ = '['; ++ memcpy(next, name, len); ++ next += len; ++ if (i == item) ++ *next++ = ']'; ++ *next++ = ' '; ++ } ++ ++ next[-1] = '\n'; ++ *next++ = 0; ++ ++ return next - buf; ++} ++ ++static ssize_t leds_cpu_trigger_show_active(struct class_device *dev, char *buf) +{ -+ return leds_show_prop(dev, buf, TimerFrequency); ++ return leds_cpu_trigger_show_prop(dev, buf, ++ offsetof(struct cpu_trigger_data, active)); +} + -+static ssize_t leds_show_duty_cycle(struct class_device *dev, char *buf) ++static ssize_t leds_cpu_trigger_show_idle(struct class_device *dev, char *buf) +{ -+ return leds_show_prop(dev, buf, TimerDutyCycle); ++ return leds_cpu_trigger_show_prop(dev, buf, ++ offsetof(struct cpu_trigger_data, idle)); +} + -+static ssize_t leds_store_prop(struct class_device *dev, const char *buf, -+ size_t size, enum timer_property what) - { - struct led_device *led_dev = dev->class_data; - struct timer_trig_data *timer_data = led_dev->trigger_data; -@@ -74,17 +123,33 @@ static ssize_t leds_store_frequency(stru - if (after - buf > 0) { - ret = after - buf; - write_lock(&led_dev->lock); -- timer_data->frequency = state; -- mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(timer_data->frequency)); -+ switch (what) -+ { -+ case TimerFrequency: timer_data->frequency = state; break; -+ case TimerDutyCycle: timer_data->duty_cycle = state; break; ++/* ++ * Any matching leading substring selects a property - so "onoffonoff" ++ * sets the property to off. ++ */ ++static ssize_t leds_cpu_trigger_store_prop(struct class_device *dev, ++ const char *buf, size_t size, size_t where) ++{ ++ size_t rc = 0; ++ cpu_trigger_led_state value = 0/*sic*/; ++ struct led_device *led; ++ ++ /* ignore space characters before the value. */ ++ while (rc < size && isspace(buf[rc])) ++ ++rc; ++ if (rc >= size) ++ return rc; ++ ++ /* look for a simple match against the trigger name, case ++ * sensitive. ++ */ ++ do { ++ const char *name = cpu_trigger_names[value]; ++ size_t len = strlen(name); ++ if (len <= size && memcmp(buf+rc, name, len) == 0) { ++ rc = len; ++ break; + } -+ /* Cause the timer to fire in a jiffy */ -+ mod_timer(&timer_data->timer, jiffies + 1); - write_unlock(&led_dev->lock); - } - - return ret; - } - -+static ssize_t leds_store_frequency(struct class_device *dev, const char *buf, size_t size) ++ if (++value >= cpu_led_invalid) ++ return -EINVAL; ++ } while (1); ++ ++ led = dev->class_data; ++ write_lock(&led->lock); ++ { ++ if (likely(led->trigger_data != 0)) ++ *(cpu_trigger_led_state*)( ++ led->trigger_data + where) = value; ++ ++ } ++ write_unlock(&led->lock); ++ ++ return rc; ++} ++ ++static ssize_t leds_cpu_trigger_store_active(struct class_device *dev, ++ const char *buf, size_t size) +{ -+ return leds_store_prop(dev, buf, size, TimerFrequency); ++ ssize_t rc = leds_cpu_trigger_store_prop(dev, buf, size, ++ offsetof(struct cpu_trigger_data, active)); ++ /* ++ * At least one CPU must be active (otherwise who is doing this?) ++ * Call down into the global state below to cause an update ++ * to happen now. ++ */ ++ leds_cpu_trigger_state_change(); ++ return rc; +} + -+static ssize_t leds_store_duty_cycle(struct class_device *dev, const char *buf, size_t size) ++static ssize_t leds_cpu_trigger_store_idle(struct class_device *dev, ++ const char *buf, size_t size) +{ -+ return leds_store_prop(dev, buf, size, TimerDutyCycle); ++ return leds_cpu_trigger_store_prop(dev, buf, size, ++ offsetof(struct cpu_trigger_data, idle)); +} + - static CLASS_DEVICE_ATTR(frequency, 0644, leds_show_frequency, leds_store_frequency); -+static CLASS_DEVICE_ATTR(duty_cycle, 0644, leds_show_duty_cycle, leds_store_duty_cycle); - --void timer_trig_activate(struct led_device *led_dev) -+static void do_activate(struct led_device *led_dev, unsigned long idle_brightness) - { - struct timer_trig_data *timer_data; - -@@ -94,6 +159,11 @@ void timer_trig_activate(struct led_devi - - led_dev->trigger_data = timer_data; - timer_data->frequency = 0; -+ timer_data->duty_cycle = 100; -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ timer_data->idle_brightness = idle_brightness; -+ timer_data->is_idle = 0; -+#endif - - init_timer(&timer_data->timer); - timer_data->timer.function = leds_timer_function; -@@ -101,12 +171,27 @@ void timer_trig_activate(struct led_devi - timer_data->timer.expires = 0; - - class_device_create_file(led_dev->class_dev, &class_device_attr_frequency); -+ class_device_create_file(led_dev->class_dev, &class_device_attr_duty_cycle); - } - --void timer_trig_deactivate(struct led_device *led_dev) -+static void timer_trig_activate(struct led_device *led_dev) ++static CLASS_DEVICE_ATTR(active, 0644, leds_cpu_trigger_show_active, ++ leds_cpu_trigger_store_active); ++ ++static CLASS_DEVICE_ATTR(idle, 0644, leds_cpu_trigger_show_idle, ++ leds_cpu_trigger_store_idle); ++ ++/* ++ * Activate and deactivate are called on individual LEDs when the ++ * LED trigger property is changed. The LED write lock is held. ++ */ ++static void leds_cpu_trigger_activate(struct led_device *led) +{ -+ do_activate(led_dev, 100); ++ /* ++ * The initial setting of the trigger is simple CPU activity ++ * with the LED off for idle and on for active. Consequently ++ * there is no need to mess with the global state initially, ++ * we know the CPU is active at this moment! ++ */ ++ struct cpu_trigger_data *data = kmalloc(sizeof *data, GFP_KERNEL); ++ if (unlikely(data == 0)) ++ return; ++ ++ data->active = cpu_led_on; ++ data->idle = cpu_led_off; ++ led->trigger_data = data; ++ ++ class_device_create_file(led->class_dev, &class_device_attr_active); ++ class_device_create_file(led->class_dev, &class_device_attr_idle); ++ ++ led_set_brightness(led, LED_FULL); +} + -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+static void cpu_trig_activate(struct led_device *led_dev) ++static void leds_cpu_trigger_deactivate(struct led_device *led) +{ -+ /* As above but the LED is off when the CPU is idle */ -+ do_activate(led_dev, 0); ++ struct cpu_trigger_data *data = led->trigger_data; ++ if (likely(data != 0)) { ++ led_set_brightness(led, LED_OFF); ++ ++ class_device_remove_file(led->class_dev, &class_device_attr_idle); ++ class_device_remove_file(led->class_dev, &class_device_attr_active); ++ ++ led->trigger_data = 0; ++ kfree(data); ++ } +} -+#endif -+ -+static void timer_trig_deactivate(struct led_device *led_dev) - { - struct timer_trig_data *timer_data = led_dev->trigger_data; - if (timer_data) { -+ class_device_remove_file(led_dev->class_dev, &class_device_attr_duty_cycle); - class_device_remove_file(led_dev->class_dev, &class_device_attr_frequency); - del_timer_sync(&timer_data->timer); - kfree(timer_data); -@@ -119,16 +204,90 @@ static struct led_trigger timer_led_trig - .deactivate = timer_trig_deactivate, - }; - -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+static struct led_trigger cpu_led_trigger = { -+ .name = "cpu-activity", -+ .activate = cpu_trig_activate, -+ .deactivate = timer_trig_deactivate, -+}; + -+static struct led_trigger idle_led_trigger = { -+ .name = "cpu-idle", -+ .activate = timer_trig_activate, -+ .deactivate = timer_trig_deactivate, -+}; + -+static int leds_do_idle = 0; -+#endif ++/* ++ * Global state -------------------------------------------------------------- ++ * ++ * This is global because the CPU state is global and we only need one timer to ++ * do this stuff. ++ */ ++typedef struct leds_cpu_trigger_data { ++ struct led_trigger trigger; /* the lock in here protects everything */ ++ struct timer_list timer; ++ unsigned long last_active_time; /* record of last jiffies */ ++ unsigned long last_idle_time; /* record of last jiffies */ ++ int count_active; /* number of active CPUs */ ++} leds_cpu_trigger_data; + - static int __init timer_trig_init(void) - { -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ int rc = led_trigger_register(&idle_led_trigger); -+ if (rc) -+ return rc; -+ rc = led_trigger_register(&cpu_led_trigger); -+ if (rc) -+ return rc; -+ leds_do_idle = 1; -+#endif - return led_trigger_register(&timer_led_trigger); - } - - static void __exit timer_trig_exit (void) - { - led_trigger_unregister(&timer_led_trigger); -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ leds_do_idle = 0; -+ led_trigger_unregister(&cpu_led_trigger); -+ led_trigger_unregister(&idle_led_trigger); -+#endif - } - -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+static void leds_trigger_idle(struct led_trigger *trigger, int is_idle) ++/* ++ * Mark state - uses the current time (jiffies) to work out ++ * whether this is a mark or space. ++ */ ++static int leds_cpu_trigger_mark(struct leds_cpu_trigger_data *data, ++ unsigned long now) { ++ if (data->count_active > 0) { ++ unsigned long elapsed = now - data->last_active_time; ++ elapsed %= LEDS_CPU_ACTIVE_SPACE + LEDS_CPU_ACTIVE_MARK; ++ data->last_active_time = now - elapsed; ++ return elapsed > LEDS_CPU_ACTIVE_SPACE; ++ } else { ++ unsigned long elapsed = now - data->last_idle_time; ++ elapsed %= LEDS_CPU_IDLE_SPACE + LEDS_CPU_IDLE_MARK; ++ data->last_idle_time = now - elapsed; ++ return elapsed > LEDS_CPU_IDLE_SPACE; ++ } ++} ++ ++ ++/* ++ * State change - given information about the nature of the ++ * (possible) state change call up to each LED to adjust its ++ * state. Returns true if any LED is blinking. The lock ++ * must be held (a read lock is adequate). ++ */ ++static int leds_cpu_trigger_scan_leds(struct leds_cpu_trigger_data *data, ++ unsigned long now) +{ ++ int blinking = 0; ++ const int active = data->count_active > 0; ++ const enum led_brightness brightness = ++ leds_cpu_trigger_mark(data, now) ? LED_FULL : LED_OFF; + struct list_head *entry; + -+ if (!trigger) -+ return; ++ list_for_each(entry, &data->trigger.led_devs) { ++ struct led_device *led = ++ list_entry(entry, struct led_device, trig_list); + -+ read_lock(&trigger->leddev_list_lock); -+ list_for_each(entry, &trigger->led_devs) { -+ struct led_device *led_dev; -+ struct timer_trig_data *timer_data; ++ blinking |= leds_cpu_trigger_led_state_change(led, ++ active, brightness); ++ } + -+ /* The timer must be deactivated in this thread if the CPU -+ * is going idle, otherwise this function will simply stop -+ * the CPU ever becoming idle. -+ */ -+ led_dev = list_entry(entry, struct led_device, trig_list); -+ write_lock(&led_dev->lock); -+ timer_data = led_dev->trigger_data; -+ if (is_idle && !timer_data->is_idle && timer_data->frequency) -+ del_timer(&timer_data->timer); -+ timer_data->is_idle = is_idle; -+ write_unlock(&led_dev->lock); -+ -+ /* Force the LED to the correct state and instantiate -+ * a timer if necessary. ++ return blinking; ++} ++ ++/* ++ * Set the timer correctly according to the current state, the lock ++ * must be held for write. ++ */ ++static void leds_cpu_trigger_set_timer(struct leds_cpu_trigger_data *state, ++ unsigned long now) ++{ ++ unsigned long next; ++ if (state->count_active > 0) { ++ next = state->last_active_time; ++ if (now - next > LEDS_CPU_ACTIVE_SPACE) ++ next += LEDS_CPU_ACTIVE_MARK; ++ next += LEDS_CPU_ACTIVE_SPACE; ++ } else { ++ next = state->last_idle_time; ++ if (now - next > LEDS_CPU_IDLE_SPACE) ++ next += LEDS_CPU_IDLE_MARK; ++ next += LEDS_CPU_IDLE_SPACE; ++ } ++ mod_timer(&state->timer, next); ++} ++ ++/* ++ * The timer callback if the LED is currently flashing, the callback ++ * calls the state change function and, if that returns true, meaning ++ * that at least one LED is still blinking, the timer is restarted ++ * with the correct timeout. ++ */ ++static void leds_cpu_trigger_timer_callback(unsigned long data) ++{ ++ struct leds_cpu_trigger_data *state = ++ (struct leds_cpu_trigger_data *)data; ++ ++ write_lock(&state->trigger.leddev_list_lock); ++ { ++ unsigned long now = jiffies; ++ ++ /* If at least one LED is set to flash; set the timer ++ * again (this won't reset the timer set within the ++ * idle loop). + */ -+ leds_timer_function((unsigned long)led_dev); ++ if (leds_cpu_trigger_scan_leds(state, now)) ++ leds_cpu_trigger_set_timer(state, now); ++ } ++ write_unlock(&state->trigger.leddev_list_lock); ++} ++ ++ ++/* ++ * There is one global control structure, one timer and one set ++ * of state for active CPUs shared across all the LEDs. Individual ++ * LEDs say how this state to be handled. It is currently *not* ++ * possible to show per-cpu activity on individual LEDs, the code ++ * maintains a count of active CPUs and the state is only 'idle' ++ * if all CPUs are idle. ++ */ ++static struct leds_cpu_trigger_data leds_cpu_trigger = { ++ .trigger = { ++ .name = "cpu", ++ .activate = leds_cpu_trigger_activate, ++ .deactivate = leds_cpu_trigger_deactivate, ++ } , ++ .timer = TIMER_INITIALIZER(leds_cpu_trigger_timer_callback, 0, ++ (unsigned long)&leds_cpu_trigger), ++ .last_active_time = 0, ++ .last_idle_time = 0, ++ .count_active = 0, ++}; ++ ++/* ++ * State change - callback from an individual LED on a property change which ++ * might require a redisplay. ++ */ ++static void leds_cpu_trigger_state_change() { ++ write_lock(&leds_cpu_trigger.trigger.leddev_list_lock); ++ { ++ unsigned long now = jiffies; ++ ++ if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now) && ++ !timer_pending(&leds_cpu_trigger.timer)) ++ leds_cpu_trigger_set_timer(&leds_cpu_trigger, now); + } -+ read_unlock(&trigger->leddev_list_lock); ++ write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock); +} + -+void leds_idle(int is_idle) ++/* ++ * Called from every CPU at the start and end of the idle loop. ++ * The active count is initially 0, even though CPUs are running, ++ * so the code below must check for the resultant underflow. ++ * ++ * If the idle behaviour is 'flash' then when the timer times out ++ * it will take the CPU out of idle, set the active state (which ++ * may also be flash), drop back into idle and reset the timer to ++ * the idle timeout... ++ */ ++static void leds_cpu_trigger_idle(int is_idle) +{ -+ if (leds_do_idle) { -+ leds_trigger_idle(&cpu_led_trigger, is_idle); -+ leds_trigger_idle(&idle_led_trigger, is_idle); ++ write_lock(&leds_cpu_trigger.trigger.leddev_list_lock); ++ if ((is_idle && leds_cpu_trigger.count_active > 0 && ++ --leds_cpu_trigger.count_active == 0) || ++ (!is_idle && leds_cpu_trigger.count_active < num_online_cpus() && ++ ++leds_cpu_trigger.count_active == 1)) { ++ unsigned long now = jiffies; ++ ++ /* State change - the system just became idle or active, ++ * call the del_timer first in an attempt to minimise ++ * getting a timer interrupt which will take us unnecessarily ++ * out of idle (this doesn't matter). ++ */ ++ del_timer(&leds_cpu_trigger.timer); ++ if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now)) ++ leds_cpu_trigger_set_timer(&leds_cpu_trigger, now); + } ++ write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock); +} + -+EXPORT_SYMBOL_GPL(leds_idle); -+#endif ++/* ++ * Module init and exit - register the trigger, then store ++ * the idle callback in the arch-specific global. For this ++ * module to link (into the kernel) or load (into a running ++ * kernel) the architecture must define the leds_idle global. ++ */ ++static int __init leds_cpu_trigger_init(void) ++{ ++ int rc = led_trigger_register(&leds_cpu_trigger.trigger); ++ leds_idle = leds_cpu_trigger_idle; ++ return rc; ++} ++module_init(leds_cpu_trigger_init); + - module_init(timer_trig_init); - module_exit(timer_trig_exit); - ++static void __exit leds_cpu_trigger_exit(void) ++{ ++ leds_idle = 0; ++ del_timer_sync(&leds_cpu_trigger.timer); ++ led_trigger_unregister(&leds_cpu_trigger.trigger); ++} ++module_exit(leds_cpu_trigger_exit); ++ ++MODULE_AUTHOR("John Bowler <jbowler@acm.org>"); ++MODULE_DESCRIPTION("CPU activity LED trigger"); ++MODULE_LICENSE("MIT"); +diff -rup linux-2.6.15.2/.pc/951-ixp4xx-leds-cpu-activity.patch/include/linux/leds.h linux-2.6.15.2/include/linux/leds.h --- linux-2.6.15/include/linux/leds.h 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.15/include/linux/leds.h 1970-01-01 00:00:00.000000000 +0000 -@@ -89,3 +89,21 @@ void led_trigger_event(struct led_trigge +@@ -95,3 +95,12 @@ void led_trigger_event(struct led_trigge #define led_trigger_event(x, y) do {} while(0) #endif @@ -359,17 +585,8 @@ Signed-off-by: John Bowler <jbowler@acm.org> +/* + * CPU activity indication. + */ -+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY -+ +/* Idle callback - call with is_idle==1 at the start of the idle loop -+ * and with is_idle==0 at the end. ++ * and with is_idle==0 at the end. This symbol must be defined by ++ * the arch core to be able to use LEDS_TRIGGER_CPU_ACTIVITY + */ -+void leds_idle(int is_idle); -+ -+#else -+ -+/* No CPU activity support. */ -+#define leds_idle(x) do {} while (0) -+ -+#endif -+ ++extern void (*leds_idle)(int is_idle); |