| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 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> | 
|  | 29 | #include "leds.h" | 
|  | 30 |  | 
|  | 31 | #define MAX_NAME_LEN	8 | 
|  | 32 |  | 
|  | 33 | struct led_trigger_cpu { | 
|  | 34 | char name[MAX_NAME_LEN]; | 
|  | 35 | struct led_trigger *_trig; | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 36 | }; | 
|  | 37 |  | 
|  | 38 | static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig); | 
|  | 39 |  | 
|  | 40 | /** | 
|  | 41 | * ledtrig_cpu - emit a CPU event as a trigger | 
|  | 42 | * @evt: CPU event to be emitted | 
|  | 43 | * | 
|  | 44 | * Emit a CPU event on a CPU core, which will trigger a | 
|  | 45 | * binded LED to turn on or turn off. | 
|  | 46 | */ | 
|  | 47 | void ledtrig_cpu(enum cpu_led_event ledevt) | 
|  | 48 | { | 
|  | 49 | struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig); | 
|  | 50 |  | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 51 | /* Locate the correct CPU LED */ | 
|  | 52 | switch (ledevt) { | 
|  | 53 | case CPU_LED_IDLE_END: | 
|  | 54 | case CPU_LED_START: | 
|  | 55 | /* Will turn the LED on, max brightness */ | 
|  | 56 | led_trigger_event(trig->_trig, LED_FULL); | 
|  | 57 | break; | 
|  | 58 |  | 
|  | 59 | case CPU_LED_IDLE_START: | 
|  | 60 | case CPU_LED_STOP: | 
|  | 61 | case CPU_LED_HALTED: | 
|  | 62 | /* Will turn the LED off */ | 
|  | 63 | led_trigger_event(trig->_trig, LED_OFF); | 
|  | 64 | break; | 
|  | 65 |  | 
|  | 66 | default: | 
|  | 67 | /* Will leave the LED as it is */ | 
|  | 68 | break; | 
|  | 69 | } | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 70 | } | 
|  | 71 | EXPORT_SYMBOL(ledtrig_cpu); | 
|  | 72 |  | 
|  | 73 | static int ledtrig_cpu_syscore_suspend(void) | 
|  | 74 | { | 
|  | 75 | ledtrig_cpu(CPU_LED_STOP); | 
|  | 76 | return 0; | 
|  | 77 | } | 
|  | 78 |  | 
|  | 79 | static void ledtrig_cpu_syscore_resume(void) | 
|  | 80 | { | 
|  | 81 | ledtrig_cpu(CPU_LED_START); | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | static void ledtrig_cpu_syscore_shutdown(void) | 
|  | 85 | { | 
|  | 86 | ledtrig_cpu(CPU_LED_HALTED); | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | static struct syscore_ops ledtrig_cpu_syscore_ops = { | 
|  | 90 | .shutdown	= ledtrig_cpu_syscore_shutdown, | 
|  | 91 | .suspend	= ledtrig_cpu_syscore_suspend, | 
|  | 92 | .resume		= ledtrig_cpu_syscore_resume, | 
|  | 93 | }; | 
|  | 94 |  | 
|  | 95 | static int __init ledtrig_cpu_init(void) | 
|  | 96 | { | 
|  | 97 | int cpu; | 
|  | 98 |  | 
|  | 99 | /* Supports up to 9999 cpu cores */ | 
|  | 100 | BUILD_BUG_ON(CONFIG_NR_CPUS > 9999); | 
|  | 101 |  | 
|  | 102 | /* | 
|  | 103 | * Registering CPU led trigger for each CPU core here | 
|  | 104 | * ignores CPU hotplug, but after this CPU hotplug works | 
|  | 105 | * fine with this trigger. | 
|  | 106 | */ | 
|  | 107 | for_each_possible_cpu(cpu) { | 
|  | 108 | struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); | 
|  | 109 |  | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 110 | snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu); | 
|  | 111 |  | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 112 | led_trigger_register_simple(trig->name, &trig->_trig); | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 113 | } | 
|  | 114 |  | 
|  | 115 | register_syscore_ops(&ledtrig_cpu_syscore_ops); | 
|  | 116 |  | 
|  | 117 | pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); | 
|  | 118 |  | 
|  | 119 | return 0; | 
|  | 120 | } | 
|  | 121 | module_init(ledtrig_cpu_init); | 
|  | 122 |  | 
|  | 123 | static void __exit ledtrig_cpu_exit(void) | 
|  | 124 | { | 
|  | 125 | int cpu; | 
|  | 126 |  | 
|  | 127 | for_each_possible_cpu(cpu) { | 
|  | 128 | struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); | 
|  | 129 |  | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 130 | led_trigger_unregister_simple(trig->_trig); | 
|  | 131 | trig->_trig = NULL; | 
|  | 132 | memset(trig->name, 0, MAX_NAME_LEN); | 
| Bryan Wu | 8f88731 | 2011-06-25 18:33:50 +0800 | [diff] [blame] | 133 | } | 
|  | 134 |  | 
|  | 135 | unregister_syscore_ops(&ledtrig_cpu_syscore_ops); | 
|  | 136 | } | 
|  | 137 | module_exit(ledtrig_cpu_exit); | 
|  | 138 |  | 
|  | 139 | MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>"); | 
|  | 140 | MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>"); | 
|  | 141 | MODULE_DESCRIPTION("CPU LED trigger"); | 
|  | 142 | MODULE_LICENSE("GPL"); |