2012-08-24 01:58:44 +00:00
|
|
|
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
|
2012-02-24 01:44:37 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
|
|
* only version 2 as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/mutex.h>
|
|
|
|
#include <linux/kobject.h>
|
|
|
|
#include <linux/cpufreq.h>
|
|
|
|
#include <linux/platform_device.h>
|
|
|
|
#include <mach/msm_dcvs.h>
|
|
|
|
|
|
|
|
struct msm_gov {
|
|
|
|
int cpu;
|
|
|
|
unsigned int cur_freq;
|
|
|
|
unsigned int min_freq;
|
|
|
|
unsigned int max_freq;
|
|
|
|
struct msm_dcvs_freq gov_notifier;
|
|
|
|
struct cpufreq_policy *policy;
|
|
|
|
};
|
|
|
|
|
|
|
|
static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex);
|
|
|
|
static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info);
|
|
|
|
static char core_name[NR_CPUS][10];
|
|
|
|
|
|
|
|
static void msm_gov_check_limits(struct cpufreq_policy *policy)
|
|
|
|
{
|
|
|
|
struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
|
|
|
|
|
|
|
|
if (policy->max < gov->cur_freq)
|
|
|
|
__cpufreq_driver_target(policy, policy->max,
|
|
|
|
CPUFREQ_RELATION_H);
|
|
|
|
else if (policy->min > gov->min_freq)
|
|
|
|
__cpufreq_driver_target(policy, policy->min,
|
|
|
|
CPUFREQ_RELATION_L);
|
|
|
|
else
|
|
|
|
__cpufreq_driver_target(policy, gov->cur_freq,
|
|
|
|
CPUFREQ_RELATION_L);
|
|
|
|
|
|
|
|
gov->cur_freq = policy->cur;
|
|
|
|
gov->min_freq = policy->min;
|
|
|
|
gov->max_freq = policy->max;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int msm_dcvs_freq_set(struct msm_dcvs_freq *self,
|
|
|
|
unsigned int freq)
|
|
|
|
{
|
|
|
|
int ret = -EINVAL;
|
|
|
|
struct msm_gov *gov =
|
|
|
|
container_of(self, struct msm_gov, gov_notifier);
|
|
|
|
|
|
|
|
mutex_lock(&per_cpu(gov_mutex, gov->cpu));
|
|
|
|
|
|
|
|
if (freq < gov->min_freq)
|
|
|
|
freq = gov->min_freq;
|
|
|
|
if (freq > gov->max_freq)
|
|
|
|
freq = gov->max_freq;
|
|
|
|
|
|
|
|
ret = __cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L);
|
|
|
|
gov->cur_freq = gov->policy->cur;
|
|
|
|
|
|
|
|
mutex_unlock(&per_cpu(gov_mutex, gov->cpu));
|
|
|
|
|
|
|
|
if (!ret)
|
|
|
|
return gov->cur_freq;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self)
|
|
|
|
{
|
|
|
|
struct msm_gov *gov =
|
|
|
|
container_of(self, struct msm_gov, gov_notifier);
|
|
|
|
|
|
|
|
return gov->cur_freq;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpufreq_governor_msm(struct cpufreq_policy *policy,
|
|
|
|
unsigned int event)
|
|
|
|
{
|
|
|
|
unsigned int cpu = policy->cpu;
|
|
|
|
int ret = 0;
|
|
|
|
int handle = 0;
|
|
|
|
struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu);
|
|
|
|
struct msm_dcvs_freq *dcvs_notifier =
|
|
|
|
&(per_cpu(msm_gov_info, cpu).gov_notifier);
|
|
|
|
|
|
|
|
switch (event) {
|
|
|
|
case CPUFREQ_GOV_START:
|
|
|
|
if (!cpu_online(cpu))
|
|
|
|
return -EINVAL;
|
|
|
|
BUG_ON(!policy->cur);
|
|
|
|
mutex_lock(&per_cpu(gov_mutex, cpu));
|
|
|
|
per_cpu(msm_gov_info, cpu).cpu = cpu;
|
|
|
|
gov->policy = policy;
|
|
|
|
dcvs_notifier->core_name = core_name[cpu];
|
|
|
|
dcvs_notifier->set_frequency = msm_dcvs_freq_set;
|
|
|
|
dcvs_notifier->get_frequency = msm_dcvs_freq_get;
|
|
|
|
handle = msm_dcvs_freq_sink_register(dcvs_notifier);
|
|
|
|
BUG_ON(handle < 0);
|
|
|
|
msm_gov_check_limits(policy);
|
|
|
|
mutex_unlock(&per_cpu(gov_mutex, cpu));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CPUFREQ_GOV_STOP:
|
|
|
|
msm_dcvs_freq_sink_unregister(dcvs_notifier);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CPUFREQ_GOV_LIMITS:
|
|
|
|
mutex_lock(&per_cpu(gov_mutex, cpu));
|
|
|
|
msm_gov_check_limits(policy);
|
|
|
|
mutex_unlock(&per_cpu(gov_mutex, cpu));
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct cpufreq_governor cpufreq_gov_msm = {
|
|
|
|
.name = "msm-dcvs",
|
|
|
|
.governor = cpufreq_governor_msm,
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __devinit msm_gov_probe(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int cpu;
|
|
|
|
struct msm_dcvs_core_info *core = NULL;
|
2012-08-27 01:27:53 +00:00
|
|
|
int sensor = 0;
|
2012-02-24 01:44:37 +00:00
|
|
|
|
|
|
|
core = pdev->dev.platform_data;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
|
|
|
mutex_init(&per_cpu(gov_mutex, cpu));
|
|
|
|
snprintf(core_name[cpu], 10, "cpu%d", cpu);
|
2012-08-27 01:27:53 +00:00
|
|
|
if (cpu < core->num_cores)
|
|
|
|
sensor = core->sensors[cpu];
|
|
|
|
ret = msm_dcvs_register_core(core_name[cpu], core, sensor);
|
2012-02-24 01:44:37 +00:00
|
|
|
if (ret)
|
|
|
|
pr_err("Unable to register core for %d\n", cpu);
|
|
|
|
}
|
|
|
|
|
|
|
|
return cpufreq_register_governor(&cpufreq_gov_msm);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __devexit msm_gov_remove(struct platform_device *pdev)
|
|
|
|
{
|
|
|
|
platform_set_drvdata(pdev, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct platform_driver msm_gov_driver = {
|
|
|
|
.probe = msm_gov_probe,
|
|
|
|
.remove = __devexit_p(msm_gov_remove),
|
|
|
|
.driver = {
|
|
|
|
.name = "msm_dcvs_gov",
|
|
|
|
.owner = THIS_MODULE,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init cpufreq_gov_msm_init(void)
|
|
|
|
{
|
|
|
|
return platform_driver_register(&msm_gov_driver);
|
|
|
|
}
|
|
|
|
late_initcall(cpufreq_gov_msm_init);
|