#include <linux/slab.h>
#include <linux/cpu.h>
#include <linux/completion.h>
+#include <linux/mutex.h>
#define dprintk(msg...) cpufreq_debug_printk(CPUFREQ_DEBUG_CORE, "cpufreq-core", msg)
* 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 struct cpufreq_driver *cpufreq_driver;
+static struct cpufreq_policy *cpufreq_cpu_data[NR_CPUS];
static DEFINE_SPINLOCK(cpufreq_driver_lock);
/* internal prototypes */
* 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 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 DEFINE_MUTEX (cpufreq_governor_mutex);
-struct cpufreq_policy * cpufreq_cpu_get(unsigned int cpu)
+struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu)
{
struct cpufreq_policy *data;
unsigned long flags;
if (!kobject_get(&data->kobj))
goto err_out_put_module;
-
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
-
return data;
- err_out_put_module:
+err_out_put_module:
module_put(cpufreq_driver->owner);
- err_out_unlock:
+err_out_unlock:
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
- err_out:
+err_out:
return NULL;
}
EXPORT_SYMBOL_GPL(cpufreq_cpu_get);
+
void cpufreq_cpu_put(struct cpufreq_policy *data)
{
kobject_put(&data->kobj);
/**
- * cpufreq_notify_transition - call notifier chain and adjust_jiffies on frequency transition
+ * 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.
+ * 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)
{
+ struct cpufreq_policy *policy;
+
BUG_ON(irqs_disabled());
freqs->flags = cpufreq_driver->flags;
- dprintk("notification %u of frequency transition to %u kHz\n", state, freqs->new);
+ dprintk("notification %u of frequency transition to %u kHz\n",
+ state, freqs->new);
down_read(&cpufreq_notifier_rwsem);
+
+ policy = cpufreq_cpu_data[freqs->cpu];
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".
+ /* 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]->cpu == freqs->cpu)) &&
- (likely(cpufreq_cpu_data[freqs->cpu]->cur)) &&
- (unlikely(freqs->old != cpufreq_cpu_data[freqs->cpu]->cur)))
- {
- dprintk(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;
+ if ((policy) && (policy->cpu == freqs->cpu) &&
+ (policy->cur) && (policy->cur != freqs->old)) {
+ dprintk(KERN_WARNING "Warning: CPU frequency is"
+ " %u, cpufreq assumed %u kHz.\n",
+ freqs->old, policy->cur);
+ freqs->old = policy->cur;
}
}
- notifier_call_chain(&cpufreq_transition_notifier_list, CPUFREQ_PRECHANGE, freqs);
+ 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])) &&
- (likely(cpufreq_cpu_data[freqs->cpu]->cpu == freqs->cpu)))
- cpufreq_cpu_data[freqs->cpu]->cur = freqs->new;
+ notifier_call_chain(&cpufreq_transition_notifier_list,
+ CPUFREQ_POSTCHANGE, freqs);
+ if (likely(policy) && likely(policy->cpu == freqs->cpu))
+ policy->cur = freqs->new;
break;
}
up_read(&cpufreq_notifier_rwsem);
return -EINVAL;
} else {
struct cpufreq_governor *t;
- down(&cpufreq_governor_sem);
+ mutex_lock(&cpufreq_governor_mutex);
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);
+ mutex_unlock(&cpufreq_governor_mutex);
return 0;
}
}
- out:
- up(&cpufreq_governor_sem);
+out:
+ mutex_unlock(&cpufreq_governor_mutex);
}
return -EINVAL;
}
return -EINVAL;
ret = cpufreq_set_policy(&new_policy);
-
return ret ? ret : count;
}
goto out;
i += scnprintf(&buf[i], CPUFREQ_NAME_LEN, "%s ", t->name);
}
- out:
+out:
i += sprintf(&buf[i], "\n");
return i;
}
policy->cpu = cpu;
policy->cpus = cpumask_of_cpu(cpu);
- init_MUTEX_LOCKED(&policy->lock);
+ mutex_init(&policy->lock);
+ mutex_lock(&policy->lock);
init_completion(&policy->kobj_unregister);
INIT_WORK(&policy->update, handle_update, (void *)(long)cpu);
ret = cpufreq_driver->init(policy);
if (ret) {
dprintk("initialization failed\n");
+ mutex_unlock(&policy->lock);
goto err_out;
}
strlcpy(policy->kobj.name, "cpufreq", KOBJ_NAME_LEN);
ret = kobject_register(&policy->kobj);
- if (ret)
+ if (ret) {
+ mutex_unlock(&policy->lock);
goto err_out_driver_exit;
-
+ }
/* set up files for this cpu device */
drv_attr = cpufreq_driver->attr;
while ((drv_attr) && (*drv_attr)) {
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);
+ mutex_unlock(&policy->lock);
/* set default policy */
spin_unlock_irqrestore(&cpufreq_driver_lock, flags);
#endif
- down(&data->lock);
+ mutex_lock(&data->lock);
if (cpufreq_driver->target)
__cpufreq_governor(data, CPUFREQ_GOV_STOP);
- up(&data->lock);
+ mutex_unlock(&data->lock);
kobject_unregister(&data->kobj);
kfree(data);
cpufreq_debug_enable_ratelimit();
-
return 0;
}
unsigned int ret = 0;
if (policy) {
- down(&policy->lock);
+ mutex_lock(&policy->lock);
ret = policy->cur;
- up(&policy->lock);
+ mutex_unlock(&policy->lock);
cpufreq_cpu_put(policy);
}
if (!cpufreq_driver->get)
goto out;
- down(&policy->lock);
+ mutex_lock(&policy->lock);
ret = cpufreq_driver->get(cpu);
- if (ret && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS))
- {
+ if (ret && policy->cur && !(cpufreq_driver->flags & CPUFREQ_CONST_LOOPS)) {
/* verify no discrepancy between actual and saved value exists */
if (unlikely(ret != policy->cur)) {
cpufreq_out_of_sync(cpu, policy->cur, ret);
}
}
- up(&policy->lock);
+ mutex_unlock(&policy->lock);
- out:
+out:
cpufreq_cpu_put(policy);
return (ret);
cpu_policy->cur = cur_freq;
}
- out:
+out:
cpufreq_cpu_put(cpu_policy);
return 0;
}
if (!policy)
return -EINVAL;
- down(&policy->lock);
+ mutex_lock(&policy->lock);
ret = __cpufreq_driver_target(policy, target_freq, relation);
- up(&policy->lock);
+ mutex_unlock(&policy->lock);
cpufreq_cpu_put(policy);
-
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_driver_target);
if (!policy)
return -EINVAL;
- down(&policy->lock);
+ mutex_lock(&policy->lock);
ret = __cpufreq_governor(policy, event);
- up(&policy->lock);
+ mutex_unlock(&policy->lock);
cpufreq_cpu_put(policy);
-
return ret;
}
EXPORT_SYMBOL_GPL(cpufreq_governor);
if (!governor)
return -EINVAL;
- down(&cpufreq_governor_sem);
+ mutex_lock(&cpufreq_governor_mutex);
list_for_each_entry(t, &cpufreq_governor_list, governor_list) {
if (!strnicmp(governor->name,t->name,CPUFREQ_NAME_LEN)) {
- up(&cpufreq_governor_sem);
+ mutex_unlock(&cpufreq_governor_mutex);
return -EBUSY;
}
}
list_add(&governor->governor_list, &cpufreq_governor_list);
- up(&cpufreq_governor_sem);
-
+ mutex_unlock(&cpufreq_governor_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(cpufreq_register_governor);
if (!governor)
return;
- down(&cpufreq_governor_sem);
+ mutex_lock(&cpufreq_governor_mutex);
list_del(&governor->governor_list);
- up(&cpufreq_governor_sem);
+ mutex_unlock(&cpufreq_governor_mutex);
return;
}
EXPORT_SYMBOL_GPL(cpufreq_unregister_governor);
if (!cpu_policy)
return -EINVAL;
- down(&cpu_policy->lock);
+ mutex_lock(&cpu_policy->lock);
memcpy(policy, cpu_policy, sizeof(struct cpufreq_policy));
- up(&cpu_policy->lock);
+ mutex_unlock(&cpu_policy->lock);
cpufreq_cpu_put(cpu_policy);
-
return 0;
}
EXPORT_SYMBOL(cpufreq_get_policy);
dprintk("setting new policy for CPU %u: %u - %u kHz\n", policy->cpu,
policy->min, policy->max);
- memcpy(&policy->cpuinfo,
- &data->cpuinfo,
- sizeof(struct cpufreq_cpuinfo));
+ memcpy(&policy->cpuinfo, &data->cpuinfo, sizeof(struct cpufreq_cpuinfo));
/* verify the cpu speed can be set within this limit */
ret = cpufreq_driver->verify(policy);
up_read(&cpufreq_notifier_rwsem);
- data->min = policy->min;
- data->max = policy->max;
+ data->min = policy->min;
+ data->max = policy->max;
dprintk("new min and max freqs are %u - %u kHz\n", data->min, data->max);
__cpufreq_governor(data, CPUFREQ_GOV_LIMITS);
}
- error_out:
+error_out:
cpufreq_debug_enable_ratelimit();
return ret;
}
return -EINVAL;
/* lock this CPU */
- down(&data->lock);
+ mutex_lock(&data->lock);
ret = __cpufreq_set_policy(data, policy);
data->user_policy.min = data->min;
data->user_policy.policy = data->policy;
data->user_policy.governor = data->governor;
- up(&data->lock);
+ mutex_unlock(&data->lock);
cpufreq_cpu_put(data);
return ret;
if (!data)
return -ENODEV;
- down(&data->lock);
+ mutex_lock(&data->lock);
dprintk("updating policy for CPU %u\n", cpu);
- memcpy(&policy,
- data,
- sizeof(struct cpufreq_policy));
+ memcpy(&policy, data, sizeof(struct cpufreq_policy));
policy.min = data->user_policy.min;
policy.max = data->user_policy.max;
policy.policy = data->user_policy.policy;
policy.governor = data->user_policy.governor;
+ /* BIOS might change freq behind our back
+ -> ask driver for current freq and notify governors about a change */
+ if (cpufreq_driver->get) {
+ policy.cur = cpufreq_driver->get(cpu);
+ if (!data->cur) {
+ dprintk("Driver did not initialize current freq");
+ data->cur = policy.cur;
+ } else {
+ if (data->cur != policy.cur)
+ cpufreq_out_of_sync(cpu, data->cur, policy.cur);
+ }
+ }
+
ret = __cpufreq_set_policy(data, &policy);
- up(&data->lock);
+ mutex_unlock(&data->lock);
cpufreq_cpu_put(data);
return ret;