Merge branch 'next' of git://github.com/kernelslacker/cpufreq
* 'next' of git://github.com/kernelslacker/cpufreq: [CPUFREQ] db8500: support all frequencies [CPUFREQ] db8500: remove unneeded for loop iteration over freq_table [CPUFREQ] ARM Exynos4210 PM/Suspend compatibility with different bootloaders [CPUFREQ] ARM: ux500: send cpufreq notification for all cpus [CPUFREQ] e_powersaver: Allow user to lower maximum voltage [CPUFREQ] e_powersaver: Check BIOS limit for CPU frequency [CPUFREQ] e_powersaver: Additional checks [CPUFREQ] exynos4210: Show list of available frequencies
This commit is contained in:
commit
70c9f18ca8
3 changed files with 268 additions and 32 deletions
drivers/cpufreq
|
@ -18,24 +18,29 @@
|
|||
static struct cpufreq_frequency_table freq_table[] = {
|
||||
[0] = {
|
||||
.index = 0,
|
||||
.frequency = 300000,
|
||||
.frequency = 200000,
|
||||
},
|
||||
[1] = {
|
||||
.index = 1,
|
||||
.frequency = 600000,
|
||||
.frequency = 300000,
|
||||
},
|
||||
[2] = {
|
||||
/* Used for MAX_OPP, if available */
|
||||
.index = 2,
|
||||
.frequency = CPUFREQ_TABLE_END,
|
||||
.frequency = 600000,
|
||||
},
|
||||
[3] = {
|
||||
/* Used for MAX_OPP, if available */
|
||||
.index = 3,
|
||||
.frequency = CPUFREQ_TABLE_END,
|
||||
},
|
||||
[4] = {
|
||||
.index = 4,
|
||||
.frequency = CPUFREQ_TABLE_END,
|
||||
},
|
||||
};
|
||||
|
||||
static enum arm_opp idx2opp[] = {
|
||||
ARM_EXTCLK,
|
||||
ARM_50_OPP,
|
||||
ARM_100_OPP,
|
||||
ARM_MAX_OPP
|
||||
|
@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
|
|||
|
||||
freqs.old = policy->cur;
|
||||
freqs.new = freq_table[idx].frequency;
|
||||
freqs.cpu = policy->cpu;
|
||||
|
||||
if (freqs.old == freqs.new)
|
||||
return 0;
|
||||
|
||||
/* pre-change notification */
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
for_each_cpu(freqs.cpu, policy->cpus)
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||
|
||||
/* request the PRCM unit for opp change */
|
||||
if (prcmu_set_arm_opp(idx2opp[idx])) {
|
||||
|
@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
|
|||
}
|
||||
|
||||
/* post change notification */
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
for_each_cpu(freqs.cpu, policy->cpus)
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -104,16 +110,18 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
|
|||
static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int res;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
|
||||
|
||||
if (cpu_is_u8500v2() && !prcmu_is_u8400()) {
|
||||
freq_table[0].frequency = 400000;
|
||||
freq_table[1].frequency = 800000;
|
||||
if (!prcmu_is_u8400()) {
|
||||
freq_table[1].frequency = 400000;
|
||||
freq_table[2].frequency = 800000;
|
||||
if (prcmu_has_arm_maxopp())
|
||||
freq_table[2].frequency = 1000000;
|
||||
freq_table[3].frequency = 1000000;
|
||||
}
|
||||
pr_info("db8500-cpufreq : Available frequencies:\n");
|
||||
while (freq_table[i].frequency != CPUFREQ_TABLE_END)
|
||||
pr_info(" %d Mhz\n", freq_table[i++].frequency/1000);
|
||||
|
||||
/* get policy fields based on the table */
|
||||
res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
|
||||
|
@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
|
|||
policy->min = policy->cpuinfo.min_freq;
|
||||
policy->max = policy->cpuinfo.max_freq;
|
||||
policy->cur = db8500_cpufreq_getspeed(policy->cpu);
|
||||
|
||||
for (i = 0; freq_table[i].frequency != policy->cur; i++)
|
||||
;
|
||||
|
||||
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
|
||||
|
||||
/*
|
||||
|
|
|
@ -19,6 +19,11 @@
|
|||
#include <asm/msr.h>
|
||||
#include <asm/tsc.h>
|
||||
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
#include <linux/acpi.h>
|
||||
#include <acpi/processor.h>
|
||||
#endif
|
||||
|
||||
#define EPS_BRAND_C7M 0
|
||||
#define EPS_BRAND_C7 1
|
||||
#define EPS_BRAND_EDEN 2
|
||||
|
@ -27,11 +32,59 @@
|
|||
|
||||
struct eps_cpu_data {
|
||||
u32 fsb;
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
u32 bios_limit;
|
||||
#endif
|
||||
struct cpufreq_frequency_table freq_table[];
|
||||
};
|
||||
|
||||
static struct eps_cpu_data *eps_cpu[NR_CPUS];
|
||||
|
||||
/* Module parameters */
|
||||
static int freq_failsafe_off;
|
||||
static int voltage_failsafe_off;
|
||||
static int set_max_voltage;
|
||||
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
static int ignore_acpi_limit;
|
||||
|
||||
static struct acpi_processor_performance *eps_acpi_cpu_perf;
|
||||
|
||||
/* Minimum necessary to get acpi_processor_get_bios_limit() working */
|
||||
static int eps_acpi_init(void)
|
||||
{
|
||||
eps_acpi_cpu_perf = kzalloc(sizeof(struct acpi_processor_performance),
|
||||
GFP_KERNEL);
|
||||
if (!eps_acpi_cpu_perf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!zalloc_cpumask_var(&eps_acpi_cpu_perf->shared_cpu_map,
|
||||
GFP_KERNEL)) {
|
||||
kfree(eps_acpi_cpu_perf);
|
||||
eps_acpi_cpu_perf = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (acpi_processor_register_performance(eps_acpi_cpu_perf, 0)) {
|
||||
free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
|
||||
kfree(eps_acpi_cpu_perf);
|
||||
eps_acpi_cpu_perf = NULL;
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eps_acpi_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
if (eps_acpi_cpu_perf) {
|
||||
acpi_processor_unregister_performance(eps_acpi_cpu_perf, 0);
|
||||
free_cpumask_var(eps_acpi_cpu_perf->shared_cpu_map);
|
||||
kfree(eps_acpi_cpu_perf);
|
||||
eps_acpi_cpu_perf = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static unsigned int eps_get(unsigned int cpu)
|
||||
{
|
||||
|
@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
|
|||
int k, step, voltage;
|
||||
int ret;
|
||||
int states;
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
unsigned int limit;
|
||||
#endif
|
||||
|
||||
if (policy->cpu != 0)
|
||||
return -ENODEV;
|
||||
|
@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
|
|||
return -EINVAL;
|
||||
if (current_voltage > 0x1f || max_voltage > 0x1f)
|
||||
return -EINVAL;
|
||||
if (max_voltage < min_voltage)
|
||||
if (max_voltage < min_voltage
|
||||
|| current_voltage < min_voltage
|
||||
|| current_voltage > max_voltage)
|
||||
return -EINVAL;
|
||||
|
||||
/* Check for systems using underclocked CPU */
|
||||
if (!freq_failsafe_off && max_multiplier != current_multiplier) {
|
||||
printk(KERN_INFO "eps: Your processor is running at different "
|
||||
"frequency then its maximum. Aborting.\n");
|
||||
printk(KERN_INFO "eps: You can use freq_failsafe_off option "
|
||||
"to disable this check.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!voltage_failsafe_off && max_voltage != current_voltage) {
|
||||
printk(KERN_INFO "eps: Your processor is running at different "
|
||||
"voltage then its maximum. Aborting.\n");
|
||||
printk(KERN_INFO "eps: You can use voltage_failsafe_off "
|
||||
"option to disable this check.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Calc FSB speed */
|
||||
fsb = cpu_khz / current_multiplier;
|
||||
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
/* Check for ACPI processor speed limit */
|
||||
if (!ignore_acpi_limit && !eps_acpi_init()) {
|
||||
if (!acpi_processor_get_bios_limit(policy->cpu, &limit)) {
|
||||
printk(KERN_INFO "eps: ACPI limit %u.%uGHz\n",
|
||||
limit/1000000,
|
||||
(limit%1000000)/10000);
|
||||
eps_acpi_exit(policy);
|
||||
/* Check if max_multiplier is in BIOS limits */
|
||||
if (limit && max_multiplier * fsb > limit) {
|
||||
printk(KERN_INFO "eps: Aborting.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Allow user to set lower maximum voltage then that reported
|
||||
* by processor */
|
||||
if (brand == EPS_BRAND_C7M && set_max_voltage) {
|
||||
u32 v;
|
||||
|
||||
/* Change mV to something hardware can use */
|
||||
v = (set_max_voltage - 700) / 16;
|
||||
/* Check if voltage is within limits */
|
||||
if (v >= min_voltage && v <= max_voltage) {
|
||||
printk(KERN_INFO "eps: Setting %dmV as maximum.\n",
|
||||
v * 16 + 700);
|
||||
max_voltage = v;
|
||||
}
|
||||
}
|
||||
|
||||
/* Calc number of p-states supported */
|
||||
if (brand == EPS_BRAND_C7M)
|
||||
states = max_multiplier - min_multiplier + 1;
|
||||
|
@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
|
|||
|
||||
/* Copy basic values */
|
||||
centaur->fsb = fsb;
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
centaur->bios_limit = limit;
|
||||
#endif
|
||||
|
||||
/* Fill frequency and MSR value table */
|
||||
f_table = ¢aur->freq_table[0];
|
||||
|
@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
|
|||
static int eps_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
unsigned int cpu = policy->cpu;
|
||||
struct eps_cpu_data *centaur;
|
||||
u32 lo, hi;
|
||||
|
||||
if (eps_cpu[cpu] == NULL)
|
||||
return -ENODEV;
|
||||
centaur = eps_cpu[cpu];
|
||||
|
||||
/* Get max frequency */
|
||||
rdmsr(MSR_IA32_PERF_STATUS, lo, hi);
|
||||
/* Set max frequency */
|
||||
eps_set_state(centaur, cpu, hi & 0xffff);
|
||||
/* Bye */
|
||||
cpufreq_frequency_table_put_attr(policy->cpu);
|
||||
kfree(eps_cpu[cpu]);
|
||||
|
@ -359,6 +459,19 @@ static void __exit eps_exit(void)
|
|||
cpufreq_unregister_driver(&eps_driver);
|
||||
}
|
||||
|
||||
/* Allow user to overclock his machine or to change frequency to higher after
|
||||
* unloading module */
|
||||
module_param(freq_failsafe_off, int, 0644);
|
||||
MODULE_PARM_DESC(freq_failsafe_off, "Disable current vs max frequency check");
|
||||
module_param(voltage_failsafe_off, int, 0644);
|
||||
MODULE_PARM_DESC(voltage_failsafe_off, "Disable current vs max voltage check");
|
||||
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
|
||||
module_param(ignore_acpi_limit, int, 0644);
|
||||
MODULE_PARM_DESC(ignore_acpi_limit, "Don't check ACPI's processor speed limit");
|
||||
#endif
|
||||
module_param(set_max_voltage, int, 0644);
|
||||
MODULE_PARM_DESC(set_max_voltage, "Set maximum CPU voltage (mV) C7-M only");
|
||||
|
||||
MODULE_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
|
||||
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <mach/map.h>
|
||||
#include <mach/regs-clock.h>
|
||||
|
@ -36,6 +38,10 @@ static struct regulator *int_regulator;
|
|||
static struct cpufreq_freqs freqs;
|
||||
static unsigned int memtype;
|
||||
|
||||
static unsigned int locking_frequency;
|
||||
static bool frequency_locked;
|
||||
static DEFINE_MUTEX(cpufreq_lock);
|
||||
|
||||
enum exynos4_memory_type {
|
||||
DDR2 = 4,
|
||||
LPDDR2,
|
||||
|
@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy,
|
|||
{
|
||||
unsigned int index, old_index;
|
||||
unsigned int arm_volt, int_volt;
|
||||
int err = -EINVAL;
|
||||
|
||||
freqs.old = exynos4_getspeed(policy->cpu);
|
||||
|
||||
mutex_lock(&cpufreq_lock);
|
||||
|
||||
if (frequency_locked && target_freq != locking_frequency) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
|
||||
freqs.old, relation, &old_index))
|
||||
return -EINVAL;
|
||||
goto out;
|
||||
|
||||
if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
|
||||
target_freq, relation, &index))
|
||||
return -EINVAL;
|
||||
goto out;
|
||||
|
||||
err = 0;
|
||||
|
||||
freqs.new = exynos4_freq_table[index].frequency;
|
||||
freqs.cpu = policy->cpu;
|
||||
|
||||
if (freqs.new == freqs.old)
|
||||
return 0;
|
||||
goto out;
|
||||
|
||||
/* get the voltage value */
|
||||
arm_volt = exynos4_volt_table[index].arm_volt;
|
||||
|
@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy,
|
|||
|
||||
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&cpufreq_lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
/*
|
||||
* These suspend/resume are used as syscore_ops, it is already too
|
||||
* late to set regulator voltages at this stage.
|
||||
*/
|
||||
static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
|
||||
{
|
||||
return 0;
|
||||
|
@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
|
|||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume
|
||||
* context
|
||||
* @notifier
|
||||
* @pm_event
|
||||
* @v
|
||||
*
|
||||
* While frequency_locked == true, target() ignores every frequency but
|
||||
* locking_frequency. The locking_frequency value is the initial frequency,
|
||||
* which is set by the bootloader. In order to eliminate possible
|
||||
* inconsistency in clock values, we save and restore frequencies during
|
||||
* suspend and resume and block CPUFREQ activities. Note that the standard
|
||||
* suspend/resume cannot be used as they are too deep (syscore_ops) for
|
||||
* regulator actions.
|
||||
*/
|
||||
static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier,
|
||||
unsigned long pm_event, void *v)
|
||||
{
|
||||
struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */
|
||||
static unsigned int saved_frequency;
|
||||
unsigned int temp;
|
||||
|
||||
mutex_lock(&cpufreq_lock);
|
||||
switch (pm_event) {
|
||||
case PM_SUSPEND_PREPARE:
|
||||
if (frequency_locked)
|
||||
goto out;
|
||||
frequency_locked = true;
|
||||
|
||||
if (locking_frequency) {
|
||||
saved_frequency = exynos4_getspeed(0);
|
||||
|
||||
mutex_unlock(&cpufreq_lock);
|
||||
exynos4_target(policy, locking_frequency,
|
||||
CPUFREQ_RELATION_H);
|
||||
mutex_lock(&cpufreq_lock);
|
||||
}
|
||||
|
||||
break;
|
||||
case PM_POST_SUSPEND:
|
||||
|
||||
if (saved_frequency) {
|
||||
/*
|
||||
* While frequency_locked, only locking_frequency
|
||||
* is valid for target(). In order to use
|
||||
* saved_frequency while keeping frequency_locked,
|
||||
* we temporarly overwrite locking_frequency.
|
||||
*/
|
||||
temp = locking_frequency;
|
||||
locking_frequency = saved_frequency;
|
||||
|
||||
mutex_unlock(&cpufreq_lock);
|
||||
exynos4_target(policy, locking_frequency,
|
||||
CPUFREQ_RELATION_H);
|
||||
mutex_lock(&cpufreq_lock);
|
||||
|
||||
locking_frequency = temp;
|
||||
}
|
||||
|
||||
frequency_locked = false;
|
||||
break;
|
||||
}
|
||||
out:
|
||||
mutex_unlock(&cpufreq_lock);
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block exynos4_cpufreq_nb = {
|
||||
.notifier_call = exynos4_cpufreq_pm_notifier,
|
||||
};
|
||||
|
||||
static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
int ret;
|
||||
|
||||
policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
|
||||
|
||||
cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
|
||||
|
@ -479,16 +575,35 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
|||
*/
|
||||
cpumask_setall(policy->cpus);
|
||||
|
||||
return cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
|
||||
ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
cpufreq_frequency_table_put_attr(policy->cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct freq_attr *exynos4_cpufreq_attr[] = {
|
||||
&cpufreq_freq_attr_scaling_available_freqs,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct cpufreq_driver exynos4_driver = {
|
||||
.flags = CPUFREQ_STICKY,
|
||||
.verify = exynos4_verify_speed,
|
||||
.target = exynos4_target,
|
||||
.get = exynos4_getspeed,
|
||||
.init = exynos4_cpufreq_cpu_init,
|
||||
.exit = exynos4_cpufreq_cpu_exit,
|
||||
.name = "exynos4_cpufreq",
|
||||
.attr = exynos4_cpufreq_attr,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = exynos4_cpufreq_suspend,
|
||||
.resume = exynos4_cpufreq_resume,
|
||||
|
@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void)
|
|||
if (IS_ERR(cpu_clk))
|
||||
return PTR_ERR(cpu_clk);
|
||||
|
||||
locking_frequency = exynos4_getspeed(0);
|
||||
|
||||
moutcore = clk_get(NULL, "moutcore");
|
||||
if (IS_ERR(moutcore))
|
||||
goto out;
|
||||
|
@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void)
|
|||
printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
|
||||
}
|
||||
|
||||
register_pm_notifier(&exynos4_cpufreq_nb);
|
||||
|
||||
return cpufreq_register_driver(&exynos4_driver);
|
||||
|
||||
out:
|
||||
|
|
Loading…
Add table
Reference in a new issue