1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ledtrig-cpu.c - LED trigger based on CPU activity |
4 | * |
5 | * This LED trigger will be registered for first 8 CPUs and named |
6 | * as cpu0..cpu7. There's additional trigger called cpu that |
7 | * is on when any CPU is active. |
8 | * |
9 | * If you want support for arbitrary number of CPUs, make it one trigger, |
10 | * with additional sysfs file selecting which CPU to watch. |
11 | * |
12 | * It can be bound to any LED just like other triggers using either a |
13 | * board file or via sysfs interface. |
14 | * |
15 | * An API named ledtrig_cpu is exported for any user, who want to add CPU |
16 | * activity indication in their code. |
17 | * |
18 | * Copyright 2011 Linus Walleij <linus.walleij@linaro.org> |
19 | * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com> |
20 | */ |
21 | |
22 | #include <linux/kernel.h> |
23 | #include <linux/init.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/percpu.h> |
26 | #include <linux/syscore_ops.h> |
27 | #include <linux/rwsem.h> |
28 | #include <linux/cpu.h> |
29 | #include "../leds.h" |
30 | |
31 | #define MAX_NAME_LEN 8 |
32 | |
33 | struct led_trigger_cpu { |
34 | bool is_active; |
35 | char name[MAX_NAME_LEN]; |
36 | struct led_trigger *_trig; |
37 | }; |
38 | |
39 | static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); |
40 | |
41 | static struct led_trigger *trig_cpu_all; |
42 | static atomic_t num_active_cpus = ATOMIC_INIT(0); |
43 | |
44 | /** |
45 | * ledtrig_cpu - emit a CPU event as a trigger |
46 | * @ledevt: CPU event to be emitted |
47 | * |
48 | * Emit a CPU event on a CPU core, which will trigger a |
49 | * bound LED to turn on or turn off. |
50 | */ |
51 | void ledtrig_cpu(enum cpu_led_event ledevt) |
52 | { |
53 | struct led_trigger_cpu *trig = this_cpu_ptr(&cpu_trig); |
54 | bool is_active = trig->is_active; |
55 | |
56 | /* Locate the correct CPU LED */ |
57 | switch (ledevt) { |
58 | case CPU_LED_IDLE_END: |
59 | case CPU_LED_START: |
60 | /* Will turn the LED on, max brightness */ |
61 | is_active = true; |
62 | break; |
63 | |
64 | case CPU_LED_IDLE_START: |
65 | case CPU_LED_STOP: |
66 | case CPU_LED_HALTED: |
67 | /* Will turn the LED off */ |
68 | is_active = false; |
69 | break; |
70 | |
71 | default: |
72 | /* Will leave the LED as it is */ |
73 | break; |
74 | } |
75 | |
76 | if (is_active != trig->is_active) { |
77 | unsigned int active_cpus; |
78 | unsigned int total_cpus; |
79 | |
80 | /* Update trigger state */ |
81 | trig->is_active = is_active; |
82 | atomic_add(i: is_active ? 1 : -1, v: &num_active_cpus); |
83 | active_cpus = atomic_read(v: &num_active_cpus); |
84 | total_cpus = num_present_cpus(); |
85 | |
86 | led_trigger_event(trigger: trig->_trig, |
87 | event: is_active ? LED_FULL : LED_OFF); |
88 | |
89 | |
90 | led_trigger_event(trigger: trig_cpu_all, |
91 | DIV_ROUND_UP(LED_FULL * active_cpus, total_cpus)); |
92 | |
93 | } |
94 | } |
95 | EXPORT_SYMBOL(ledtrig_cpu); |
96 | |
97 | static int ledtrig_cpu_syscore_suspend(void) |
98 | { |
99 | ledtrig_cpu(CPU_LED_STOP); |
100 | return 0; |
101 | } |
102 | |
103 | static void ledtrig_cpu_syscore_resume(void) |
104 | { |
105 | ledtrig_cpu(CPU_LED_START); |
106 | } |
107 | |
108 | static void ledtrig_cpu_syscore_shutdown(void) |
109 | { |
110 | ledtrig_cpu(CPU_LED_HALTED); |
111 | } |
112 | |
113 | static struct syscore_ops ledtrig_cpu_syscore_ops = { |
114 | .shutdown = ledtrig_cpu_syscore_shutdown, |
115 | .suspend = ledtrig_cpu_syscore_suspend, |
116 | .resume = ledtrig_cpu_syscore_resume, |
117 | }; |
118 | |
119 | static int ledtrig_online_cpu(unsigned int cpu) |
120 | { |
121 | ledtrig_cpu(CPU_LED_START); |
122 | return 0; |
123 | } |
124 | |
125 | static int ledtrig_prepare_down_cpu(unsigned int cpu) |
126 | { |
127 | ledtrig_cpu(CPU_LED_STOP); |
128 | return 0; |
129 | } |
130 | |
131 | static int __init ledtrig_cpu_init(void) |
132 | { |
133 | unsigned int cpu; |
134 | int ret; |
135 | |
136 | /* Supports up to 9999 cpu cores */ |
137 | BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); |
138 | |
139 | /* |
140 | * Registering a trigger for all CPUs. |
141 | */ |
142 | led_trigger_register_simple(name: "cpu" , trigger: &trig_cpu_all); |
143 | |
144 | /* |
145 | * Registering CPU led trigger for each CPU core here |
146 | * ignores CPU hotplug, but after this CPU hotplug works |
147 | * fine with this trigger. |
148 | */ |
149 | for_each_possible_cpu(cpu) { |
150 | struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); |
151 | |
152 | if (cpu >= 8) |
153 | continue; |
154 | |
155 | snprintf(buf: trig->name, MAX_NAME_LEN, fmt: "cpu%u" , cpu); |
156 | |
157 | led_trigger_register_simple(name: trig->name, trigger: &trig->_trig); |
158 | } |
159 | |
160 | register_syscore_ops(ops: &ledtrig_cpu_syscore_ops); |
161 | |
162 | ret = cpuhp_setup_state(state: CPUHP_AP_ONLINE_DYN, name: "leds/trigger:starting" , |
163 | startup: ledtrig_online_cpu, teardown: ledtrig_prepare_down_cpu); |
164 | if (ret < 0) |
165 | pr_err("CPU hotplug notifier for ledtrig-cpu could not be registered: %d\n" , |
166 | ret); |
167 | |
168 | pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n" ); |
169 | |
170 | return 0; |
171 | } |
172 | device_initcall(ledtrig_cpu_init); |
173 | |