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 + -
显示快捷键?