root/trunk/patches/2.6.22/82-leds-arm-cpu-activity.patch

Revision 833, 17.8 kB (checked in by blaster8, 3 years ago)

Archive old versions, add 2.6.22 devel patchset

  • linux-2.6.22-rc1-arm/arch/arm/kernel/process.c

    old new  
    2525#include <linux/kallsyms.h> 
    2626#include <linux/init.h> 
    2727#include <linux/cpu.h> 
     28#include <linux/leds.h> 
    2829#include <linux/elfcore.h> 
    2930#include <linux/pm.h> 
    3031#include <linux/tick.h> 
     
    120121 
    121122 
    122123/* 
     124 * CPU activity indicator. 
     125 */ 
     126void (*leds_idle)(int is_idle); 
     127EXPORT_SYMBOL(leds_idle); 
     128 
     129/* 
    123130 * This is our default idle handler.  We need to disable 
    124131 * interrupts here to ensure we don't miss a wakeup call. 
    125132 */ 
     
    160167                if (!idle) 
    161168                        idle = default_idle; 
    162169                leds_event(led_idle_start); 
     170                if (leds_idle) 
     171                        leds_idle(1); 
    163172                tick_nohz_stop_sched_tick(); 
    164173                while (!need_resched()) 
    165174                        idle(); 
     175                if (leds_idle) 
     176                        leds_idle(0); 
    166177                leds_event(led_idle_end); 
    167178                tick_nohz_restart_sched_tick(); 
    168179                preempt_enable_no_resched(); 
  • linux-2.6.22-rc1-arm/drivers/leds/Kconfig

    old new  
    128128          load average. 
    129129          If unsure, say Y. 
    130130 
     131config LEDS_TRIGGER_CPU_ACTIVITY 
     132        tristate "LED CPU activity trigger" 
     133        depends LEDS_TRIGGER_TIMER 
     134        help 
     135          This allows LEDs to be set to show cpu activity via sysfs. 
     136          The LED will blink when the cpu is active and stay steady 
     137          (on or off according to the trigger selected) when idle. 
     138          If unsure, say Y. 
     139 
    131140endmenu 
    132141 
  • linux-2.6.22-rc1-arm/drivers/leds/Makefile

    old new  
    2121obj-$(CONFIG_LEDS_TRIGGER_TIMER)        += ledtrig-timer.o 
    2222obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK)     += ledtrig-ide-disk.o 
    2323obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)    += ledtrig-heartbeat.o 
     24obj-$(CONFIG_LEDS_TRIGGER_CPU_ACTIVITY) += ledtrig-cpu.o 
  • /dev/null

    old new  
     1/* 
     2 * LEDs CPU activity trigger 
     3 * 
     4 * Author: John Bowler <jbowler@acm.org> 
     5 * 
     6 * Copyright (c) 2006 John Bowler 
     7 * 
     8 * Permission is hereby granted, free of charge, to any 
     9 * person obtaining a copy of this software and associated 
     10 * documentation files (the "Software"), to deal in the 
     11 * Software without restriction, including without 
     12 * limitation the rights to use, copy, modify, merge, 
     13 * publish, distribute, sublicense, and/or sell copies of 
     14 * the Software, and to permit persons to whom the 
     15 * Software is furnished to do so, subject to the 
     16 * following conditions: 
     17 * 
     18 * The above copyright notice and this permission notice 
     19 * shall be included in all copies or substantial portions 
     20 * of the Software. 
     21 * 
     22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 
     23 * ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
     24 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
     25 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 
     26 * SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
     27 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
     28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
     29 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 
     30 * OTHER DEALINGS IN THE SOFTWARE. 
     31 * 
     32 */ 
     33 
     34#include <linux/ctype.h> 
     35#include <linux/kernel.h> 
     36#include <linux/module.h> 
     37#include <linux/init.h> 
     38#include <linux/spinlock.h> 
     39#include <linux/timer.h> 
     40#include <linux/device.h> 
     41 
     42#include <linux/leds.h> 
     43#include "leds.h" 
     44 
     45//#include <linux/list.h> 
     46//#include <linux/sysdev.h> 
     47 
     48 
     49/* 
     50 * To simplify this the LED state is given for each case of 
     51 * CPU state - idle or active.  The LED can be: 
     52 * 
     53 * off 
     54 * flash - slow for idle, fast (flicker) for active 
     55 * on 
     56 * 
     57 * This gives two useless states - off/off and on/on 
     58 */ 
     59typedef enum cpu_trigger_led_state { 
     60        cpu_led_off, 
     61        cpu_led_flash, 
     62        cpu_led_on, 
     63        cpu_led_invalid 
     64} cpu_trigger_led_state; 
     65 
     66static const char *const cpu_trigger_names[] = { 
     67        "off", 
     68        "flash", 
     69        "on", 
     70        "invalid" 
     71}; 
     72 
     73/* Forward declaration - this is called back when an LED property 
     74 * is changed. 
     75 */ 
     76static void leds_cpu_trigger_state_change(void); 
     77 
     78/* 
     79 * These constants define the actual mark/space of the flashing 
     80 * in jiffies.  msecs_to_jiffies rounds up and is compile time 
     81 * evaluable for constant arguments.  Writing the ?: stuff below 
     82 * this way ensures the compiler doesn't think it needs to 
     83 * compile in the math of msecs_to_jiffies. 
     84 * 
     85 * These values have been determined by experiment to work well 
     86 * for the ready/status LED on a LinkSys NSLU2 (light piped) and 
     87 * for the user LED on a Loft (Gateway Avila variant) board where 
     88 * the LED was directly visible.  Light Output Varies Everywhere. 
     89 */ 
     90#define LEDS_CPU_ACTIVE_MARK    msecs_to_jiffies(40) 
     91#define LEDS_CPU_IDLE_MARK      msecs_to_jiffies(800) 
     92#define LEDS_CPU_ACTIVE_SPACE   msecs_to_jiffies(60) 
     93#define LEDS_CPU_IDLE_SPACE     msecs_to_jiffies(800) 
     94 
     95 
     96/* 
     97 * Individual LEDs ------------------------------------------------------------ 
     98 */ 
     99struct cpu_trigger_data { 
     100        cpu_trigger_led_state active; /* Behaviour when the CPU is active. */ 
     101        cpu_trigger_led_state idle;   /* Behaviour when the CPU is idle. */ 
     102}; 
     103 
     104/* 
     105 * LED state change - called when the state of a single LED might 
     106 * have changed.  Returns true if the LED is blinking.  The argument 
     107 * is the blink state - the brightness of the blinking LED. 
     108 */ 
     109static int leds_cpu_trigger_led_state_change(struct led_classdev *led, 
     110                int is_active, enum led_brightness brightness) 
     111{ 
     112        int is_blinking = 0; 
     113 
     114        struct cpu_trigger_data *data = led->trigger_data; 
     115 
     116        /* Find the new brightness for the LED, if the LED is 
     117         * set to flash then the brightness passed in is the 
     118         * required value. 
     119         */ 
     120        if (likely(data != 0)) 
     121                switch (is_active ? data->active : data->idle) { 
     122                case cpu_led_off:   brightness = LED_OFF;  break; 
     123                case cpu_led_flash: is_blinking = 1;       break; 
     124                case cpu_led_on:    brightness = LED_FULL; break; 
     125                } 
     126        else 
     127                brightness = is_active ? LED_FULL : LED_OFF; 
     128 
     129        led_set_brightness(led, brightness); 
     130 
     131        return is_blinking; 
     132} 
     133 
     134/* 
     135 * sysfs properties, the property is output at an list of the 
     136 * values with the current setting enclosed in [] 
     137 */ 
     138static ssize_t leds_cpu_trigger_show_prop(struct class_device *dev, char *buf, 
     139                size_t where) 
     140{ 
     141        struct led_classdev     *led = dev->class_data; 
     142        cpu_trigger_led_state  item = cpu_led_invalid, i; 
     143        char                  *next; 
     144 
     145        if (likely(led->trigger_data != 0)) 
     146                item = *(const cpu_trigger_led_state*)( 
     147                                led->trigger_data + where); 
     148 
     149        for (i=0, next=buf; i<cpu_led_invalid; ++i) { 
     150                const char *name = cpu_trigger_names[i]; 
     151                size_t len = strlen(name); 
     152 
     153                if (i == item) 
     154                        *next++ = '['; 
     155                memcpy(next, name, len); 
     156                next += len; 
     157                if (i == item) 
     158                        *next++ = ']'; 
     159                *next++ = ' '; 
     160        } 
     161 
     162        next[-1] = '\n'; 
     163        *next++ = 0; 
     164 
     165        return next - buf; 
     166} 
     167 
     168static ssize_t leds_cpu_trigger_show_active(struct class_device *dev, char *buf) 
     169{ 
     170        return leds_cpu_trigger_show_prop(dev, buf, 
     171                        offsetof(struct cpu_trigger_data, active)); 
     172} 
     173 
     174static ssize_t leds_cpu_trigger_show_idle(struct class_device *dev, char *buf) 
     175{ 
     176        return leds_cpu_trigger_show_prop(dev, buf, 
     177                        offsetof(struct cpu_trigger_data, idle)); 
     178} 
     179 
     180/* 
     181 * Any matching leading substring selects a property - so "onoffonoff" 
     182 * sets the property to off. 
     183 */ 
     184static ssize_t leds_cpu_trigger_store_prop(struct class_device *dev, 
     185                const char *buf, size_t size, size_t where) 
     186{ 
     187        size_t rc = 0; 
     188        cpu_trigger_led_state value = 0/*sic*/; 
     189        struct led_classdev *led; 
     190 
     191        /* ignore space characters before the value. */ 
     192        while (rc < size && isspace(buf[rc])) 
     193                ++rc; 
     194        if (rc >= size) 
     195                return rc; 
     196 
     197        /* look for a simple match against the trigger name, case 
     198         * sensitive. 
     199         */ 
     200        do { 
     201                const char *name = cpu_trigger_names[value]; 
     202                size_t len = strlen(name); 
     203                if (len <= size && memcmp(buf+rc, name, len) == 0) { 
     204                        rc = len; 
     205                        break; 
     206                } 
     207                if (++value >= cpu_led_invalid) 
     208                        return -EINVAL; 
     209        } while (1); 
     210 
     211        led = dev->class_data; 
     212        if (likely(led->trigger_data != 0)) 
     213                *(cpu_trigger_led_state*)( 
     214                                led->trigger_data + where) = value; 
     215 
     216        return rc; 
     217} 
     218 
     219static ssize_t leds_cpu_trigger_store_active(struct class_device *dev, 
     220                const char *buf, size_t size) 
     221{ 
     222        ssize_t rc = leds_cpu_trigger_store_prop(dev, buf, size, 
     223                        offsetof(struct cpu_trigger_data, active)); 
     224        /* 
     225         * At least one CPU must be active (otherwise who is doing this?) 
     226         * Call down into the global state below to cause an update 
     227         * to happen now. 
     228         */ 
     229        leds_cpu_trigger_state_change(); 
     230        return rc; 
     231} 
     232 
     233static ssize_t leds_cpu_trigger_store_idle(struct class_device *dev, 
     234                const char *buf, size_t size) 
     235{ 
     236        return leds_cpu_trigger_store_prop(dev, buf, size, 
     237                        offsetof(struct cpu_trigger_data, idle)); 
     238} 
     239 
     240static CLASS_DEVICE_ATTR(active, 0644, leds_cpu_trigger_show_active, 
     241                                        leds_cpu_trigger_store_active); 
     242 
     243static CLASS_DEVICE_ATTR(idle, 0644, leds_cpu_trigger_show_idle, 
     244                                        leds_cpu_trigger_store_idle); 
     245 
     246/* 
     247 * Activate and deactivate are called on individual LEDs when the 
     248 * LED trigger property is changed. 
     249 */ 
     250static void leds_cpu_trigger_activate(struct led_classdev *led) 
     251{ 
     252        /* 
     253         * The initial setting of the trigger is simple CPU activity 
     254         * with the LED off for idle and on for active.  Consequently 
     255         * there is no need to mess with the global state initially, 
     256         * we know the CPU is active at this moment! 
     257         */ 
     258        struct cpu_trigger_data *data = kmalloc(sizeof *data, GFP_KERNEL); 
     259        if (unlikely(data == 0)) 
     260                return; 
     261 
     262        data->active = cpu_led_on; 
     263        data->idle = cpu_led_off; 
     264        led->trigger_data = data; 
     265 
     266        class_device_create_file(led->class_dev, &class_device_attr_active); 
     267        class_device_create_file(led->class_dev, &class_device_attr_idle); 
     268 
     269        led_set_brightness(led, LED_FULL); 
     270} 
     271 
     272static void leds_cpu_trigger_deactivate(struct led_classdev *led) 
     273{ 
     274        struct cpu_trigger_data *data = led->trigger_data; 
     275        if (likely(data != 0)) { 
     276                led_set_brightness(led, LED_OFF); 
     277 
     278                class_device_remove_file(led->class_dev, &class_device_attr_idle); 
     279                class_device_remove_file(led->class_dev, &class_device_attr_active); 
     280 
     281                led->trigger_data = 0; 
     282                kfree(data); 
     283        } 
     284} 
     285 
     286 
     287/* 
     288 * Global state  -------------------------------------------------------------- 
     289 * 
     290 * This is global because the CPU state is global and we only need one timer to 
     291 * do this stuff. 
     292 */ 
     293typedef struct leds_cpu_trigger_data { 
     294        struct led_trigger trigger; /* the lock in here protects everything */ 
     295        struct timer_list  timer; 
     296        unsigned long      last_active_time; /* record of last jiffies */ 
     297        unsigned long      last_idle_time;   /* record of last jiffies */ 
     298        int                count_active;     /* number of active CPUs */ 
     299} leds_cpu_trigger_data; 
     300 
     301/* 
     302 * Mark state - uses the current time (jiffies) to work out 
     303 * whether this is a mark or space. 
     304 */ 
     305static int leds_cpu_trigger_mark(struct leds_cpu_trigger_data *data, 
     306                unsigned long now) { 
     307        if (data->count_active > 0) { 
     308                unsigned long elapsed = now - data->last_active_time; 
     309                elapsed %= LEDS_CPU_ACTIVE_SPACE + LEDS_CPU_ACTIVE_MARK; 
     310                data->last_active_time = now - elapsed; 
     311                return elapsed > LEDS_CPU_ACTIVE_SPACE; 
     312        } else { 
     313                unsigned long elapsed = now - data->last_idle_time; 
     314                elapsed %= LEDS_CPU_IDLE_SPACE + LEDS_CPU_IDLE_MARK; 
     315                data->last_idle_time = now - elapsed; 
     316                return elapsed > LEDS_CPU_IDLE_SPACE; 
     317        } 
     318} 
     319 
     320 
     321/* 
     322 * State change - given information about the nature of the 
     323 * (possible) state change call up to each LED to adjust its 
     324 * state.  Returns true if any LED is blinking.  The lock 
     325 * must be held (a read lock is adequate). 
     326 */ 
     327static int leds_cpu_trigger_scan_leds(struct leds_cpu_trigger_data *data, 
     328                unsigned long now) 
     329{ 
     330        int blinking = 0; 
     331        const int active = data->count_active > 0; 
     332        const enum led_brightness brightness = 
     333                leds_cpu_trigger_mark(data, now) ? LED_FULL : LED_OFF; 
     334        struct list_head *entry; 
     335 
     336        list_for_each(entry, &data->trigger.led_cdevs) { 
     337                struct led_classdev *led = 
     338                        list_entry(entry, struct led_classdev, trig_list); 
     339 
     340                blinking |= leds_cpu_trigger_led_state_change(led, 
     341                                active, brightness); 
     342        } 
     343 
     344        return blinking; 
     345} 
     346 
     347/* 
     348 * Set the timer correctly according to the current state, the lock 
     349 * must be held for write. 
     350 */ 
     351static void leds_cpu_trigger_set_timer(struct leds_cpu_trigger_data *state, 
     352                unsigned long now) 
     353{ 
     354        unsigned long next; 
     355        if (state->count_active > 0) { 
     356                next = state->last_active_time; 
     357                if (now - next > LEDS_CPU_ACTIVE_SPACE) 
     358                        next += LEDS_CPU_ACTIVE_MARK; 
     359                next += LEDS_CPU_ACTIVE_SPACE; 
     360        } else { 
     361                next = state->last_idle_time; 
     362                if (now - next > LEDS_CPU_IDLE_SPACE) 
     363                        next += LEDS_CPU_IDLE_MARK; 
     364                next += LEDS_CPU_IDLE_SPACE; 
     365        } 
     366        mod_timer(&state->timer, next); 
     367} 
     368 
     369/* 
     370 * The timer callback if the LED is currently flashing, the callback 
     371 * calls the state change function and, if that returns true, meaning 
     372 * that at least one LED is still blinking, the timer is restarted 
     373 * with the correct timeout. 
     374 */ 
     375static void leds_cpu_trigger_timer_callback(unsigned long data) 
     376{ 
     377        struct leds_cpu_trigger_data *state = 
     378                                (struct leds_cpu_trigger_data *)data; 
     379 
     380        write_lock(&state->trigger.leddev_list_lock); 
     381        { 
     382                unsigned long now = jiffies; 
     383 
     384                /* If at least one LED is set to flash; set the timer 
     385                 * again (this won't reset the timer set within the 
     386                 * idle loop). 
     387                 */ 
     388                if (leds_cpu_trigger_scan_leds(state, now)) 
     389                        leds_cpu_trigger_set_timer(state, now); 
     390        } 
     391        write_unlock(&state->trigger.leddev_list_lock); 
     392} 
     393 
     394 
     395/* 
     396 * There is one global control structure, one timer and one set 
     397 * of state for active CPUs shared across all the LEDs.  Individual 
     398 * LEDs say how this state to be handled.  It is currently *not* 
     399 * possible to show per-cpu activity on individual LEDs, the code 
     400 * maintains a count of active CPUs and the state is only 'idle' 
     401 * if all CPUs are idle. 
     402 */ 
     403static struct leds_cpu_trigger_data leds_cpu_trigger = { 
     404        .trigger = { 
     405                .name       = "cpu", 
     406                .activate   = leds_cpu_trigger_activate, 
     407                .deactivate = leds_cpu_trigger_deactivate, 
     408        } , 
     409        .timer   = TIMER_INITIALIZER(leds_cpu_trigger_timer_callback, 0, 
     410                        (unsigned long)&leds_cpu_trigger), 
     411        .last_active_time = 0, 
     412        .last_idle_time   = 0, 
     413        .count_active     = 0, 
     414}; 
     415 
     416/* 
     417 * State change - callback from an individual LED on a property change which 
     418 * might require a redisplay. 
     419 */ 
     420static void leds_cpu_trigger_state_change() { 
     421        write_lock(&leds_cpu_trigger.trigger.leddev_list_lock); 
     422        { 
     423                unsigned long now = jiffies; 
     424 
     425                if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now) && 
     426                        !timer_pending(&leds_cpu_trigger.timer)) 
     427                        leds_cpu_trigger_set_timer(&leds_cpu_trigger, now); 
     428        } 
     429        write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock); 
     430} 
     431 
     432/* 
     433 * Called from every CPU at the start and end of the idle loop. 
     434 * The active count is initially 0, even though CPUs are running, 
     435 * so the code below must check for the resultant underflow. 
     436 * 
     437 * If the idle behaviour is 'flash' then when the timer times out 
     438 * it will take the CPU out of idle, set the active state (which 
     439 * may also be flash), drop back into idle and reset the timer to 
     440 * the idle timeout... 
     441 */ 
     442static void leds_cpu_trigger_idle(int is_idle) 
     443{ 
     444        write_lock(&leds_cpu_trigger.trigger.leddev_list_lock); 
     445        if ((is_idle && leds_cpu_trigger.count_active > 0 && 
     446                                --leds_cpu_trigger.count_active == 0) || 
     447            (!is_idle && leds_cpu_trigger.count_active < num_online_cpus() && 
     448                                ++leds_cpu_trigger.count_active == 1)) { 
     449                unsigned long now = jiffies; 
     450 
     451                /* State change - the system just became idle or active, 
     452                 * call the del_timer first in an attempt to minimise 
     453                 * getting a timer interrupt which will take us unnecessarily 
     454                 * out of idle (this doesn't matter). 
     455                 */ 
     456                del_timer(&leds_cpu_trigger.timer); 
     457                if (leds_cpu_trigger_scan_leds(&leds_cpu_trigger, now)) 
     458                        leds_cpu_trigger_set_timer(&leds_cpu_trigger, now); 
     459        } 
     460        write_unlock(&leds_cpu_trigger.trigger.leddev_list_lock); 
     461} 
     462 
     463/* 
     464 * Module init and exit - register the trigger, then store 
     465 * the idle callback in the arch-specific global.  For this 
     466 * module to link (into the kernel) or load (into a running 
     467 * kernel) the architecture must define the leds_idle global. 
     468 */ 
     469static int __init leds_cpu_trigger_init(void) 
     470{ 
     471        int rc = led_trigger_register(&leds_cpu_trigger.trigger); 
     472        leds_idle = leds_cpu_trigger_idle; 
     473        return rc; 
     474} 
     475module_init(leds_cpu_trigger_init); 
     476 
     477static void __exit leds_cpu_trigger_exit(void) 
     478{ 
     479        leds_idle = 0; 
     480        del_timer_sync(&leds_cpu_trigger.timer); 
     481        led_trigger_unregister(&leds_cpu_trigger.trigger); 
     482} 
     483module_exit(leds_cpu_trigger_exit); 
     484 
     485MODULE_AUTHOR("John Bowler <jbowler@acm.org>"); 
     486MODULE_DESCRIPTION("CPU activity LED trigger"); 
     487MODULE_LICENSE("MIT"); 
  • linux-2.6.22-rc1-arm/include/linux/leds.h

    old new  
    110110#define ledtrig_ide_activity() do {} while(0) 
    111111#endif 
    112112 
     113/* 
     114 * CPU activity indication. 
     115 */ 
     116/* Idle callback - call with is_idle==1 at the start of the idle loop 
     117 * and with is_idle==0 at the end.  This symbol must be defined by 
     118 * the arch core to be able to use LEDS_TRIGGER_CPU_ACTIVITY 
     119 */ 
     120extern void (*leds_idle)(int is_idle); 
     121 
    113122#endif          /* __LINUX_LEDS_H_INCLUDED */ 
Note: See TracBrowser for help on using the browser.