Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update arch/arm/mach-msm/cpufreq.c #5

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 86 additions & 168 deletions arch/arm/mach-msm/cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@
#include <linux/cpufreq.h>
#include <linux/workqueue.h>
#include <linux/completion.h>
#include <linux/mutex.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/sched.h>
#include <linux/suspend.h>

#include "acpuclock.h"
#include "cpufreq.h"

#ifdef CONFIG_SMP
struct cpufreq_work_struct {
Expand All @@ -39,40 +41,77 @@ struct cpufreq_work_struct {
};

static DEFINE_PER_CPU(struct cpufreq_work_struct, cpufreq_work);
static struct workqueue_struct *msm_cpufreq_wq;
#endif

struct cpufreq_suspend_t {
struct mutex suspend_mutex;
int device_suspended;
};
static DEFINE_MUTEX(msm_cpufreq_voter_lock);
static int msm_cpufreq_vote = MSM_CPUFREQ_IDLE;
static LIST_HEAD(msm_cpufreq_voters);

#define dprintk(msg...) \
cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-msm", msg)

static DEFINE_PER_CPU(struct cpufreq_suspend_t, cpufreq_suspend);
static int msm_cpufreq_check_votes(void)
{
struct msm_cpufreq_voter *voter;
int vote = MSM_CPUFREQ_IDLE;

static int override_cpu;
list_for_each_entry(voter, &msm_cpufreq_voters, item)
vote |= voter->vote(voter);

#define dprintk(msg...) \
cpufreq_debug_printk(CPUFREQ_DEBUG_DRIVER, "cpufreq-msm", msg)
return vote;
}

void msm_cpufreq_voter_update(struct msm_cpufreq_voter *v)
{
mutex_lock(&msm_cpufreq_voter_lock);
msm_cpufreq_vote = msm_cpufreq_check_votes();
mutex_unlock(&msm_cpufreq_voter_lock);
}
EXPORT_SYMBOL_GPL(msm_cpufreq_voter_update);

int msm_cpufreq_register_voter(struct msm_cpufreq_voter *v)
{
if (v == NULL || v->vote == NULL)
return -EINVAL;

mutex_lock(&msm_cpufreq_voter_lock);
list_add(&v->item, &msm_cpufreq_voters);
msm_cpufreq_vote = msm_cpufreq_check_votes();
mutex_unlock(&msm_cpufreq_voter_lock);

return 0;
}
EXPORT_SYMBOL_GPL(msm_cpufreq_register_voter);

void msm_cpufreq_unregister_voter(struct msm_cpufreq_voter *v)
{
if (v == NULL)
return;

mutex_lock(&msm_cpufreq_voter_lock);
list_del(&v->item);
msm_cpufreq_vote = msm_cpufreq_check_votes();
mutex_unlock(&msm_cpufreq_voter_lock);
}
EXPORT_SYMBOL_GPL(msm_cpufreq_unregister_voter);

static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq)
{
int ret = 0;
struct cpufreq_freqs freqs;

/* race condition ok */
if (msm_cpufreq_vote == MSM_CPUFREQ_ACTIVE)
new_freq = policy->max;

freqs.old = policy->cur;
if (override_cpu) {
if (policy->cur == policy->max)
return 0;
else
freqs.new = policy->max;
} else
freqs.new = new_freq;
freqs.new = new_freq;
freqs.cpu = policy->cpu;
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
ret = acpuclk_set_rate(policy->cpu, new_freq, SETRATE_CPUFREQ);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
ret = acpuclk_set_rate(policy->cpu, new_freq, SETRATE_CPUFREQ);
if (!ret)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);

return ret;
}

Expand All @@ -94,35 +133,12 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy,
int ret = -EFAULT;
int index;
struct cpufreq_frequency_table *table;
#ifdef CONFIG_SMP
struct cpufreq_work_struct *cpu_work = NULL;
cpumask_var_t mask;

if (!alloc_cpumask_var(&mask, GFP_KERNEL))
return -ENOMEM;

if (!cpu_active(policy->cpu)) {
pr_info("cpufreq: cpu %d is not active.\n", policy->cpu);
return -ENODEV;
}
#endif

mutex_lock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex);

if (per_cpu(cpufreq_suspend, policy->cpu).device_suspended) {
pr_debug("cpufreq: cpu%d scheduling frequency change "
"in suspend.\n", policy->cpu);
ret = -EFAULT;
goto done;
}

table = cpufreq_frequency_get_table(policy->cpu);
if (cpufreq_frequency_table_target(policy, table, target_freq, relation,
&index)) {
if (cpufreq_frequency_table_target(policy, table, target_freq, relation, &index)) {
pr_err("cpufreq: invalid target_freq: %d\n", target_freq);
ret = -EINVAL;
goto done;
}
return -EINVAL;
}

#ifdef CONFIG_CPU_FREQ_DEBUG
dprintk("CPU[%d] target %d relation %d (%d-%d) selected %d\n",
Expand All @@ -131,31 +147,29 @@ static int msm_cpufreq_target(struct cpufreq_policy *policy,
#endif

#ifdef CONFIG_SMP
cpu_work = &per_cpu(cpufreq_work, policy->cpu);
cpu_work->policy = policy;
cpu_work->frequency = table[index].frequency;
cpu_work->status = -ENODEV;

cpumask_clear(mask);
cpumask_set_cpu(policy->cpu, mask);
if (cpumask_equal(mask, &current->cpus_allowed)) {
ret = set_cpu_freq(cpu_work->policy, cpu_work->frequency);
goto done;
} else {
if (get_cpu() == policy->cpu) {
/* Issue a direct call, since we are on the same cpu */
ret = set_cpu_freq(policy, table[index].frequency);
put_cpu();
} else {
struct cpufreq_work_struct *cpu_work = NULL;

put_cpu();
cpu_work = &per_cpu(cpufreq_work, policy->cpu);
cpu_work->policy = policy;
cpu_work->frequency = table[index].frequency;

init_completion(&cpu_work->complete);
cancel_work_sync(&cpu_work->work);
INIT_COMPLETION(cpu_work->complete);
queue_work_on(policy->cpu, msm_cpufreq_wq, &cpu_work->work);
schedule_work_on(policy->cpu, &cpu_work->work);
wait_for_completion(&cpu_work->complete);
}
ret = cpu_work->status;
}

free_cpumask_var(mask);
ret = cpu_work->status;
#else
ret = set_cpu_freq(policy, table[index].frequency);
#endif

done:
mutex_unlock(&per_cpu(cpufreq_suspend, policy->cpu).suspend_mutex);
return ret;
}

Expand All @@ -168,14 +182,13 @@ static int msm_cpufreq_verify(struct cpufreq_policy *policy)

static int __cpuinit msm_cpufreq_init(struct cpufreq_policy *policy)
{
int cur_freq;
int index;
struct cpufreq_frequency_table *table;
#ifdef CONFIG_SMP
struct cpufreq_work_struct *cpu_work = NULL;
#endif

table = cpufreq_frequency_get_table(policy->cpu);
policy->cur = acpuclk_get_rate(policy->cpu);
if (cpufreq_frequency_table_cpuinfo(policy, table)) {
#ifdef CONFIG_MSM_CPU_FREQ_SET_MIN_MAX
policy->cpuinfo.min_freq = CONFIG_MSM_CPU_FREQ_MIN;
Expand All @@ -187,95 +200,21 @@ static int __cpuinit msm_cpufreq_init(struct cpufreq_policy *policy)
policy->max = CONFIG_MSM_CPU_FREQ_MAX;
#endif

cur_freq = acpuclk_get_rate(policy->cpu);
if (cpufreq_frequency_table_target(policy, table, cur_freq,
CPUFREQ_RELATION_H, &index)) {
pr_info("cpufreq: cpu%d at invalid freq: %d\n",
policy->cpu, cur_freq);
return -EINVAL;
}

if (cur_freq != table[index].frequency) {
int ret = 0;
ret = acpuclk_set_rate(policy->cpu, table[index].frequency,
SETRATE_CPUFREQ);
if (ret)
return ret;
pr_info("cpufreq: cpu%d init at %d switching to %d\n",
policy->cpu, cur_freq, table[index].frequency);
cur_freq = table[index].frequency;
}

policy->cur = cur_freq;

policy->cpuinfo.transition_latency =
acpuclk_get_switch_time() * NSEC_PER_USEC;
acpuclk_get_switch_time() * NSEC_PER_USEC;

#ifdef CONFIG_SMP
cpu_work = &per_cpu(cpufreq_work, policy->cpu);
INIT_WORK(&cpu_work->work, set_cpu_work);
init_completion(&cpu_work->complete);
#endif

return 0;
}

static int msm_cpufreq_suspend(void)
{
int cpu;

for_each_possible_cpu(cpu) {
mutex_lock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex);
per_cpu(cpufreq_suspend, cpu).device_suspended = 1;
mutex_unlock(&per_cpu(cpufreq_suspend, cpu).suspend_mutex);
}

return NOTIFY_DONE;
}

static int msm_cpufreq_resume(void)
{
int cpu;

for_each_possible_cpu(cpu) {
per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
}

return NOTIFY_DONE;
}

static int msm_cpufreq_pm_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
switch (event) {
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
return msm_cpufreq_resume();
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
return msm_cpufreq_suspend();
default:
return NOTIFY_DONE;
}
}

static ssize_t store_mfreq(struct sysdev_class *class,
struct sysdev_class_attribute *attr,
const char *buf, size_t count)
{
u64 val;

if (strict_strtoull(buf, 0, &val) < 0) {
pr_err("Invalid parameter to mfreq\n");
return 0;
}
if (val)
override_cpu = 1;
else
override_cpu = 0;
return count;
}

static SYSDEV_CLASS_ATTR(mfreq, 0200, NULL, store_mfreq);
static struct freq_attr *msm_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL,
};

static struct cpufreq_driver msm_cpufreq_driver = {
/* lps calculations are handled here. */
Expand All @@ -284,33 +223,12 @@ static struct cpufreq_driver msm_cpufreq_driver = {
.verify = msm_cpufreq_verify,
.target = msm_cpufreq_target,
.name = "msm",
};

static struct notifier_block msm_cpufreq_pm_notifier = {
.notifier_call = msm_cpufreq_pm_event,
.attr = msm_cpufreq_attr,
};

static int __init msm_cpufreq_register(void)
{
int cpu;

int err = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
&attr_mfreq.attr);
if (err)
pr_err("Failed to create sysfs mfreq\n");

for_each_possible_cpu(cpu) {
mutex_init(&(per_cpu(cpufreq_suspend, cpu).suspend_mutex));
per_cpu(cpufreq_suspend, cpu).device_suspended = 0;
}

#ifdef CONFIG_SMP
msm_cpufreq_wq = create_workqueue("msm-cpufreq");
#endif

register_pm_notifier(&msm_cpufreq_pm_notifier);
return cpufreq_register_driver(&msm_cpufreq_driver);
}

late_initcall(msm_cpufreq_register);

late_initcall(msm_cpufreq_register);