/* * linux/kernel/cpufreq.c * * Copyright (C) 2001 Russell King * * $Id: cpufreq.c,v 1.1.1.1 2003/06/23 22:18:44 jharrell Exp $ * * 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. * * CPU speed changing core functionality. We provide the following * services to the system: * - notifier lists to inform other code of the freq change both * before and after the freq change. * - the ability to change the freq speed * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* requires system.h */ /* * This list is for kernel code that needs to handle * changes to devices when the CPU clock speed changes. */ static struct notifier_block *cpufreq_notifier_list; static DECLARE_MUTEX_LOCKED(cpufreq_sem); static unsigned long cpufreq_ref_loops; static unsigned int cpufreq_ref_freq; static int cpufreq_initialised; static unsigned int (*cpufreq_validatespeed)(unsigned int); static void (*cpufreq_setspeed)(unsigned int); #ifndef CONFIG_SMP static unsigned int __cpufreq_max; static unsigned int __cpufreq_min; static unsigned int __cpufreq_cur; #endif static unsigned long scale(unsigned long ref, u_int div, u_int mult) { unsigned long new_jiffy_l, new_jiffy_h; /* * Recalculate loops_per_jiffy. We do it this way to * avoid math overflow on 32-bit machines. Maybe we * should make this architecture dependent? If you have * a better way of doing this, please replace! * * new = old * mult / div */ new_jiffy_h = ref / div; new_jiffy_l = (ref % div) / 100; new_jiffy_h *= mult; new_jiffy_l = new_jiffy_l * mult / div; return new_jiffy_h + new_jiffy_l * 100; } /* * cpufreq_max command line parameter. Use: * cpufreq=59000-221000 * to set the CPU frequency to 59 to 221MHz. */ static int __init cpufreq_setup(char *str) { unsigned int min, max; int i; min = 0; max = simple_strtoul(str, &str, 0); if (*str == '-') { min = max; max = simple_strtoul(str + 1, NULL, 0); } for (i = 0; i < smp_num_cpus; i++) { cpufreq_max(i) = max; cpufreq_min(i) = min; } return 1; } __setup("cpufreq=", cpufreq_setup); /** * cpufreq_register_notifier - register a driver with cpufreq * @nb: notifier function to register * * Add a driver to the list of drivers that which to be notified about * CPU clock rate changes. The driver will be called three times on * clock change. * * This function may sleep, and has the same return conditions as * notifier_chain_register. */ int cpufreq_register_notifier(struct notifier_block *nb) { int ret; down(&cpufreq_sem); ret = notifier_chain_register(&cpufreq_notifier_list, nb); up(&cpufreq_sem); return ret; } EXPORT_SYMBOL(cpufreq_register_notifier); /** * cpufreq_unregister_notifier - unregister a driver with cpufreq * @nb: notifier block to be unregistered * * Remove a driver from the CPU frequency notifier lists. * * This function may sleep, and has the same return conditions as * notifier_chain_unregister. */ int cpufreq_unregister_notifier(struct notifier_block *nb) { int ret; down(&cpufreq_sem); ret = notifier_chain_unregister(&cpufreq_notifier_list, nb); up(&cpufreq_sem); return ret; } EXPORT_SYMBOL(cpufreq_unregister_notifier); /* * This notifier alters the system "loops_per_jiffy" for the clock * speed change. We ignore CPUFREQ_MINMAX here. */ static void adjust_jiffies(unsigned long val, struct cpufreq_info *ci) { if ((val == CPUFREQ_PRECHANGE && ci->old_freq < ci->new_freq) || (val == CPUFREQ_POSTCHANGE && ci->old_freq > ci->new_freq)) loops_per_jiffy = scale(cpufreq_ref_loops, cpufreq_ref_freq, ci->new_freq); } #ifdef CONFIG_PM /** * cpufreq_restore - restore the CPU clock frequency after resume * * Restore the CPU clock frequency so that our idea of the current * frequency reflects the actual hardware. */ int cpufreq_restore(void) { unsigned long old_cpus; int cpu = smp_processor_id(); int ret; if (!cpufreq_initialised) panic("cpufreq_restore() called before initialisation!"); if (in_interrupt()) panic("cpufreq_restore() called from interrupt context!"); /* * Bind to the current CPU. */ old_cpus = current->cpus_allowed; current->cpus_allowed = 1UL << cpu_logical_map(cpu); down(&cpufreq_sem); ret = -ENXIO; if (cpufreq_setspeed) { cpufreq_setspeed(cpufreq_current(cpu)); ret = 0; } up(&cpufreq_sem); current->cpus_allowed = old_cpus; return ret; } EXPORT_SYMBOL(cpufreq_restore); #endif /** * cpu_setfreq - change the CPU clock frequency. * @freq: frequency (in kHz) at which we should run. * * Set the CPU clock frequency, informing all registered users of * the change. We bound the frequency according to the cpufreq_max * command line parameter, and the parameters the registered users * will allow. * * This function must be called from process context, and on the * cpu that we wish to change the frequency of. * * We return 0 if successful. (we are currently always successful). */ int cpufreq_set(unsigned int freq) { unsigned long old_cpus; struct cpufreq_info clkinfo; struct cpufreq_minmax minmax; int cpu = smp_processor_id(); int ret; if (!cpufreq_initialised) panic("cpufreq_set() called before initialisation!"); if (in_interrupt()) panic("cpufreq_set() called from interrupt context!"); /* * Bind to the current CPU. */ old_cpus = current->cpus_allowed; current->cpus_allowed = 1UL << cpu_logical_map(cpu); down(&cpufreq_sem); ret = -ENXIO; if (!cpufreq_setspeed || !cpufreq_validatespeed) goto out; /* * Don't allow the CPU to be clocked over the limit. */ minmax.min_freq = cpufreq_min(cpu); minmax.max_freq = cpufreq_max(cpu); minmax.cur_freq = cpufreq_current(cpu); minmax.new_freq = freq; /* * Find out what the registered devices will currently tolerate, * and limit the requested clock rate to these values. Drivers * must not rely on the 'new_freq' value - it is only a guide. */ notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_MINMAX, &minmax); if (freq < minmax.min_freq) freq = minmax.min_freq; if (freq > minmax.max_freq) freq = minmax.max_freq; /* * Ask the CPU specific code to validate the speed. If the speed * is not acceptable, make it acceptable. Current policy is to * round the frequency down to the value the processor actually * supports. */ freq = cpufreq_validatespeed(freq); if (cpufreq_current(cpu) != freq) { clkinfo.old_freq = cpufreq_current(cpu); clkinfo.new_freq = freq; notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_PRECHANGE, &clkinfo); adjust_jiffies(CPUFREQ_PRECHANGE, &clkinfo); /* * Actually set the CPU frequency. */ cpufreq_setspeed(freq); cpufreq_current(cpu) = freq; adjust_jiffies(CPUFREQ_POSTCHANGE, &clkinfo); notifier_call_chain(&cpufreq_notifier_list, CPUFREQ_POSTCHANGE, &clkinfo); } ret = 0; out: up(&cpufreq_sem); current->cpus_allowed = old_cpus; return ret; } EXPORT_SYMBOL(cpufreq_set); /** * cpufreq_setmax - set the CPU to maximum frequency * * Sets the CPU this function is executed on to maximum frequency. */ int cpufreq_setmax(void) { return cpufreq_set(cpufreq_max(smp_processor_id())); } EXPORT_SYMBOL(cpufreq_setmax); /** * cpufreq_get - return the current CPU clock frequency in 1kHz * @cpu: cpu number to obtain frequency for * * Returns the specified CPUs frequency in kHz. */ unsigned int cpufreq_get(int cpu) { if (!cpufreq_initialised) panic("cpufreq_get() called before initialisation!"); return cpufreq_current(cpu); } EXPORT_SYMBOL(cpufreq_get); #ifdef CONFIG_SYSCTL static int cpufreq_procctl(ctl_table *ctl, int write, struct file *filp, void *buffer, size_t *lenp) { char buf[16], *p; int cpu = 0, len, left = *lenp; if (!left || (filp->f_pos && !write)) { *lenp = 0; return 0; } if (write) { unsigned int freq; len = left; if (left > sizeof(buf)) left = sizeof(buf); if (copy_from_user(buf, buffer, left)) return -EFAULT; buf[sizeof(buf) - 1] = '\0'; freq = simple_strtoul(buf, &p, 0); cpufreq_set(freq); } else { len = sprintf(buf, "%d\n", cpufreq_get(cpu)); if (len > left) len = left; if (copy_to_user(buffer, buf, len)) return -EFAULT; } *lenp = len; filp->f_pos += len; return 0; } static int cpufreq_sysctl(ctl_table *table, int *name, int nlen, void *oldval, size_t *oldlenp, void *newval, size_t newlen, void **context) { int cpu = 0; if (oldval && oldlenp) { size_t oldlen; if (get_user(oldlen, oldlenp)) return -EFAULT; if (oldlen != sizeof(unsigned int)) return -EINVAL; if (put_user(cpufreq_get(cpu), (unsigned int *)oldval) || put_user(sizeof(unsigned int), oldlenp)) return -EFAULT; } if (newval && newlen) { unsigned int freq; if (newlen != sizeof(unsigned int)) return -EINVAL; if (get_user(freq, (unsigned int *)newval)) return -EFAULT; cpufreq_set(freq); } return 1; } static ctl_table ctl_cpu_vars[4] = { { ctl_name: CPU_NR_FREQ_MAX, procname: "speed-max", data: &cpufreq_max(0), maxlen: sizeof(cpufreq_max(0)), mode: 0444, proc_handler: proc_dointvec, }, { ctl_name: CPU_NR_FREQ_MIN, procname: "speed-min", data: &cpufreq_min(0), maxlen: sizeof(cpufreq_min(0)), mode: 0444, proc_handler: proc_dointvec, }, { ctl_name: CPU_NR_FREQ, procname: "speed", mode: 0644, proc_handler: cpufreq_procctl, strategy: cpufreq_sysctl, }, { ctl_name: 0, } }; enum { CPU_NR = 1, }; static ctl_table ctl_cpu_nr[2] = { { ctl_name: CPU_NR, procname: "0", mode: 0555, child: ctl_cpu_vars, }, { ctl_name: 0, } }; static ctl_table ctl_cpu[2] = { { ctl_name: CTL_CPU, procname: "cpu", mode: 0555, child: ctl_cpu_nr, }, { ctl_name: 0, } }; static inline void cpufreq_sysctl_init(void) { register_sysctl_table(ctl_cpu, 0); } #else #define cpufreq_sysctl_init() #endif /** * cpufreq_setfunctions - Set CPU clock functions * @validate: pointer to validation function * @setspeed: pointer to setspeed function */ void __init cpufreq_setfunctions(unsigned int (*validate)(unsigned int), void (*setspeed)(unsigned int)) { down(&cpufreq_sem); cpufreq_validatespeed = validate; cpufreq_setspeed = setspeed; up(&cpufreq_sem); } EXPORT_SYMBOL(cpufreq_setfunctions); /** * cpufreq_init - Initialise the cpufreq core * @freq: current CPU clock speed. * * Initialise the cpufreq core. If the cpufreq_max command line * parameter has not been specified, we set the maximum clock rate * to the current CPU clock rate. */ void cpufreq_init(unsigned int freq) { /* * If the user doesn't tell us their maximum frequency, * then we default to the current CPU frequency. * The validatespeed code is responsible for limiting * this further. */ if (cpufreq_max(0) == 0) cpufreq_max(0) = freq; printk(KERN_INFO "CPU clock: %d.%03d MHz (%d.%03d-%d.%03d MHz)\n", freq / 1000, freq % 1000, cpufreq_min(0) / 1000, cpufreq_min(0) % 1000, cpufreq_max(0) / 1000, cpufreq_max(0) % 1000); cpufreq_ref_loops = loops_per_jiffy; cpufreq_ref_freq = freq; cpufreq_current(smp_processor_id()) = freq; cpufreq_initialised = 1; up(&cpufreq_sem); cpufreq_sysctl_init(); } EXPORT_SYMBOL(cpufreq_init);