summaryrefslogtreecommitdiff
path: root/packages/linux/ixp4xx-kernel/2.6.15/951-ixp4xx-leds-cpu-activity.patch
blob: adfd659ab66e04821ce3dc66ec963fe499e15b0d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
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.

Signed-off-by: John Bowler <jbowler@acm.org>

--- 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 @@
 #include <linux/kallsyms.h>
 #include <linux/init.h>
 #include <linux/cpu.h>
+#include <linux/leds.h>
 
 #include <asm/system.h>
 #include <asm/io.h>
@@ -121,8 +122,10 @@ void cpu_idle(void)
 		if (!idle)
 			idle = default_idle;
 		leds_event(led_idle_start);
+		leds_idle(1);
 		while (!need_resched())
 			idle();
+		leds_idle(0);
 		leds_event(led_idle_end);
 		preempt_enable_no_resched();
 		schedule();
--- 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
 	  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 LEDS_TRIGGER_TIMER
+	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.
+	  If unsure, say Y.
+
 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;
 
-	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
+};
+
+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);
+	}
 
 	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)
+	{
+		case TimerFrequency: value = timer_data->frequency; break;
+		case TimerDutyCycle: value = timer_data->duty_cycle; break;
+	}
 	read_unlock(&led_dev->lock);
 
+	sprintf(buf, "%lu\n", value);
+
 	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)
+{
+	return leds_show_prop(dev, buf, TimerFrequency);
+}
+
+static ssize_t leds_show_duty_cycle(struct class_device *dev, char *buf)
+{
+	return leds_show_prop(dev, buf, TimerDutyCycle);
+}
+
+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;
+		}
+		/* 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)
+{
+	return leds_store_prop(dev, buf, size, TimerFrequency);
+}
+
+static ssize_t leds_store_duty_cycle(struct class_device *dev, const char *buf, size_t size)
+{
+	return leds_store_prop(dev, buf, size, TimerDutyCycle);
+}
+
 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)
+{
+	do_activate(led_dev, 100);
+}
+
+#ifdef CONFIG_LEDS_TRIGGER_CPU_ACTIVITY
+static void cpu_trig_activate(struct led_device *led_dev)
+{
+	/* As above but the LED is off when the CPU is idle */
+	do_activate(led_dev, 0);
+}
+#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
+
 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)
+{
+	struct list_head *entry;
+
+	if (!trigger)
+		return;
+
+	read_lock(&trigger->leddev_list_lock);
+	list_for_each(entry, &trigger->led_devs) {
+		struct led_device *led_dev;
+		struct timer_trig_data *timer_data;
+
+		/* 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.
+		 */
+		leds_timer_function((unsigned long)led_dev);
+	}
+	read_unlock(&trigger->leddev_list_lock);
+}
+
+void leds_idle(int is_idle)
+{
+	if (leds_do_idle) {
+		leds_trigger_idle(&cpu_led_trigger, is_idle);
+		leds_trigger_idle(&idle_led_trigger, is_idle);
+	}
+}
+
+EXPORT_SYMBOL_GPL(leds_idle);
+#endif
+
 module_init(timer_trig_init);
 module_exit(timer_trig_exit);
 
--- 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
 #define led_trigger_event(x, y) do {} while(0)
 
 #endif
+
+/*
+ * 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.
+ */
+void leds_idle(int is_idle);
+
+#else
+
+/* No CPU activity support. */
+#define leds_idle(x) do {} while (0)
+
+#endif
+