⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cpufreq.c

📁 嵌入式系统设计与实例开发实验教材二源码 多线程应用程序设计 串行端口程序设计 AD接口实验 CAN总线通信实验 GPS通信实验 Linux内核移植与编译实验 IC卡读写实验 SD驱动使
💻 C
字号:
/* *  linux/kernel/cpufreq.c * *  Copyright (C) 2001 Russell King * *  $Id: cpufreq.c,v 1.17.2.2 2002/05/03 13:26:27 rmk 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 * * ** You'll need to add CTL_CPU = 10 to include/linux/sysctl.h if * it's not already present. * * When this appears in the kernel, the sysctl enums will move to * include/linux/sysctl.h */#include <linux/config.h>#include <linux/types.h>#include <linux/module.h>#include <linux/notifier.h>#include <linux/cpufreq.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/smp.h>#include <linux/fs.h>#include <linux/sysctl.h>#include <asm/semaphore.h>#include <asm/system.h>#include <asm/uaccess.h>#include <linux/interrupt.h>	/* 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_SMPstatic unsigned int __cpufreq_max;static unsigned int __cpufreq_min;static unsigned int __cpufreq_cur;#endifstatic 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_GPL(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_GPL(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_GPL(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_SYSCTLstatic intcpufreq_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 intcpufreq_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;}enum {	CPU_NR_FREQ_MAX = 1,	CPU_NR_FREQ_MIN = 2,	CPU_NR_FREQ = 3};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 */voidcpufreq_setfunctions(unsigned int (*validate)(unsigned int),		     void (*setspeed)(unsigned int)){	down(&cpufreq_sem);	cpufreq_validatespeed = validate;	cpufreq_setspeed = setspeed;	up(&cpufreq_sem);}EXPORT_SYMBOL_GPL(cpufreq_setfunctions);/** *	cpufreq_init - Initialise the cpufreq core *	@freq: current CPU clock speed. *	@min_freq: minimum CPU clock speed. *	@max_freq: maximum 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,		  unsigned int min_freq,		  unsigned int max_freq){	/*	 * If the user doesn't tell us their maximum frequency,	 * or if it is invalid, use the values determined 	 * by the cpufreq-arch-specific initialization functions.	 * The validatespeed code is responsible for limiting	 * this further.	 */	if (max_freq && ((cpufreq_max(0) == 0) || (cpufreq_max(0) > max_freq)))		cpufreq_max(0) = max_freq;	if (min_freq && ((cpufreq_min(0) == 0) || (cpufreq_min(0) < min_freq)))		cpufreq_min(0) = min_freq;	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_GPL(cpufreq_init);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -