cpufreq.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,167 行 · 第 1/2 页

C
1,167
字号
/* *  linux/drivers/cpufreq/cpufreq.c * *  Copyright (C) 2001 Russell King *            (C) 2002 - 2003 Dominik Brodowski <linux@brodo.de> * * 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. * */#include <linux/config.h>#include <linux/kernel.h>#include <linux/module.h>#include <linux/init.h>#include <linux/notifier.h>#include <linux/cpufreq.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/spinlock.h>#include <linux/device.h>#include <linux/slab.h>#include <linux/cpu.h>#include <linux/completion.h>/** * The "cpufreq driver" - the arch- or hardware-dependend low * level driver of CPUFreq support, and its spinlock. This lock * also protects the cpufreq_cpu_data array. */static struct cpufreq_driver   	*cpufreq_driver;static struct cpufreq_policy	*cpufreq_cpu_data[NR_CPUS];static spinlock_t		cpufreq_driver_lock = SPIN_LOCK_UNLOCKED;/* internal prototypes */static int __cpufreq_governor(struct cpufreq_policy *policy, unsigned int event);static void handle_update(void *data);static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci);/** * Two notifier lists: the "policy" list is involved in the  * validation process for a new CPU frequency policy; the  * "transition" list for kernel code that needs to handle * changes to devices when the CPU clock speed changes. * The mutex locks both lists. */static struct notifier_block    *cpufreq_policy_notifier_list;static struct notifier_block    *cpufreq_transition_notifier_list;static DECLARE_RWSEM		(cpufreq_notifier_rwsem);static LIST_HEAD(cpufreq_governor_list);static DECLARE_MUTEX		(cpufreq_governor_sem);static struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu){	struct cpufreq_policy *data;	unsigned long flags;	if (cpu >= NR_CPUS)		goto err_out;	/* get the cpufreq driver */	spin_lock_irqsave(&cpufreq_driver_lock, flags);	if (!cpufreq_driver)		goto err_out_unlock;	if (!try_module_get(cpufreq_driver->owner))		goto err_out_unlock;	/* get the CPU */	data = cpufreq_cpu_data[cpu];	if (!data)		goto err_out_put_module;	if (!kobject_get(&data->kobj))		goto err_out_put_module;	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);	return data; err_out_put_module:	module_put(cpufreq_driver->owner); err_out_unlock:	spin_unlock_irqrestore(&cpufreq_driver_lock, flags); err_out:	return NULL;}static void cpufreq_cpu_put(struct cpufreq_policy *data){	kobject_put(&data->kobj);	module_put(cpufreq_driver->owner);}/********************************************************************* *            EXTERNALLY AFFECTING FREQUENCY CHANGES                 * *********************************************************************//** * adjust_jiffies - adjust the system "loops_per_jiffy" * * This function alters the system "loops_per_jiffy" for the clock * speed change. Note that loops_per_jiffy cannot be updated on SMP * systems as each CPU might be scaled differently. So, use the arch  * per-CPU loops_per_jiffy value wherever possible. */#ifndef CONFIG_SMPstatic unsigned long l_p_j_ref;static unsigned int  l_p_j_ref_freq;static inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci){	if (ci->flags & CPUFREQ_CONST_LOOPS)		return;	if (!l_p_j_ref_freq) {		l_p_j_ref = loops_per_jiffy;		l_p_j_ref_freq = ci->old;	}	if ((val == CPUFREQ_PRECHANGE  && ci->old < ci->new) ||	    (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) ||	    (val == CPUFREQ_RESUMECHANGE))		loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new);}#elsestatic inline void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) { return; }#endif/** * cpufreq_notify_transition - call notifier chain and adjust_jiffies on frequency transition * * This function calls the transition notifiers and the "adjust_jiffies" function. It is called * twice on all CPU frequency changes that have external effects.  */void cpufreq_notify_transition(struct cpufreq_freqs *freqs, unsigned int state){	BUG_ON(irqs_disabled());	freqs->flags = cpufreq_driver->flags;	down_read(&cpufreq_notifier_rwsem);	switch (state) {	case CPUFREQ_PRECHANGE:		/* detect if the driver reported a value as "old frequency" which		 * is not equal to what the cpufreq core thinks is "old frequency".		 */		if (!(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {			if ((likely(cpufreq_cpu_data[freqs->cpu])) &&			    (likely(cpufreq_cpu_data[freqs->cpu]->cur)) &&			    (unlikely(freqs->old != cpufreq_cpu_data[freqs->cpu]->cur)))			{				printk(KERN_WARNING "Warning: CPU frequency is %u, "				       "cpufreq assumed %u kHz.\n", freqs->old, cpufreq_cpu_data[freqs->cpu]->cur);				freqs->old = cpufreq_cpu_data[freqs->cpu]->cur;			}		}		notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs);		adjust_jiffies(CPUFREQ_PRECHANGE, freqs);		break;	case CPUFREQ_POSTCHANGE:		adjust_jiffies(CPUFREQ_POSTCHANGE, freqs);		notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_POSTCHANGE, freqs);		if (likely(cpufreq_cpu_data[freqs->cpu]))			cpufreq_cpu_data[freqs->cpu]->cur = freqs->new;		break;	}	up_read(&cpufreq_notifier_rwsem);}EXPORT_SYMBOL_GPL(cpufreq_notify_transition);/********************************************************************* *                          SYSFS INTERFACE                          * *********************************************************************//** * cpufreq_parse_governor - parse a governor string */int cpufreq_parse_governor (char *str_governor, unsigned int *policy,				struct cpufreq_governor **governor){	if (!cpufreq_driver)		return -EINVAL;	if (cpufreq_driver->setpolicy) {		if (!strnicmp(str_governor, "performance", CPUFREQ_NAME_LEN)) {			*policy = CPUFREQ_POLICY_PERFORMANCE;			return 0;		} else if (!strnicmp(str_governor, "powersave", CPUFREQ_NAME_LEN)) {			*policy = CPUFREQ_POLICY_POWERSAVE;			return 0;		}		return -EINVAL;	} else {		struct cpufreq_governor *t;		down(&cpufreq_governor_sem);		if (!cpufreq_driver || !cpufreq_driver->target)			goto out;		list_for_each_entry(t, &cpufreq_governor_list, governor_list) {			if (!strnicmp(str_governor,t->name,CPUFREQ_NAME_LEN)) {				*governor = t;				up(&cpufreq_governor_sem);				return 0;			}		}	out:		up(&cpufreq_governor_sem);	}	return -EINVAL;}EXPORT_SYMBOL_GPL(cpufreq_parse_governor);/* drivers/base/cpu.c */extern struct sysdev_class cpu_sysdev_class;/** * cpufreq_per_cpu_attr_read() / show_##file_name() - print out cpufreq information * * Write out information from cpufreq_driver->policy[cpu]; object must be * "unsigned int". */#define show_one(file_name, object)		 			\static ssize_t show_##file_name 					\(struct cpufreq_policy * policy, char *buf)				\{									\	return sprintf (buf, "%u\n", policy->object);			\}show_one(cpuinfo_min_freq, cpuinfo.min_freq);show_one(cpuinfo_max_freq, cpuinfo.max_freq);show_one(scaling_min_freq, min);show_one(scaling_max_freq, max);show_one(scaling_cur_freq, cur);/** * cpufreq_per_cpu_attr_write() / store_##file_name() - sysfs write access */#define store_one(file_name, object)			\static ssize_t store_##file_name					\(struct cpufreq_policy * policy, const char *buf, size_t count)		\{									\	unsigned int ret = -EINVAL;					\	struct cpufreq_policy new_policy;				\									\	ret = cpufreq_get_policy(&new_policy, policy->cpu);		\	if (ret)							\		return -EINVAL;						\									\	ret = sscanf (buf, "%u", &new_policy.object);			\	if (ret != 1)							\		return -EINVAL;						\									\	ret = cpufreq_set_policy(&new_policy);				\									\	return ret ? ret : count;					\}store_one(scaling_min_freq,min);store_one(scaling_max_freq,max);/** * show_cpuinfo_cur_freq - current CPU frequency as detected by hardware */static ssize_t show_cpuinfo_cur_freq (struct cpufreq_policy * policy, char *buf){	unsigned int cur_freq = cpufreq_get(policy->cpu);	if (!cur_freq)		return sprintf(buf, "<unknown>");	return sprintf(buf, "%u\n", cur_freq);}/** * show_scaling_governor - show the current policy for the specified CPU */static ssize_t show_scaling_governor (struct cpufreq_policy * policy, char *buf){	if(policy->policy == CPUFREQ_POLICY_POWERSAVE)		return sprintf(buf, "powersave\n");	else if (policy->policy == CPUFREQ_POLICY_PERFORMANCE)		return sprintf(buf, "performance\n");	else if (policy->governor)		return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n", policy->governor->name);	return -EINVAL;}/** * store_scaling_governor - store policy for the specified CPU */static ssize_t store_scaling_governor (struct cpufreq_policy * policy, 				       const char *buf, size_t count) {	unsigned int ret = -EINVAL;	char	str_governor[16];	struct cpufreq_policy new_policy;	ret = cpufreq_get_policy(&new_policy, policy->cpu);	if (ret)		return ret;	ret = sscanf (buf, "%15s", str_governor);	if (ret != 1)		return -EINVAL;	if (cpufreq_parse_governor(str_governor, &new_policy.policy, &new_policy.governor))		return -EINVAL;	ret = cpufreq_set_policy(&new_policy);	return ret ? ret : count;}/** * show_scaling_driver - show the cpufreq driver currently loaded */static ssize_t show_scaling_driver (struct cpufreq_policy * policy, char *buf){	return scnprintf(buf, CPUFREQ_NAME_LEN, "%s\n", cpufreq_driver->name);}/** * show_scaling_available_governors - show the available CPUfreq governors */static ssize_t show_scaling_available_governors (struct cpufreq_policy * policy,				char *buf){	ssize_t i = 0;	struct cpufreq_governor *t;	if (!cpufreq_driver->target) {		i += sprintf(buf, "performance powersave");		goto out;	}	list_for_each_entry(t, &cpufreq_governor_list, governor_list) {		if (i >= (ssize_t) ((PAGE_SIZE / sizeof(char)) - (CPUFREQ_NAME_LEN + 2)))			goto out;		i += scnprintf(&buf[i], CPUFREQ_NAME_LEN, "%s ", t->name);	} out:	i += sprintf(&buf[i], "\n");	return i;}#define define_one_ro(_name) \struct freq_attr _name = { \	.attr = { .name = __stringify(_name), .mode = 0444 }, \	.show = show_##_name, \}#define define_one_ro0400(_name) \struct freq_attr _name = { \	.attr = { .name = __stringify(_name), .mode = 0400 }, \	.show = show_##_name, \}#define define_one_rw(_name) \struct freq_attr _name = { \	.attr = { .name = __stringify(_name), .mode = 0644 }, \	.show = show_##_name, \	.store = store_##_name, \}define_one_ro0400(cpuinfo_cur_freq);define_one_ro(cpuinfo_min_freq);define_one_ro(cpuinfo_max_freq);define_one_ro(scaling_available_governors);define_one_ro(scaling_driver);define_one_ro(scaling_cur_freq);define_one_rw(scaling_min_freq);define_one_rw(scaling_max_freq);define_one_rw(scaling_governor);static struct attribute * default_attrs[] = {	&cpuinfo_min_freq.attr,	&cpuinfo_max_freq.attr,	&scaling_min_freq.attr,	&scaling_max_freq.attr,	&scaling_governor.attr,	&scaling_driver.attr,	&scaling_available_governors.attr,	NULL};#define to_policy(k) container_of(k,struct cpufreq_policy,kobj)#define to_attr(a) container_of(a,struct freq_attr,attr)static ssize_t show(struct kobject * kobj, struct attribute * attr ,char * buf){	struct cpufreq_policy * policy = to_policy(kobj);	struct freq_attr * fattr = to_attr(attr);	ssize_t ret;	policy = cpufreq_cpu_get(policy->cpu);	if (!policy)		return -EINVAL;	ret = fattr->show ? fattr->show(policy,buf) : 0;	cpufreq_cpu_put(policy);	return ret;}static ssize_t store(struct kobject * kobj, struct attribute * attr, 		     const char * buf, size_t count){	struct cpufreq_policy * policy = to_policy(kobj);	struct freq_attr * fattr = to_attr(attr);	ssize_t ret;	policy = cpufreq_cpu_get(policy->cpu);	if (!policy)		return -EINVAL;	ret = fattr->store ? fattr->store(policy,buf,count) : 0;	cpufreq_cpu_put(policy);	return ret;}static void cpufreq_sysfs_release(struct kobject * kobj){	struct cpufreq_policy * policy = to_policy(kobj);	complete(&policy->kobj_unregister);}static struct sysfs_ops sysfs_ops = {	.show	= show,	.store	= store,};static struct kobj_type ktype_cpufreq = {	.sysfs_ops	= &sysfs_ops,	.default_attrs	= default_attrs,	.release	= cpufreq_sysfs_release,};/** * cpufreq_add_dev - add a CPU device * * Adds the cpufreq interface for a CPU device.  */static int cpufreq_add_dev (struct sys_device * sys_dev){	unsigned int cpu = sys_dev->id;	int ret = 0;	struct cpufreq_policy new_policy;	struct cpufreq_policy *policy;	struct freq_attr **drv_attr;	unsigned long flags;	if (!try_module_get(cpufreq_driver->owner))		return -EINVAL;	policy = kmalloc(sizeof(struct cpufreq_policy), GFP_KERNEL);	if (!policy) {		ret = -ENOMEM;		goto nomem_out;	}	memset(policy, 0, sizeof(struct cpufreq_policy));	policy->cpu = cpu;	init_MUTEX_LOCKED(&policy->lock);	init_completion(&policy->kobj_unregister);	INIT_WORK(&policy->update, handle_update, (void *)(long)cpu);	/* call driver. From then on the cpufreq must be able	 * to accept all calls to ->verify and ->setpolicy for this CPU	 */	ret = cpufreq_driver->init(policy);	if (ret)		goto err_out;	memcpy(&new_policy, policy, sizeof(struct cpufreq_policy));	/* prepare interface data */	policy->kobj.parent = &sys_dev->kobj;	policy->kobj.ktype = &ktype_cpufreq;	strlcpy(policy->kobj.name, "cpufreq", KOBJ_NAME_LEN);	ret = kobject_register(&policy->kobj);	if (ret)		goto err_out;	/* set up files for this cpu device */	drv_attr = cpufreq_driver->attr;	while ((drv_attr) && (*drv_attr)) {		sysfs_create_file(&policy->kobj, &((*drv_attr)->attr));		drv_attr++;	}	if (cpufreq_driver->get)		sysfs_create_file(&policy->kobj, &cpuinfo_cur_freq.attr);	if (cpufreq_driver->target)		sysfs_create_file(&policy->kobj, &scaling_cur_freq.attr);	spin_lock_irqsave(&cpufreq_driver_lock, flags);	cpufreq_cpu_data[cpu] = policy;	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);	policy->governor = NULL; /* to assure that the starting sequence is				  * run in cpufreq_set_policy */	up(&policy->lock);		/* set default policy */		ret = cpufreq_set_policy(&new_policy);	if (ret)		goto err_out_unregister;	module_put(cpufreq_driver->owner);	return 0;err_out_unregister:	spin_lock_irqsave(&cpufreq_driver_lock, flags);	cpufreq_cpu_data[cpu] = NULL;	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);	kobject_unregister(&policy->kobj);	wait_for_completion(&policy->kobj_unregister);err_out:	kfree(policy);nomem_out:	module_put(cpufreq_driver->owner);	return ret;}/** * cpufreq_remove_dev - remove a CPU device * * Removes the cpufreq interface for a CPU device. */static int cpufreq_remove_dev (struct sys_device * sys_dev){	unsigned int cpu = sys_dev->id;	unsigned long flags;	struct cpufreq_policy *data;	spin_lock_irqsave(&cpufreq_driver_lock, flags);	data = cpufreq_cpu_data[cpu];	if (!data) {		spin_unlock_irqrestore(&cpufreq_driver_lock, flags);		return -EINVAL;	}	cpufreq_cpu_data[cpu] = NULL;	spin_unlock_irqrestore(&cpufreq_driver_lock, flags);	if (!kobject_get(&data->kobj))		return -EFAULT;	if (cpufreq_driver->target)		__cpufreq_governor(data, CPUFREQ_GOV_STOP);	kobject_unregister(&data->kobj);	kobject_put(&data->kobj);	/* we need to make sure that the underlying kobj is actually	 * not referenced anymore by anybody before we proceed with 	 * unloading.	 */	wait_for_completion(&data->kobj_unregister);	if (cpufreq_driver->exit)		cpufreq_driver->exit(data);	kfree(data);	return 0;}static void handle_update(void *data)

⌨️ 快捷键说明

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