summaryrefslogtreecommitdiff
path: root/packages/linux/ixp4xx-kernel/2.6.15
diff options
context:
space:
mode:
authorJohn Bowler <jbowler@nslu2-linux.org>2006-02-05 06:09:57 +0000
committerOpenEmbedded Project <openembedded-devel@lists.openembedded.org>2006-02-05 06:09:57 +0000
commit58d8ffa2cfd134d912985cb9fe41727b74461ab0 (patch)
tree9e131f81151d533183204c1fdc6f5b5c95b38e9f /packages/linux/ixp4xx-kernel/2.6.15
parent858581d2eaddc72696473470bc66bc0a3245aea2 (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.patch151
-rw-r--r--packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch783
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);