Commit | Line | Data |
---|---|---|
8f88731d BW |
1 | /* |
2 | * ledtrig-cpu.c - LED trigger based on CPU activity | |
3 | * | |
4 | * This LED trigger will be registered for each possible CPU and named as | |
5 | * cpu0, cpu1, cpu2, cpu3, etc. | |
6 | * | |
7 | * It can be bound to any LED just like other triggers using either a | |
8 | * board file or via sysfs interface. | |
9 | * | |
10 | * An API named ledtrig_cpu is exported for any user, who want to add CPU | |
11 | * activity indication in their code | |
12 | * | |
13 | * Copyright 2011 Linus Walleij <linus.walleij@linaro.org> | |
14 | * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com> | |
15 | * | |
16 | * This program is free software; you can redistribute it and/or modify | |
17 | * it under the terms of the GNU General Public License version 2 as | |
18 | * published by the Free Software Foundation. | |
19 | * | |
20 | */ | |
21 | ||
22 | #include <linux/module.h> | |
23 | #include <linux/kernel.h> | |
24 | #include <linux/init.h> | |
25 | #include <linux/slab.h> | |
26 | #include <linux/percpu.h> | |
27 | #include <linux/syscore_ops.h> | |
28 | #include <linux/rwsem.h> | |
fba14ae8 | 29 | #include <linux/cpu.h> |
f07fb521 | 30 | #include "../leds.h" |
8f88731d BW |
31 | |
32 | #define MAX_NAME_LEN 8 | |
33 | ||
34 | struct led_trigger_cpu { | |
35 | char name[MAX_NAME_LEN]; | |
36 | struct led_trigger *_trig; | |
8f88731d BW |
37 | }; |
38 | ||
39 | static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); | |
40 | ||
41 | /** | |
42 | * ledtrig_cpu - emit a CPU event as a trigger | |
43 | * @evt: CPU event to be emitted | |
44 | * | |
45 | * Emit a CPU event on a CPU core, which will trigger a | |
46 | * binded LED to turn on or turn off. | |
47 | */ | |
48 | void ledtrig_cpu(enum cpu_led_event ledevt) | |
49 | { | |
50 | struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig); | |
51 | ||
8f88731d BW |
52 | /* Locate the correct CPU LED */ |
53 | switch (ledevt) { | |
54 | case CPU_LED_IDLE_END: | |
55 | case CPU_LED_START: | |
56 | /* Will turn the LED on, max brightness */ | |
57 | led_trigger_event(trig->_trig, LED_FULL); | |
58 | break; | |
59 | ||
60 | case CPU_LED_IDLE_START: | |
61 | case CPU_LED_STOP: | |
62 | case CPU_LED_HALTED: | |
63 | /* Will turn the LED off */ | |
64 | led_trigger_event(trig->_trig, LED_OFF); | |
65 | break; | |
66 | ||
67 | default: | |
68 | /* Will leave the LED as it is */ | |
69 | break; | |
70 | } | |
8f88731d BW |
71 | } |
72 | EXPORT_SYMBOL(ledtrig_cpu); | |
73 | ||
74 | static int ledtrig_cpu_syscore_suspend(void) | |
75 | { | |
76 | ledtrig_cpu(CPU_LED_STOP); | |
77 | return 0; | |
78 | } | |
79 | ||
80 | static void ledtrig_cpu_syscore_resume(void) | |
81 | { | |
82 | ledtrig_cpu(CPU_LED_START); | |
83 | } | |
84 | ||
85 | static void ledtrig_cpu_syscore_shutdown(void) | |
86 | { | |
87 | ledtrig_cpu(CPU_LED_HALTED); | |
88 | } | |
89 | ||
90 | static struct syscore_ops ledtrig_cpu_syscore_ops = { | |
91 | .shutdown = ledtrig_cpu_syscore_shutdown, | |
92 | .suspend = ledtrig_cpu_syscore_suspend, | |
93 | .resume = ledtrig_cpu_syscore_resume, | |
94 | }; | |
95 | ||
fba14ae8 PM |
96 | static int ledtrig_cpu_notify(struct notifier_block *self, |
97 | unsigned long action, void *hcpu) | |
98 | { | |
99 | switch (action & ~CPU_TASKS_FROZEN) { | |
100 | case CPU_STARTING: | |
101 | ledtrig_cpu(CPU_LED_START); | |
102 | break; | |
103 | case CPU_DYING: | |
104 | ledtrig_cpu(CPU_LED_STOP); | |
105 | break; | |
106 | } | |
107 | ||
108 | return NOTIFY_OK; | |
109 | } | |
110 | ||
111 | ||
112 | static struct notifier_block ledtrig_cpu_nb = { | |
113 | .notifier_call = ledtrig_cpu_notify, | |
114 | }; | |
115 | ||
8f88731d BW |
116 | static int __init ledtrig_cpu_init(void) |
117 | { | |
118 | int cpu; | |
119 | ||
120 | /* Supports up to 9999 cpu cores */ | |
121 | BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); | |
122 | ||
123 | /* | |
124 | * Registering CPU led trigger for each CPU core here | |
125 | * ignores CPU hotplug, but after this CPU hotplug works | |
126 | * fine with this trigger. | |
127 | */ | |
128 | for_each_possible_cpu(cpu) { | |
129 | struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); | |
130 | ||
8f88731d BW |
131 | snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); |
132 | ||
8f88731d | 133 | led_trigger_register_simple(trig->name, &trig->_trig); |
8f88731d BW |
134 | } |
135 | ||
136 | register_syscore_ops(&ledtrig_cpu_syscore_ops); | |
fba14ae8 | 137 | register_cpu_notifier(&ledtrig_cpu_nb); |
8f88731d BW |
138 | |
139 | pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); | |
140 | ||
141 | return 0; | |
142 | } | |
143 | module_init(ledtrig_cpu_init); | |
144 | ||
145 | static void __exit ledtrig_cpu_exit(void) | |
146 | { | |
147 | int cpu; | |
148 | ||
fba14ae8 PM |
149 | unregister_cpu_notifier(&ledtrig_cpu_nb); |
150 | ||
8f88731d BW |
151 | for_each_possible_cpu(cpu) { |
152 | struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); | |
153 | ||
8f88731d BW |
154 | led_trigger_unregister_simple(trig->_trig); |
155 | trig->_trig = NULL; | |
156 | memset(trig->name, 0, MAX_NAME_LEN); | |
8f88731d BW |
157 | } |
158 | ||
159 | unregister_syscore_ops(&ledtrig_cpu_syscore_ops); | |
160 | } | |
161 | module_exit(ledtrig_cpu_exit); | |
162 | ||
163 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | |
164 | MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>"); | |
165 | MODULE_DESCRIPTION("CPU LED trigger"); | |
166 | MODULE_LICENSE("GPL"); |