Commit 70c9f18c authored by Linus Torvalds's avatar Linus Torvalds

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
parents a0a4194c 6283e328
...@@ -18,24 +18,29 @@ ...@@ -18,24 +18,29 @@
static struct cpufreq_frequency_table freq_table[] = { static struct cpufreq_frequency_table freq_table[] = {
[0] = { [0] = {
.index = 0, .index = 0,
.frequency = 300000, .frequency = 200000,
}, },
[1] = { [1] = {
.index = 1, .index = 1,
.frequency = 600000, .frequency = 300000,
}, },
[2] = { [2] = {
/* Used for MAX_OPP, if available */
.index = 2, .index = 2,
.frequency = CPUFREQ_TABLE_END, .frequency = 600000,
}, },
[3] = { [3] = {
/* Used for MAX_OPP, if available */
.index = 3, .index = 3,
.frequency = CPUFREQ_TABLE_END, .frequency = CPUFREQ_TABLE_END,
}, },
[4] = {
.index = 4,
.frequency = CPUFREQ_TABLE_END,
},
}; };
static enum arm_opp idx2opp[] = { static enum arm_opp idx2opp[] = {
ARM_EXTCLK,
ARM_50_OPP, ARM_50_OPP,
ARM_100_OPP, ARM_100_OPP,
ARM_MAX_OPP ARM_MAX_OPP
...@@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy, ...@@ -72,13 +77,13 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
freqs.old = policy->cur; freqs.old = policy->cur;
freqs.new = freq_table[idx].frequency; freqs.new = freq_table[idx].frequency;
freqs.cpu = policy->cpu;
if (freqs.old == freqs.new) if (freqs.old == freqs.new)
return 0; return 0;
/* pre-change notification */ /* 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 */ /* request the PRCM unit for opp change */
if (prcmu_set_arm_opp(idx2opp[idx])) { if (prcmu_set_arm_opp(idx2opp[idx])) {
...@@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy, ...@@ -87,7 +92,8 @@ static int db8500_cpufreq_target(struct cpufreq_policy *policy,
} }
/* post change notification */ /* post change notification */
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); for_each_cpu(freqs.cpu, policy->cpus)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0; return 0;
} }
...@@ -104,16 +110,18 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu) ...@@ -104,16 +110,18 @@ static unsigned int db8500_cpufreq_getspeed(unsigned int cpu)
static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
{ {
int res; int res;
int i;
BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table));
if (cpu_is_u8500v2() && !prcmu_is_u8400()) { if (!prcmu_is_u8400()) {
freq_table[0].frequency = 400000; freq_table[1].frequency = 400000;
freq_table[1].frequency = 800000; freq_table[2].frequency = 800000;
if (prcmu_has_arm_maxopp()) 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 */ /* get policy fields based on the table */
res = cpufreq_frequency_table_cpuinfo(policy, freq_table); res = cpufreq_frequency_table_cpuinfo(policy, freq_table);
...@@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy) ...@@ -127,10 +135,6 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)
policy->min = policy->cpuinfo.min_freq; policy->min = policy->cpuinfo.min_freq;
policy->max = policy->cpuinfo.max_freq; policy->max = policy->cpuinfo.max_freq;
policy->cur = db8500_cpufreq_getspeed(policy->cpu); policy->cur = db8500_cpufreq_getspeed(policy->cpu);
for (i = 0; freq_table[i].frequency != policy->cur; i++)
;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR; policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* /*
......
...@@ -19,6 +19,11 @@ ...@@ -19,6 +19,11 @@
#include <asm/msr.h> #include <asm/msr.h>
#include <asm/tsc.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_C7M 0
#define EPS_BRAND_C7 1 #define EPS_BRAND_C7 1
#define EPS_BRAND_EDEN 2 #define EPS_BRAND_EDEN 2
...@@ -27,11 +32,59 @@ ...@@ -27,11 +32,59 @@
struct eps_cpu_data { struct eps_cpu_data {
u32 fsb; u32 fsb;
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
u32 bios_limit;
#endif
struct cpufreq_frequency_table freq_table[]; struct cpufreq_frequency_table freq_table[];
}; };
static struct eps_cpu_data *eps_cpu[NR_CPUS]; 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) static unsigned int eps_get(unsigned int cpu)
{ {
...@@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) ...@@ -164,6 +217,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
int k, step, voltage; int k, step, voltage;
int ret; int ret;
int states; int states;
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
unsigned int limit;
#endif
if (policy->cpu != 0) if (policy->cpu != 0)
return -ENODEV; return -ENODEV;
...@@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy) ...@@ -244,11 +300,62 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
return -EINVAL; return -EINVAL;
if (current_voltage > 0x1f || max_voltage > 0x1f) if (current_voltage > 0x1f || max_voltage > 0x1f)
return -EINVAL; return -EINVAL;
if (max_voltage < min_voltage) if (max_voltage < min_voltage
|| current_voltage < min_voltage
|| current_voltage > max_voltage)
return -EINVAL; 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 */ /* Calc FSB speed */
fsb = cpu_khz / current_multiplier; 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 */ /* Calc number of p-states supported */
if (brand == EPS_BRAND_C7M) if (brand == EPS_BRAND_C7M)
states = max_multiplier - min_multiplier + 1; states = max_multiplier - min_multiplier + 1;
...@@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy) ...@@ -265,6 +372,9 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
/* Copy basic values */ /* Copy basic values */
centaur->fsb = fsb; centaur->fsb = fsb;
#if defined CONFIG_ACPI_PROCESSOR || defined CONFIG_ACPI_PROCESSOR_MODULE
centaur->bios_limit = limit;
#endif
/* Fill frequency and MSR value table */ /* Fill frequency and MSR value table */
f_table = &centaur->freq_table[0]; f_table = &centaur->freq_table[0];
...@@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy) ...@@ -303,17 +413,7 @@ static int eps_cpu_init(struct cpufreq_policy *policy)
static int eps_cpu_exit(struct cpufreq_policy *policy) static int eps_cpu_exit(struct cpufreq_policy *policy)
{ {
unsigned int cpu = policy->cpu; 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 */ /* Bye */
cpufreq_frequency_table_put_attr(policy->cpu); cpufreq_frequency_table_put_attr(policy->cpu);
kfree(eps_cpu[cpu]); kfree(eps_cpu[cpu]);
...@@ -359,6 +459,19 @@ static void __exit eps_exit(void) ...@@ -359,6 +459,19 @@ static void __exit eps_exit(void)
cpufreq_unregister_driver(&eps_driver); 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_AUTHOR("Rafal Bilski <rafalbilski@interia.pl>");
MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's."); MODULE_DESCRIPTION("Enhanced PowerSaver driver for VIA C7 CPU's.");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/notifier.h>
#include <linux/suspend.h>
#include <mach/map.h> #include <mach/map.h>
#include <mach/regs-clock.h> #include <mach/regs-clock.h>
...@@ -36,6 +38,10 @@ static struct regulator *int_regulator; ...@@ -36,6 +38,10 @@ static struct regulator *int_regulator;
static struct cpufreq_freqs freqs; static struct cpufreq_freqs freqs;
static unsigned int memtype; static unsigned int memtype;
static unsigned int locking_frequency;
static bool frequency_locked;
static DEFINE_MUTEX(cpufreq_lock);
enum exynos4_memory_type { enum exynos4_memory_type {
DDR2 = 4, DDR2 = 4,
LPDDR2, LPDDR2,
...@@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy, ...@@ -405,22 +411,32 @@ static int exynos4_target(struct cpufreq_policy *policy,
{ {
unsigned int index, old_index; unsigned int index, old_index;
unsigned int arm_volt, int_volt; unsigned int arm_volt, int_volt;
int err = -EINVAL;
freqs.old = exynos4_getspeed(policy->cpu); 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, if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
freqs.old, relation, &old_index)) freqs.old, relation, &old_index))
return -EINVAL; goto out;
if (cpufreq_frequency_table_target(policy, exynos4_freq_table, if (cpufreq_frequency_table_target(policy, exynos4_freq_table,
target_freq, relation, &index)) target_freq, relation, &index))
return -EINVAL; goto out;
err = 0;
freqs.new = exynos4_freq_table[index].frequency; freqs.new = exynos4_freq_table[index].frequency;
freqs.cpu = policy->cpu; freqs.cpu = policy->cpu;
if (freqs.new == freqs.old) if (freqs.new == freqs.old)
return 0; goto out;
/* get the voltage value */ /* get the voltage value */
arm_volt = exynos4_volt_table[index].arm_volt; arm_volt = exynos4_volt_table[index].arm_volt;
...@@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy, ...@@ -447,10 +463,16 @@ static int exynos4_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
return 0; out:
mutex_unlock(&cpufreq_lock);
return err;
} }
#ifdef CONFIG_PM #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) static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy)
{ {
return 0; return 0;
...@@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy) ...@@ -462,8 +484,82 @@ static int exynos4_cpufreq_resume(struct cpufreq_policy *policy)
} }
#endif #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) static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
{ {
int ret;
policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu); policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu);
cpufreq_frequency_table_get_attr(exynos4_freq_table, 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) ...@@ -479,16 +575,35 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy)
*/ */
cpumask_setall(policy->cpus); 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 = { static struct cpufreq_driver exynos4_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY,
.verify = exynos4_verify_speed, .verify = exynos4_verify_speed,
.target = exynos4_target, .target = exynos4_target,
.get = exynos4_getspeed, .get = exynos4_getspeed,
.init = exynos4_cpufreq_cpu_init, .init = exynos4_cpufreq_cpu_init,
.exit = exynos4_cpufreq_cpu_exit,
.name = "exynos4_cpufreq", .name = "exynos4_cpufreq",
.attr = exynos4_cpufreq_attr,
#ifdef CONFIG_PM #ifdef CONFIG_PM
.suspend = exynos4_cpufreq_suspend, .suspend = exynos4_cpufreq_suspend,
.resume = exynos4_cpufreq_resume, .resume = exynos4_cpufreq_resume,
...@@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void) ...@@ -501,6 +616,8 @@ static int __init exynos4_cpufreq_init(void)
if (IS_ERR(cpu_clk)) if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk); return PTR_ERR(cpu_clk);
locking_frequency = exynos4_getspeed(0);
moutcore = clk_get(NULL, "moutcore"); moutcore = clk_get(NULL, "moutcore");
if (IS_ERR(moutcore)) if (IS_ERR(moutcore))
goto out; goto out;
...@@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void) ...@@ -540,6 +657,8 @@ static int __init exynos4_cpufreq_init(void)
printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype);
} }
register_pm_notifier(&exynos4_cpufreq_nb);
return cpufreq_register_driver(&exynos4_driver); return cpufreq_register_driver(&exynos4_driver);
out: out:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment