Index: linux-2.6.24.3/drivers/leds/Kconfig
===================================================================
--- linux-2.6.24.3.orig/drivers/leds/Kconfig	2008-02-29 13:56:08.000000000 +0100
+++ linux-2.6.24.3/drivers/leds/Kconfig	2008-02-29 13:56:11.000000000 +0100
@@ -130,6 +130,15 @@
 	  This allows LEDs to be controlled by a programmable timer
 	  via sysfs. If unsure, say Y.
 
+config LEDS_TRIGGER_CPU_ACTIVITY
+	tristate "LED CPU Activity Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs to be set to show cpu activity via sysfs.
+	  The LED will blink when the cpu is active and stay steady
+	  (on or off according to the trigger selected) when idle.
+	  Platform support is needed for this to work. If unsure, say Y.
+
 config LEDS_TRIGGER_IDE_DISK
 	bool "LED IDE Disk Trigger"
 	depends on LEDS_TRIGGERS && BLK_DEV_IDEDISK
Index: linux-2.6.24.3/drivers/leds/ledtrig-cpu.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.24.3/drivers/leds/ledtrig-cpu.c	2008-02-29 13:56:11.000000000 +0100
@@ -0,0 +1,502 @@
+/*
+ * 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/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"
+};
+
+/* 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_classdev *led,
+		int is_active, enum led_brightness brightness)
+{
+	int is_blinking = 0;
+
+	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);
+
+	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 device *dev,
+		struct device_attribute *attr, char *buf, size_t where)
+{
+	struct led_classdev     *led = dev_get_drvdata(dev);
+	cpu_trigger_led_state  item = cpu_led_invalid, i;
+	char                  *next;
+
+	if (likely(led->trigger_data != 0))
+		item = *(const cpu_trigger_led_state*)(
+				led->trigger_data + where);
+
+	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 device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return leds_cpu_trigger_show_prop(dev, attr, buf,
+			offsetof(struct cpu_trigger_data, active));
+}
+
+static ssize_t leds_cpu_trigger_show_idle(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return leds_cpu_trigger_show_prop(dev, attr, buf,
+			offsetof(struct cpu_trigger_data, idle));
+}
+
+/*
+ * Any matching leading substring selects a property - so "onoffonoff"
+ * sets the property to off.
+ */
+static ssize_t leds_cpu_trigger_store_prop(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t size, size_t where)
+{
+	size_t rc = 0;
+	cpu_trigger_led_state value = 0/*sic*/;
+	struct led_classdev *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;
+		}
+		if (++value >= cpu_led_invalid)
+			return -EINVAL;
+	} while (1);
+
+	led = dev_get_drvdata(dev);
+	if (likely(led->trigger_data != 0))
+		*(cpu_trigger_led_state*)(
+				led->trigger_data + where) = value;
+
+	return rc;
+}
+
+static ssize_t leds_cpu_trigger_store_active(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	ssize_t rc = leds_cpu_trigger_store_prop(dev, attr, 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_cpu_trigger_store_idle(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	return leds_cpu_trigger_store_prop(dev, attr, buf, size,
+			offsetof(struct cpu_trigger_data, idle));
+}
+
+static DEVICE_ATTR(active, 0644, leds_cpu_trigger_show_active,
+					leds_cpu_trigger_store_active);
+
+static 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.
+ */
+static void leds_cpu_trigger_activate(struct led_classdev *led)
+{
+	/*
+	 * 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!
+	 */
+	int rc;
+	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;
+
+	rc = device_create_file(led->dev, &dev_attr_active);
+	if (rc)
+		goto err_out;
+	rc = device_create_file(led->dev, &dev_attr_idle);
+	if (rc)
+		goto err_out_active;
+
+	led_set_brightness(led, LED_FULL);
+	return;
+
+err_out_active:
+	device_remove_file(led->dev, &dev_attr_active);
+err_out:
+	led->trigger_data = NULL;
+	kfree(data);
+}
+
+static void leds_cpu_trigger_deactivate(struct led_classdev *led)
+{
+	struct cpu_trigger_data *data = led->trigger_data;
+	if (likely(data != 0)) {
+		led_set_brightness(led, LED_OFF);
+
+		device_remove_file(led->dev, &dev_attr_idle);
+		device_remove_file(led->dev, &dev_attr_active);
+
+		led->trigger_data = 0;
+		kfree(data);
+	}
+}
+
+
+/*
+ * 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;
+
+/*
+ * 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;
+
+	list_for_each(entry, &data->trigger.led_cdevs) {
+		struct led_classdev *led =
+			list_entry(entry, struct led_classdev, trig_list);
+
+		blinking |= leds_cpu_trigger_led_state_change(led,
+				active, brightness);
+	}
+
+	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).
+		 */
+		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);
+	}
+	write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock);
+}
+
+/*
+ * 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)
+{
+	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);
+}
+
+/*
+ * 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);
+
+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("Dual MIT/GPL");
Index: linux-2.6.24.3/drivers/leds/Makefile
===================================================================
--- linux-2.6.24.3.orig/drivers/leds/Makefile	2008-02-29 13:56:08.000000000 +0100
+++ linux-2.6.24.3/drivers/leds/Makefile	2008-02-29 13:56:11.000000000 +0100
@@ -24,3 +24,4 @@
 obj-$(CONFIG_LEDS_TRIGGER_TIMER)	+= ledtrig-timer.o
 obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)	+= ledtrig-ide-disk.o
 obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
+obj-$(CONFIG_LEDS_TRIGGER_CPU_ACTIVITY)	+= ledtrig-cpu.o
Index: linux-2.6.24.3/include/linux/leds.h
===================================================================
--- linux-2.6.24.3.orig/include/linux/leds.h	2008-02-29 13:56:08.000000000 +0100
+++ linux-2.6.24.3/include/linux/leds.h	2008-02-29 13:56:11.000000000 +0100
@@ -124,4 +124,13 @@
 };
 
 
+/*
+ * CPU activity indication.
+ */
+/* Idle callback - call with is_idle==1 at the start of the idle loop
+ * 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
+ */
+extern void (*leds_idle)(int is_idle);
+
 #endif		/* __LINUX_LEDS_H_INCLUDED */