/* * linux/drivers/cpufreq/cpufreq_dynamic.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * *** Experimental! *** * * TODO: * - Handle more than CPU 0. * - Put in a better stepping mechanism. * - Step downwards slower than stepping upwards. (I think this would * work more efficiently than as it is now.) * - Documenting in Documentation/cpufreq * - Documentation that is NOT crappy in this file. * - Proper debug printing. * */ #include #include #include #include #include #include #include #include /* How many times/second to poll */ #define POLL_FREQUENCY 10 /* Wait HZ/POLL_FREQUENCY jiffies in between polling. */ #define SLEEP_JIFFIES HZ/POLL_FREQUENCY /* Minimum and maximum idle thresholds, in percent */ #define MIN_IDLE 20 #define MAX_IDLE 80 /* Internal definitions for temporary frequency table. Ug-ly. */ #define FREQ_MAX 0 #define FREQ_MIN -1 /** Should be a list, from FREQ_MAX to FREQ_MIN of CPU speeds in KHz. * e.g. { FREQ_MAX,1500000,1000000,500000,FREQ_MIN } */ static const int32_t cpufreq_dynamic_freqs[] = { FREQ_MAX, \ /** * FIXME: Find better way to find frequencies to switch to. * * These values work on my non-mobile P4 2 GHz, but * would probably just waste CPU cycles on most other * CPUs. (I.e. these values would probably be invalid.) * * 1500000, \ * 1000000, \ * 500000, \ * 250000, \ */ FREQ_MIN \ }; /* Kernel Timer for this module. (Timers are awesome!) */ struct timer_list cpufreq_dynamic_timer; unsigned long prev_idle; // CPU idle time, in jiffies. unsigned long percent_idle; // Idle percentage. uint8_t cpufreq_dynamic_cur_freq_id; // What ID from the stupid table we're at. /** * Timer callback */ static void cpufreq_governor_dynamic_callback(unsigned long t) { percent_idle = POLL_FREQUENCY * 100 * (kstat_cpu(0).cpustat.idle - prev_idle) / HZ; prev_idle = kstat_cpu(0).cpustat.idle; if ((percent_idle > MAX_IDLE) || (percent_idle < MIN_IDLE)) { cpufreq_governor(0,CPUFREQ_GOV_LIMITS); } cpufreq_dynamic_timer.expires = jiffies + SLEEP_JIFFIES; add_timer(&cpufreq_dynamic_timer); } /** * The governor itself. * * DOCUMENT ME! */ static int cpufreq_governor_dynamic(struct cpufreq_policy *policy, unsigned int event) { switch (event) { case CPUFREQ_GOV_START: cpufreq_dynamic_timer.expires = jiffies + SLEEP_JIFFIES; cpufreq_dynamic_timer.function = cpufreq_governor_dynamic_callback; cpufreq_dynamic_cur_freq_id = 0; prev_idle = kstat_cpu(0).cpustat.idle; init_timer(&cpufreq_dynamic_timer); add_timer(&cpufreq_dynamic_timer); case CPUFREQ_GOV_LIMITS: if (percent_idle > MAX_IDLE) { if (cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id] != FREQ_MIN) { if (cpufreq_dynamic_freqs[++cpufreq_dynamic_cur_freq_id] == FREQ_MIN) { __cpufreq_driver_target(policy,policy->min,CPUFREQ_RELATION_L); } else { //printk("cpufreq_dynamic: Attempting to downgrade frequency: %i KHz\n",cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id]); __cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id],CPUFREQ_RELATION_L); } } } else if (percent_idle < MIN_IDLE) { if (cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id] != FREQ_MAX) { if (cpufreq_dynamic_freqs[--cpufreq_dynamic_cur_freq_id] == FREQ_MAX) { __cpufreq_driver_target(policy,policy->max,CPUFREQ_RELATION_L); } else { //printk("cpufreq_dynamic: Attempting to upgrade frequency: %i KHz\n",cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id]); __cpufreq_driver_target(policy,cpufreq_dynamic_freqs[cpufreq_dynamic_cur_freq_id],CPUFREQ_RELATION_L); } } } break; case CPUFREQ_GOV_STOP: del_timer(&cpufreq_dynamic_timer); break; default: break; } return 0; } static struct cpufreq_governor cpufreq_gov_dynamic = { .name = "dynamic", .governor = cpufreq_governor_dynamic, .owner = THIS_MODULE, }; EXPORT_SYMBOL(cpufreq_gov_dynamic); static int __init cpufreq_gov_dynamic_init(void) { return cpufreq_register_governor(&cpufreq_gov_dynamic); } static void __exit cpufreq_gov_dynamic_exit(void) { cpufreq_unregister_governor(&cpufreq_gov_dynamic); } MODULE_AUTHOR("Jonathan Anderson