Commit 51c4c4ce authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

Merge back earlier 'pm-cpufreq' material.

parents 6cbd7ee1 22c73795
...@@ -200,3 +200,27 @@ Description: address and size of the percpu note. ...@@ -200,3 +200,27 @@ Description: address and size of the percpu note.
note of cpu#. note of cpu#.
crash_notes_size: size of the note of cpu#. crash_notes_size: size of the note of cpu#.
What: /sys/devices/system/cpu/intel_pstate/max_perf_pct
/sys/devices/system/cpu/intel_pstate/min_perf_pct
/sys/devices/system/cpu/intel_pstate/no_turbo
Date: February 2013
Contact: linux-pm@vger.kernel.org
Description: Parameters for the Intel P-state driver
Logic for selecting the current P-state in Intel
Sandybridge+ processors. The three knobs control
limits for the P-state that will be requested by the
driver.
max_perf_pct: limits the maximum P state that will be requested by
the driver stated as a percentage of the available performance.
min_perf_pct: limits the minimum P state that will be requested by
the driver stated as a percentage of the available performance.
no_turbo: limits the driver to selecting P states below the turbo
frequency range.
More details can be found in Documentation/cpu-freq/intel-pstate.txt
Intel P-state driver
--------------------
This driver implements a scaling driver with an internal governor for
Intel Core processors. The driver follows the same model as the
Transmeta scaling driver (longrun.c) and implements the setpolicy()
instead of target(). Scaling drivers that implement setpolicy() are
assumed to implement internal governors by the cpufreq core. All the
logic for selecting the current P state is contained within the
driver; no external governor is used by the cpufreq core.
Intel SandyBridge+ processors are supported.
New sysfs files for controlling P state selection have been added to
/sys/devices/system/cpu/intel_pstate/
max_perf_pct: limits the maximum P state that will be requested by
the driver stated as a percentage of the available performance.
min_perf_pct: limits the minimum P state that will be requested by
the driver stated as a percentage of the available performance.
no_turbo: limits the driver to selecting P states below the turbo
frequency range.
For contemporary Intel processors, the frequency is controlled by the
processor itself and the P-states exposed to software are related to
performance levels. The idea that frequency can be set to a single
frequency is fiction for Intel Core processors. Even if the scaling
driver selects a single P state the actual frequency the processor
will run at is selected by the processor itself.
New debugfs files have also been added to /sys/kernel/debug/pstate_snb/
deadband
d_gain_pct
i_gain_pct
p_gain_pct
sample_rate_ms
setpoint
...@@ -303,6 +303,11 @@ void __init exynos_cpuidle_init(void) ...@@ -303,6 +303,11 @@ void __init exynos_cpuidle_init(void)
platform_device_register(&exynos_cpuidle); platform_device_register(&exynos_cpuidle);
} }
void __init exynos_cpufreq_init(void)
{
platform_device_register_simple("exynos-cpufreq", -1, NULL, 0);
}
void __init exynos_init_late(void) void __init exynos_init_late(void)
{ {
if (of_machine_is_compatible("samsung,exynos5440")) if (of_machine_is_compatible("samsung,exynos5440"))
......
...@@ -22,6 +22,7 @@ void exynos_init_io(void); ...@@ -22,6 +22,7 @@ void exynos_init_io(void);
void exynos4_restart(enum reboot_mode mode, const char *cmd); void exynos4_restart(enum reboot_mode mode, const char *cmd);
void exynos5_restart(enum reboot_mode mode, const char *cmd); void exynos5_restart(enum reboot_mode mode, const char *cmd);
void exynos_cpuidle_init(void); void exynos_cpuidle_init(void);
void exynos_cpufreq_init(void);
void exynos_init_late(void); void exynos_init_late(void);
void exynos_firmware_init(void); void exynos_firmware_init(void);
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
static void __init exynos4_dt_machine_init(void) static void __init exynos4_dt_machine_init(void)
{ {
exynos_cpuidle_init(); exynos_cpuidle_init();
exynos_cpufreq_init();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
} }
......
...@@ -44,6 +44,7 @@ static void __init exynos5_dt_machine_init(void) ...@@ -44,6 +44,7 @@ static void __init exynos5_dt_machine_init(void)
} }
exynos_cpuidle_init(); exynos_cpuidle_init();
exynos_cpufreq_init();
of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
} }
......
...@@ -181,7 +181,8 @@ config CPU_FREQ_GOV_CONSERVATIVE ...@@ -181,7 +181,8 @@ config CPU_FREQ_GOV_CONSERVATIVE
config GENERIC_CPUFREQ_CPU0 config GENERIC_CPUFREQ_CPU0
tristate "Generic CPU0 cpufreq driver" tristate "Generic CPU0 cpufreq driver"
depends on HAVE_CLK && REGULATOR && PM_OPP && OF depends on HAVE_CLK && REGULATOR && OF
select PM_OPP
help help
This adds a generic cpufreq driver for CPU0 frequency management. This adds a generic cpufreq driver for CPU0 frequency management.
It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) It supports both uniprocessor (UP) and symmetric multiprocessor (SMP)
......
...@@ -4,7 +4,8 @@ ...@@ -4,7 +4,8 @@
config ARM_BIG_LITTLE_CPUFREQ config ARM_BIG_LITTLE_CPUFREQ
tristate "Generic ARM big LITTLE CPUfreq driver" tristate "Generic ARM big LITTLE CPUfreq driver"
depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK
select PM_OPP
help help
This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. This enables the Generic CPUfreq driver for ARM big.LITTLE platforms.
...@@ -54,7 +55,8 @@ config ARM_EXYNOS5250_CPUFREQ ...@@ -54,7 +55,8 @@ config ARM_EXYNOS5250_CPUFREQ
config ARM_EXYNOS5440_CPUFREQ config ARM_EXYNOS5440_CPUFREQ
bool "SAMSUNG EXYNOS5440" bool "SAMSUNG EXYNOS5440"
depends on SOC_EXYNOS5440 depends on SOC_EXYNOS5440
depends on HAVE_CLK && PM_OPP && OF depends on HAVE_CLK && OF
select PM_OPP
default y default y
help help
This adds the CPUFreq driver for Samsung EXYNOS5440 This adds the CPUFreq driver for Samsung EXYNOS5440
...@@ -79,11 +81,11 @@ config ARM_HIGHBANK_CPUFREQ ...@@ -79,11 +81,11 @@ config ARM_HIGHBANK_CPUFREQ
If in doubt, say N. If in doubt, say N.
config ARM_IMX6Q_CPUFREQ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6Q cpufreq support" tristate "Freescale i.MX6 cpufreq support"
depends on SOC_IMX6Q depends on ARCH_MXC
depends on REGULATOR_ANATOP depends on REGULATOR_ANATOP
help help
This adds cpufreq driver support for Freescale i.MX6Q SOC. This adds cpufreq driver support for Freescale i.MX6 series SoCs.
If in doubt, say N. If in doubt, say N.
......
...@@ -488,7 +488,8 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) ...@@ -488,7 +488,8 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy)
static struct cpufreq_driver bL_cpufreq_driver = { static struct cpufreq_driver bL_cpufreq_driver = {
.name = "arm-big-little", .name = "arm-big-little",
.flags = CPUFREQ_STICKY | .flags = CPUFREQ_STICKY |
CPUFREQ_HAVE_GOVERNOR_PER_POLICY, CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = bL_cpufreq_set_target, .target_index = bL_cpufreq_set_target,
.get = bL_cpufreq_get_rate, .get = bL_cpufreq_get_rate,
......
...@@ -44,7 +44,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -44,7 +44,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index)
int ret; int ret;
freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000); freq_Hz = clk_round_rate(cpu_clk, freq_table[index].frequency * 1000);
if (freq_Hz < 0) if (freq_Hz <= 0)
freq_Hz = freq_table[index].frequency * 1000; freq_Hz = freq_table[index].frequency * 1000;
freq_exact = freq_Hz; freq_exact = freq_Hz;
......
...@@ -39,7 +39,7 @@ static struct cpufreq_driver *cpufreq_driver; ...@@ -39,7 +39,7 @@ static struct cpufreq_driver *cpufreq_driver;
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data); static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data);
static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback); static DEFINE_PER_CPU(struct cpufreq_policy *, cpufreq_cpu_data_fallback);
static DEFINE_RWLOCK(cpufreq_driver_lock); static DEFINE_RWLOCK(cpufreq_driver_lock);
static DEFINE_MUTEX(cpufreq_governor_lock); DEFINE_MUTEX(cpufreq_governor_lock);
static LIST_HEAD(cpufreq_policy_list); static LIST_HEAD(cpufreq_policy_list);
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
...@@ -320,6 +320,20 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, ...@@ -320,6 +320,20 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy,
} }
EXPORT_SYMBOL_GPL(cpufreq_notify_transition); EXPORT_SYMBOL_GPL(cpufreq_notify_transition);
/* Do post notifications when there are chances that transition has failed */
void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, int transition_failed)
{
cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
if (!transition_failed)
return;
swap(freqs->old, freqs->new);
cpufreq_notify_transition(policy, freqs, CPUFREQ_PRECHANGE);
cpufreq_notify_transition(policy, freqs, CPUFREQ_POSTCHANGE);
}
EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition);
/********************************************************************* /*********************************************************************
* SYSFS INTERFACE * * SYSFS INTERFACE *
...@@ -1059,6 +1073,46 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ...@@ -1059,6 +1073,46 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif,
} }
} }
/*
* Sometimes boot loaders set CPU frequency to a value outside of
* frequency table present with cpufreq core. In such cases CPU might be
* unstable if it has to run on that frequency for long duration of time
* and so its better to set it to a frequency which is specified in
* freq-table. This also makes cpufreq stats inconsistent as
* cpufreq-stats would fail to register because current frequency of CPU
* isn't found in freq-table.
*
* Because we don't want this change to effect boot process badly, we go
* for the next freq which is >= policy->cur ('cur' must be set by now,
* otherwise we will end up setting freq to lowest of the table as 'cur'
* is initialized to zero).
*
* We are passing target-freq as "policy->cur - 1" otherwise
* __cpufreq_driver_target() would simply fail, as policy->cur will be
* equal to target-freq.
*/
if ((cpufreq_driver->flags & CPUFREQ_NEED_INITIAL_FREQ_CHECK)
&& has_target()) {
/* Are we running at unknown frequency ? */
ret = cpufreq_frequency_table_get_index(policy, policy->cur);
if (ret == -EINVAL) {
/* Warn user and fix it */
pr_warn("%s: CPU%d: Running at unlisted freq: %u KHz\n",
__func__, policy->cpu, policy->cur);
ret = __cpufreq_driver_target(policy, policy->cur - 1,
CPUFREQ_RELATION_L);
/*
* Reaching here after boot in a few seconds may not
* mean that system will remain stable at "unknown"
* frequency for longer duration. Hence, a BUG_ON().
*/
BUG_ON(ret);
pr_warn("%s: CPU%d: Unlisted initial frequency changed to: %u KHz\n",
__func__, policy->cpu, policy->cur);
}
}
/* related cpus should atleast have policy->cpus */ /* related cpus should atleast have policy->cpus */
cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus);
...@@ -1725,17 +1779,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, ...@@ -1725,17 +1779,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
pr_err("%s: Failed to change cpu frequency: %d\n", pr_err("%s: Failed to change cpu frequency: %d\n",
__func__, retval); __func__, retval);
if (notify) { if (notify)
/* cpufreq_notify_post_transition(policy, &freqs, retval);
* Notify with old freq in case we failed to change
* frequency
*/
if (retval)
freqs.new = freqs.old;
cpufreq_notify_transition(policy, &freqs,
CPUFREQ_POSTCHANGE);
}
} }
out: out:
......
...@@ -119,8 +119,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, ...@@ -119,8 +119,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy,
{ {
int i; int i;
mutex_lock(&cpufreq_governor_lock);
if (!policy->governor_enabled) if (!policy->governor_enabled)
return; goto out_unlock;
if (!all_cpus) { if (!all_cpus) {
/* /*
...@@ -135,6 +136,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, ...@@ -135,6 +136,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy,
for_each_cpu(i, policy->cpus) for_each_cpu(i, policy->cpus)
__gov_queue_work(i, dbs_data, delay); __gov_queue_work(i, dbs_data, delay);
} }
out_unlock:
mutex_unlock(&cpufreq_governor_lock);
} }
EXPORT_SYMBOL_GPL(gov_queue_work); EXPORT_SYMBOL_GPL(gov_queue_work);
......
...@@ -257,6 +257,8 @@ static ssize_t show_sampling_rate_min_gov_pol \ ...@@ -257,6 +257,8 @@ static ssize_t show_sampling_rate_min_gov_pol \
return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \ return sprintf(buf, "%u\n", dbs_data->min_sampling_rate); \
} }
extern struct mutex cpufreq_governor_lock;
void dbs_check_cpu(struct dbs_data *dbs_data, int cpu); void dbs_check_cpu(struct dbs_data *dbs_data, int cpu);
bool need_load_eval(struct cpu_dbs_common_info *cdbs, bool need_load_eval(struct cpu_dbs_common_info *cdbs,
unsigned int sampling_rate); unsigned int sampling_rate);
......
...@@ -126,7 +126,7 @@ static int davinci_cpu_init(struct cpufreq_policy *policy) ...@@ -126,7 +126,7 @@ static int davinci_cpu_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver davinci_driver = { static struct cpufreq_driver davinci_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = davinci_verify_speed, .verify = davinci_verify_speed,
.target_index = davinci_target, .target_index = davinci_target,
.get = davinci_getspeed, .get = davinci_getspeed,
......
...@@ -48,7 +48,8 @@ static int dbx500_cpufreq_init(struct cpufreq_policy *policy) ...@@ -48,7 +48,8 @@ static int dbx500_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver dbx500_cpufreq_driver = { static struct cpufreq_driver dbx500_cpufreq_driver = {
.flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS, .flags = CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = dbx500_cpufreq_target, .target_index = dbx500_cpufreq_target,
.get = dbx500_cpufreq_getspeed, .get = dbx500_cpufreq_getspeed,
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/cpufreq.h> #include <linux/cpufreq.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/platform_device.h>
#include <plat/cpu.h> #include <plat/cpu.h>
...@@ -218,7 +219,7 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -218,7 +219,7 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver exynos_driver = { static struct cpufreq_driver exynos_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = exynos_target, .target_index = exynos_target,
.get = exynos_getspeed, .get = exynos_getspeed,
...@@ -232,7 +233,7 @@ static struct cpufreq_driver exynos_driver = { ...@@ -232,7 +233,7 @@ static struct cpufreq_driver exynos_driver = {
#endif #endif
}; };
static int __init exynos_cpufreq_init(void) static int exynos_cpufreq_probe(struct platform_device *pdev)
{ {
int ret = -EINVAL; int ret = -EINVAL;
...@@ -281,4 +282,12 @@ static int __init exynos_cpufreq_init(void) ...@@ -281,4 +282,12 @@ static int __init exynos_cpufreq_init(void)
kfree(exynos_info); kfree(exynos_info);
return -EINVAL; return -EINVAL;
} }
late_initcall(exynos_cpufreq_init);
static struct platform_driver exynos_cpufreq_platdrv = {
.driver = {
.name = "exynos-cpufreq",
.owner = THIS_MODULE,
},
.probe = exynos_cpufreq_probe,
};
module_platform_driver(exynos_cpufreq_platdrv);
...@@ -102,12 +102,12 @@ static void set_clkdiv(unsigned int div_index) ...@@ -102,12 +102,12 @@ static void set_clkdiv(unsigned int div_index)
cpu_relax(); cpu_relax();
} }
static void set_apll(unsigned int new_index, static void set_apll(unsigned int index)
unsigned int old_index)
{ {
unsigned int tmp, pdiv; unsigned int tmp;
unsigned int freq = apll_freq_5250[index].freq;
/* 1. MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */ /* MUX_CORE_SEL = MPLL, ARMCLK uses MPLL for lock time */
clk_set_parent(moutcore, mout_mpll); clk_set_parent(moutcore, mout_mpll);
do { do {
...@@ -116,24 +116,9 @@ static void set_apll(unsigned int new_index, ...@@ -116,24 +116,9 @@ static void set_apll(unsigned int new_index,
tmp &= 0x7; tmp &= 0x7;
} while (tmp != 0x2); } while (tmp != 0x2);
/* 2. Set APLL Lock time */ clk_set_rate(mout_apll, freq * 1000);
pdiv = ((apll_freq_5250[new_index].mps >> 8) & 0x3f);
__raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK);
/* 3. Change PLL PMS values */
tmp = __raw_readl(EXYNOS5_APLL_CON0);
tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0));
tmp |= apll_freq_5250[new_index].mps;
__raw_writel(tmp, EXYNOS5_APLL_CON0);
/* 4. wait_lock_time */ /* MUX_CORE_SEL = APLL */
do {
cpu_relax();
tmp = __raw_readl(EXYNOS5_APLL_CON0);
} while (!(tmp & (0x1 << 29)));
/* 5. MUX_CORE_SEL = APLL */
clk_set_parent(moutcore, mout_apll); clk_set_parent(moutcore, mout_apll);
do { do {
...@@ -141,55 +126,17 @@ static void set_apll(unsigned int new_index, ...@@ -141,55 +126,17 @@ static void set_apll(unsigned int new_index,
tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU);
tmp &= (0x7 << 16); tmp &= (0x7 << 16);
} while (tmp != (0x1 << 16)); } while (tmp != (0x1 << 16));
}
static bool exynos5250_pms_change(unsigned int old_index, unsigned int new_index)
{
unsigned int old_pm = apll_freq_5250[old_index].mps >> 8;
unsigned int new_pm = apll_freq_5250[new_index].mps >> 8;
return (old_pm == new_pm) ? 0 : 1;
} }
static void exynos5250_set_frequency(unsigned int old_index, static void exynos5250_set_frequency(unsigned int old_index,
unsigned int new_index) unsigned int new_index)
{ {
unsigned int tmp;
if (old_index > new_index) { if (old_index > new_index) {
if (!exynos5250_pms_change(old_index, new_index)) {
/* 1. Change the system clock divider values */
set_clkdiv(new_index); set_clkdiv(new_index);
/* 2. Change just s value in apll m,p,s value */ set_apll(new_index);
tmp = __raw_readl(EXYNOS5_APLL_CON0);
tmp &= ~(0x7 << 0);
tmp |= apll_freq_5250[new_index].mps & 0x7;
__raw_writel(tmp, EXYNOS5_APLL_CON0);
} else {
/* Clock Configuration Procedure */
/* 1. Change the system clock divider values */
set_clkdiv(new_index);
/* 2. Change the apll m,p,s value */
set_apll(new_index, old_index);
}
} else if (old_index < new_index) { } else if (old_index < new_index) {
if (!exynos5250_pms_change(old_index, new_index)) { set_apll(new_index);
/* 1. Change just s value in apll m,p,s value */
tmp = __raw_readl(EXYNOS5_APLL_CON0);
tmp &= ~(0x7 << 0);
tmp |= apll_freq_5250[new_index].mps & 0x7;
__raw_writel(tmp, EXYNOS5_APLL_CON0);
/* 2. Change the system clock divider values */
set_clkdiv(new_index); set_clkdiv(new_index);
} else {
/* Clock Configuration Procedure */
/* 1. Change the apll m,p,s value */
set_apll(new_index, old_index);
/* 2. Change the system clock divider values */
set_clkdiv(new_index);
}
} }
} }
...@@ -222,7 +169,6 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) ...@@ -222,7 +169,6 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info)
info->volt_table = exynos5250_volt_table; info->volt_table = exynos5250_volt_table;
info->freq_table = exynos5250_freq_table; info->freq_table = exynos5250_freq_table;
info->set_freq = exynos5250_set_frequency; info->set_freq = exynos5250_set_frequency;
info->need_apll_change = exynos5250_pms_change;
return 0; return 0;
......
...@@ -312,7 +312,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -312,7 +312,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver exynos_driver = { static struct cpufreq_driver exynos_driver = {
.flags = CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION, .flags = CPUFREQ_STICKY | CPUFREQ_ASYNC_NOTIFICATION |
CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = exynos_target, .target_index = exynos_target,
.get = exynos_getspeed, .get = exynos_getspeed,
......
...@@ -178,7 +178,29 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, ...@@ -178,7 +178,29 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
} }
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target); EXPORT_SYMBOL_GPL(cpufreq_frequency_table_target);
int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
unsigned int freq)
{
struct cpufreq_frequency_table *table;
int i;
table = cpufreq_frequency_get_table(policy->cpu);
if (unlikely(!table)) {
pr_debug("%s: Unable to find frequency table\n", __func__);
return -ENOENT;
}
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
if (table[i].frequency == freq)
return i;
}
return -EINVAL;
}
EXPORT_SYMBOL_GPL(cpufreq_frequency_table_get_index);
static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table); static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table);
/** /**
* show_available_freqs - show available frequencies for the specified CPU * show_available_freqs - show available frequencies for the specified CPU
*/ */
......
...@@ -35,6 +35,9 @@ static struct device *cpu_dev; ...@@ -35,6 +35,9 @@ static struct device *cpu_dev;
static struct cpufreq_frequency_table *freq_table; static struct cpufreq_frequency_table *freq_table;
static unsigned int transition_latency; static unsigned int transition_latency;
static u32 *imx6_soc_volt;
static u32 soc_opp_count;
static unsigned int imx6q_get_speed(unsigned int cpu) static unsigned int imx6q_get_speed(unsigned int cpu)
{ {
return clk_get_rate(arm_clk) / 1000; return clk_get_rate(arm_clk) / 1000;
...@@ -69,23 +72,22 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -69,23 +72,22 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
/* scaling up? scale voltage before frequency */ /* scaling up? scale voltage before frequency */
if (new_freq > old_freq) { if (new_freq > old_freq) {
ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
if (ret) {
dev_err(cpu_dev, "failed to scale vddpu up: %d\n", ret);
return ret;
}
ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
if (ret) {
dev_err(cpu_dev, "failed to scale vddsoc up: %d\n", ret);
return ret;
}
ret = regulator_set_voltage_tol(arm_reg, volt, 0); ret = regulator_set_voltage_tol(arm_reg, volt, 0);
if (ret) { if (ret) {
dev_err(cpu_dev, dev_err(cpu_dev,
"failed to scale vddarm up: %d\n", ret); "failed to scale vddarm up: %d\n", ret);
return ret; return ret;
} }
/*
* Need to increase vddpu and vddsoc for safety
* if we are about to run at 1.2 GHz.
*/
if (new_freq == FREQ_1P2_GHZ / 1000) {
regulator_set_voltage_tol(pu_reg,
PU_SOC_VOLTAGE_HIGH, 0);
regulator_set_voltage_tol(soc_reg,
PU_SOC_VOLTAGE_HIGH, 0);
}
} }
/* /*
...@@ -120,12 +122,15 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) ...@@ -120,12 +122,15 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
"failed to scale vddarm down: %d\n", ret); "failed to scale vddarm down: %d\n", ret);
ret = 0; ret = 0;
} }
ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0);
if (old_freq == FREQ_1P2_GHZ / 1000) { if (ret) {
regulator_set_voltage_tol(pu_reg, dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret);
PU_SOC_VOLTAGE_NORMAL, 0); ret = 0;
regulator_set_voltage_tol(soc_reg, }
PU_SOC_VOLTAGE_NORMAL, 0); ret = regulator_set_voltage_tol(pu_reg, imx6_soc_volt[index], 0);
if (ret) {
dev_warn(cpu_dev, "failed to scale vddpu down: %d\n", ret);
ret = 0;
} }
} }
...@@ -138,6 +143,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy) ...@@ -138,6 +143,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver imx6q_cpufreq_driver = { static struct cpufreq_driver imx6q_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = imx6q_set_target, .target_index = imx6q_set_target,
.get = imx6q_get_speed, .get = imx6q_get_speed,
...@@ -153,6 +159,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -153,6 +159,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
unsigned long min_volt, max_volt; unsigned long min_volt, max_volt;
int num, ret; int num, ret;
const struct property *prop;
const __be32 *val;
u32 nr, i, j;
cpu_dev = get_cpu_device(0); cpu_dev = get_cpu_device(0);
if (!cpu_dev) { if (!cpu_dev) {
...@@ -187,13 +196,26 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -187,13 +196,26 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
goto put_node; goto put_node;
} }
/* We expect an OPP table supplied by platform */ /*
* We expect an OPP table supplied by platform.
* Just, incase the platform did not supply the OPP
* table, it will try to get it.
*/
num = dev_pm_opp_get_opp_count(cpu_dev);
if (num < 0) {
ret = of_init_opp_table(cpu_dev);
if (ret < 0) {
dev_err(cpu_dev, "failed to init OPP table: %d\n", ret);
goto put_node;
}
num = dev_pm_opp_get_opp_count(cpu_dev); num = dev_pm_opp_get_opp_count(cpu_dev);
if (num < 0) { if (num < 0) {
ret = num; ret = num;
dev_err(cpu_dev, "no OPP table is found: %d\n", ret); dev_err(cpu_dev, "no OPP table is found: %d\n", ret);
goto put_node; goto put_node;
} }
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) { if (ret) {
...@@ -201,9 +223,61 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -201,9 +223,61 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
goto put_node; goto put_node;
} }
/* Make imx6_soc_volt array's size same as arm opp number */
imx6_soc_volt = devm_kzalloc(cpu_dev, sizeof(*imx6_soc_volt) * num, GFP_KERNEL);
if (imx6_soc_volt == NULL) {
ret = -ENOMEM;
goto free_freq_table;
}
prop = of_find_property(np, "fsl,soc-operating-points", NULL);
if (!prop || !prop->value)
goto soc_opp_out;
/*
* Each OPP is a set of tuples consisting of frequency and
* voltage like <freq-kHz vol-uV>.
*/
nr = prop->length / sizeof(u32);
if (nr % 2 || (nr / 2) < num)
goto soc_opp_out;
for (j = 0; j < num; j++) {
val = prop->value;
for (i = 0; i < nr / 2; i++) {
unsigned long freq = be32_to_cpup(val++);
unsigned long volt = be32_to_cpup(val++);
if (freq_table[j].frequency == freq) {
imx6_soc_volt[soc_opp_count++] = volt;
break;
}
}
}
soc_opp_out:
/* use fixed soc opp volt if no valid soc opp info found in dtb */
if (soc_opp_count != num) {
dev_warn(cpu_dev, "can NOT find valid fsl,soc-operating-points property in dtb, use default value!\n");
for (j = 0; j < num; j++)
imx6_soc_volt[j] = PU_SOC_VOLTAGE_NORMAL;
if (freq_table[num - 1].frequency * 1000 == FREQ_1P2_GHZ)
imx6_soc_volt[num - 1] = PU_SOC_VOLTAGE_HIGH;
}
if (of_property_read_u32(np, "clock-latency", &transition_latency)) if (of_property_read_u32(np, "clock-latency", &transition_latency))
transition_latency = CPUFREQ_ETERNAL; transition_latency = CPUFREQ_ETERNAL;
/*
* Calculate the ramp time for max voltage change in the
* VDDSOC and VDDPU regulators.
*/
ret = regulator_set_voltage_time(soc_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]);
if (ret > 0)
transition_latency += ret * 1000;
ret = regulator_set_voltage_time(pu_reg, imx6_soc_volt[0], imx6_soc_volt[num - 1]);
if (ret > 0)
transition_latency += ret * 1000;
/* /*
* OPP is maintained in order of increasing frequency, and * OPP is maintained in order of increasing frequency, and
* freq_table initialised from OPP is therefore sorted in the * freq_table initialised from OPP is therefore sorted in the
...@@ -221,18 +295,6 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) ...@@ -221,18 +295,6 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
if (ret > 0) if (ret > 0)
transition_latency += ret * 1000; transition_latency += ret * 1000;
/* Count vddpu and vddsoc latency in for 1.2 GHz support */
if (freq_table[num].frequency == FREQ_1P2_GHZ / 1000) {
ret = regulator_set_voltage_time(pu_reg, PU_SOC_VOLTAGE_NORMAL,
PU_SOC_VOLTAGE_HIGH);
if (ret > 0)
transition_latency += ret * 1000;
ret = regulator_set_voltage_time(soc_reg, PU_SOC_VOLTAGE_NORMAL,
PU_SOC_VOLTAGE_HIGH);
if (ret > 0)
transition_latency += ret * 1000;
}
ret = cpufreq_register_driver(&imx6q_cpufreq_driver); ret = cpufreq_register_driver(&imx6q_cpufreq_driver);
if (ret) { if (ret) {
dev_err(cpu_dev, "failed register driver: %d\n", ret); dev_err(cpu_dev, "failed register driver: %d\n", ret);
......
...@@ -190,6 +190,7 @@ static int integrator_cpufreq_init(struct cpufreq_policy *policy) ...@@ -190,6 +190,7 @@ static int integrator_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver integrator_driver = { static struct cpufreq_driver integrator_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = integrator_verify_policy, .verify = integrator_verify_policy,
.target = integrator_set_target, .target = integrator_set_target,
.get = integrator_get, .get = integrator_get,
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#define SAMPLE_COUNT 3 #define SAMPLE_COUNT 3
#define BYT_RATIOS 0x66a #define BYT_RATIOS 0x66a
#define BYT_VIDS 0x66b
#define FRAC_BITS 8 #define FRAC_BITS 8
#define int_tofp(X) ((int64_t)(X) << FRAC_BITS) #define int_tofp(X) ((int64_t)(X) << FRAC_BITS)
...@@ -64,6 +65,12 @@ struct pstate_data { ...@@ -64,6 +65,12 @@ struct pstate_data {
int turbo_pstate; int turbo_pstate;
}; };
struct vid_data {
int32_t min;
int32_t max;
int32_t ratio;
};
struct _pid { struct _pid {
int setpoint; int setpoint;
int32_t integral; int32_t integral;
...@@ -82,10 +89,9 @@ struct cpudata { ...@@ -82,10 +89,9 @@ struct cpudata {
struct timer_list timer; struct timer_list timer;
struct pstate_data pstate; struct pstate_data pstate;
struct vid_data vid;
struct _pid pid; struct _pid pid;
int min_pstate_count;
u64 prev_aperf; u64 prev_aperf;
u64 prev_mperf; u64 prev_mperf;
int sample_ptr; int sample_ptr;
...@@ -106,7 +112,8 @@ struct pstate_funcs { ...@@ -106,7 +112,8 @@ struct pstate_funcs {
int (*get_max)(void); int (*get_max)(void);
int (*get_min)(void); int (*get_min)(void);
int (*get_turbo)(void); int (*get_turbo)(void);
void (*set)(int pstate); void (*set)(struct cpudata*, int pstate);
void (*get_vid)(struct cpudata *);
}; };
struct cpu_defaults { struct cpu_defaults {
...@@ -358,6 +365,42 @@ static int byt_get_max_pstate(void) ...@@ -358,6 +365,42 @@ static int byt_get_max_pstate(void)
return (value >> 16) & 0xFF; return (value >> 16) & 0xFF;
} }
static void byt_set_pstate(struct cpudata *cpudata, int pstate)
{
u64 val;
int32_t vid_fp;
u32 vid;
val = pstate << 8;
if (limits.no_turbo)
val |= (u64)1 << 32;
vid_fp = cpudata->vid.min + mul_fp(
int_tofp(pstate - cpudata->pstate.min_pstate),
cpudata->vid.ratio);
vid_fp = clamp_t(int32_t, vid_fp, cpudata->vid.min, cpudata->vid.max);
vid = fp_toint(vid_fp);
val |= vid;
wrmsrl(MSR_IA32_PERF_CTL, val);
}
static void byt_get_vid(struct cpudata *cpudata)
{
u64 value;
rdmsrl(BYT_VIDS, value);
cpudata->vid.min = int_tofp((value >> 8) & 0x7f);
cpudata->vid.max = int_tofp((value >> 16) & 0x7f);
cpudata->vid.ratio = div_fp(
cpudata->vid.max - cpudata->vid.min,
int_tofp(cpudata->pstate.max_pstate -
cpudata->pstate.min_pstate));
}
static int core_get_min_pstate(void) static int core_get_min_pstate(void)
{ {
u64 value; u64 value;
...@@ -384,7 +427,7 @@ static int core_get_turbo_pstate(void) ...@@ -384,7 +427,7 @@ static int core_get_turbo_pstate(void)
return ret; return ret;
} }
static void core_set_pstate(int pstate) static void core_set_pstate(struct cpudata *cpudata, int pstate)
{ {
u64 val; u64 val;
...@@ -425,7 +468,8 @@ static struct cpu_defaults byt_params = { ...@@ -425,7 +468,8 @@ static struct cpu_defaults byt_params = {
.get_max = byt_get_max_pstate, .get_max = byt_get_max_pstate,
.get_min = byt_get_min_pstate, .get_min = byt_get_min_pstate,
.get_turbo = byt_get_max_pstate, .get_turbo = byt_get_max_pstate,
.set = core_set_pstate, .set = byt_set_pstate,
.get_vid = byt_get_vid,
}, },
}; };
...@@ -462,7 +506,7 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) ...@@ -462,7 +506,7 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
cpu->pstate.current_pstate = pstate; cpu->pstate.current_pstate = pstate;
pstate_funcs.set(pstate); pstate_funcs.set(cpu, pstate);
} }
static inline void intel_pstate_pstate_increase(struct cpudata *cpu, int steps) static inline void intel_pstate_pstate_increase(struct cpudata *cpu, int steps)
...@@ -488,6 +532,9 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) ...@@ -488,6 +532,9 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
cpu->pstate.max_pstate = pstate_funcs.get_max(); cpu->pstate.max_pstate = pstate_funcs.get_max();
cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(); cpu->pstate.turbo_pstate = pstate_funcs.get_turbo();
if (pstate_funcs.get_vid)
pstate_funcs.get_vid(cpu);
/* /*
* goto max pstate so we don't slow up boot if we are built-in if we are * goto max pstate so we don't slow up boot if we are built-in if we are
* a module we will take care of it during normal operation * a module we will take care of it during normal operation
...@@ -568,15 +615,6 @@ static void intel_pstate_timer_func(unsigned long __data) ...@@ -568,15 +615,6 @@ static void intel_pstate_timer_func(unsigned long __data)
intel_pstate_sample(cpu); intel_pstate_sample(cpu);
intel_pstate_adjust_busy_pstate(cpu); intel_pstate_adjust_busy_pstate(cpu);
if (cpu->pstate.current_pstate == cpu->pstate.min_pstate) {
cpu->min_pstate_count++;
if (!(cpu->min_pstate_count % 5)) {
intel_pstate_set_pstate(cpu, cpu->pstate.max_pstate);
}
} else
cpu->min_pstate_count = 0;
intel_pstate_set_sample_time(cpu); intel_pstate_set_sample_time(cpu);
} }
...@@ -782,6 +820,7 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs) ...@@ -782,6 +820,7 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs)
pstate_funcs.get_min = funcs->get_min; pstate_funcs.get_min = funcs->get_min;
pstate_funcs.get_turbo = funcs->get_turbo; pstate_funcs.get_turbo = funcs->get_turbo;
pstate_funcs.set = funcs->set; pstate_funcs.set = funcs->set;
pstate_funcs.get_vid = funcs->get_vid;
} }
#if IS_ENABLED(CONFIG_ACPI) #if IS_ENABLED(CONFIG_ACPI)
......
...@@ -97,6 +97,7 @@ static int kirkwood_cpufreq_cpu_init(struct cpufreq_policy *policy) ...@@ -97,6 +97,7 @@ static int kirkwood_cpufreq_cpu_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver kirkwood_cpufreq_driver = { static struct cpufreq_driver kirkwood_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.get = kirkwood_cpufreq_get_cpu_frequency, .get = kirkwood_cpufreq_get_cpu_frequency,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = kirkwood_cpufreq_target, .target_index = kirkwood_cpufreq_target,
......
...@@ -162,7 +162,7 @@ static int omap_cpu_exit(struct cpufreq_policy *policy) ...@@ -162,7 +162,7 @@ static int omap_cpu_exit(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver omap_driver = { static struct cpufreq_driver omap_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = omap_target, .target_index = omap_target,
.get = omap_getspeed, .get = omap_getspeed,
......
...@@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, ...@@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
cpu, target_freq, cpu, target_freq,
(pcch_virt_addr + pcc_cpu_data->input_offset)); (pcch_virt_addr + pcc_cpu_data->input_offset));
freqs.old = policy->cur;
freqs.new = target_freq; freqs.new = target_freq;
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
...@@ -228,25 +229,20 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, ...@@ -228,25 +229,20 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy,
memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ); memset_io((pcch_virt_addr + pcc_cpu_data->input_offset), 0, BUF_SZ);
status = ioread16(&pcch_hdr->status); status = ioread16(&pcch_hdr->status);
iowrite16(0, &pcch_hdr->status);
cpufreq_notify_post_transition(policy, &freqs, status != CMD_COMPLETE);
spin_unlock(&pcc_lock);
if (status != CMD_COMPLETE) { if (status != CMD_COMPLETE) {
pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", pr_debug("target: FAILED for cpu %d, with status: 0x%x\n",
cpu, status); cpu, status);
goto cmd_incomplete; return -EINVAL;
} }
iowrite16(0, &pcch_hdr->status);
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu); pr_debug("target: was SUCCESSFUL for cpu %d\n", cpu);
spin_unlock(&pcc_lock);
return 0; return 0;
cmd_incomplete:
freqs.new = freqs.old;
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
iowrite16(0, &pcch_hdr->status);
spin_unlock(&pcc_lock);
return -EINVAL;
} }
static int pcc_get_offset(int cpu) static int pcc_get_offset(int cpu)
......
...@@ -26,20 +26,52 @@ ...@@ -26,20 +26,52 @@
static unsigned int busfreq; /* FSB, in 10 kHz */ static unsigned int busfreq; /* FSB, in 10 kHz */
static unsigned int max_multiplier; static unsigned int max_multiplier;
static unsigned int param_busfreq = 0;
static unsigned int param_max_multiplier = 0;
module_param_named(max_multiplier, param_max_multiplier, uint, S_IRUGO);
MODULE_PARM_DESC(max_multiplier, "Maximum multiplier (allowed values: 20 30 35 40 45 50 55 60)");
module_param_named(bus_frequency, param_busfreq, uint, S_IRUGO);
MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz");
/* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */ /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */
static struct cpufreq_frequency_table clock_ratio[] = { static struct cpufreq_frequency_table clock_ratio[] = {
{45, /* 000 -> 4.5x */ 0}, {60, /* 110 -> 6.0x */ 0},
{55, /* 011 -> 5.5x */ 0},
{50, /* 001 -> 5.0x */ 0}, {50, /* 001 -> 5.0x */ 0},
{45, /* 000 -> 4.5x */ 0},
{40, /* 010 -> 4.0x */ 0}, {40, /* 010 -> 4.0x */ 0},
{55, /* 011 -> 5.5x */ 0},
{20, /* 100 -> 2.0x */ 0},
{30, /* 101 -> 3.0x */ 0},
{60, /* 110 -> 6.0x */ 0},
{35, /* 111 -> 3.5x */ 0}, {35, /* 111 -> 3.5x */ 0},
{30, /* 101 -> 3.0x */ 0},
{20, /* 100 -> 2.0x */ 0},
{0, CPUFREQ_TABLE_END} {0, CPUFREQ_TABLE_END}
}; };
static const u8 index_to_register[8] = { 6, 3, 1, 0, 2, 7, 5, 4 };
static const u8 register_to_index[8] = { 3, 2, 4, 1, 7, 6, 0, 5 };
static const struct {
unsigned freq;
unsigned mult;
} usual_frequency_table[] = {
{ 400000, 40 }, // 100 * 4
{ 450000, 45 }, // 100 * 4.5
{ 475000, 50 }, // 95 * 5
{ 500000, 50 }, // 100 * 5
{ 506250, 45 }, // 112.5 * 4.5
{ 533500, 55 }, // 97 * 5.5
{ 550000, 55 }, // 100 * 5.5
{ 562500, 50 }, // 112.5 * 5
{ 570000, 60 }, // 95 * 6
{ 600000, 60 }, // 100 * 6
{ 618750, 55 }, // 112.5 * 5.5
{ 660000, 55 }, // 120 * 5.5
{ 675000, 60 }, // 112.5 * 6
{ 720000, 60 }, // 120 * 6
};
#define FREQ_RANGE 3000
/** /**
* powernow_k6_get_cpu_multiplier - returns the current FSB multiplier * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier
...@@ -49,18 +81,53 @@ static struct cpufreq_frequency_table clock_ratio[] = { ...@@ -49,18 +81,53 @@ static struct cpufreq_frequency_table clock_ratio[] = {
*/ */
static int powernow_k6_get_cpu_multiplier(void) static int powernow_k6_get_cpu_multiplier(void)
{ {
u64 invalue = 0; unsigned long invalue = 0;
u32 msrval; u32 msrval;
local_irq_disable();
msrval = POWERNOW_IOPORT + 0x1; msrval = POWERNOW_IOPORT + 0x1;
wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */ wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
invalue = inl(POWERNOW_IOPORT + 0x8); invalue = inl(POWERNOW_IOPORT + 0x8);
msrval = POWERNOW_IOPORT + 0x0; msrval = POWERNOW_IOPORT + 0x0;
wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
return clock_ratio[(invalue >> 5)&7].driver_data; local_irq_enable();
return clock_ratio[register_to_index[(invalue >> 5)&7]].driver_data;
} }
static void powernow_k6_set_cpu_multiplier(unsigned int best_i)
{
unsigned long outvalue, invalue;
unsigned long msrval;
unsigned long cr0;
/* we now need to transform best_i to the BVC format, see AMD#23446 */
/*
* The processor doesn't respond to inquiry cycles while changing the
* frequency, so we must disable cache.
*/
local_irq_disable();
cr0 = read_cr0();
write_cr0(cr0 | X86_CR0_CD);
wbinvd();
outvalue = (1<<12) | (1<<10) | (1<<9) | (index_to_register[best_i]<<5);
msrval = POWERNOW_IOPORT + 0x1;
wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
invalue = inl(POWERNOW_IOPORT + 0x8);
invalue = invalue & 0x1f;
outvalue = outvalue | invalue;
outl(outvalue, (POWERNOW_IOPORT + 0x8));
msrval = POWERNOW_IOPORT + 0x0;
wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
write_cr0(cr0);
local_irq_enable();
}
/** /**
* powernow_k6_target - set the PowerNow! multiplier * powernow_k6_target - set the PowerNow! multiplier
...@@ -71,8 +138,6 @@ static int powernow_k6_get_cpu_multiplier(void) ...@@ -71,8 +138,6 @@ static int powernow_k6_get_cpu_multiplier(void)
static int powernow_k6_target(struct cpufreq_policy *policy, static int powernow_k6_target(struct cpufreq_policy *policy,
unsigned int best_i) unsigned int best_i)
{ {
unsigned long outvalue = 0, invalue = 0;
unsigned long msrval;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
if (clock_ratio[best_i].driver_data > max_multiplier) { if (clock_ratio[best_i].driver_data > max_multiplier) {
...@@ -85,35 +150,63 @@ static int powernow_k6_target(struct cpufreq_policy *policy, ...@@ -85,35 +150,63 @@ static int powernow_k6_target(struct cpufreq_policy *policy,
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
/* we now need to transform best_i to the BVC format, see AMD#23446 */ powernow_k6_set_cpu_multiplier(best_i);
outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5);
msrval = POWERNOW_IOPORT + 0x1;
wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */
invalue = inl(POWERNOW_IOPORT + 0x8);
invalue = invalue & 0xf;
outvalue = outvalue | invalue;
outl(outvalue , (POWERNOW_IOPORT + 0x8));
msrval = POWERNOW_IOPORT + 0x0;
wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
return 0; return 0;
} }
static int powernow_k6_cpu_init(struct cpufreq_policy *policy) static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
{ {
unsigned int i, f; unsigned int i, f;
unsigned khz;
if (policy->cpu != 0) if (policy->cpu != 0)
return -ENODEV; return -ENODEV;
/* get frequencies */ max_multiplier = 0;
max_multiplier = powernow_k6_get_cpu_multiplier(); khz = cpu_khz;
busfreq = cpu_khz / max_multiplier; for (i = 0; i < ARRAY_SIZE(usual_frequency_table); i++) {
if (khz >= usual_frequency_table[i].freq - FREQ_RANGE &&
khz <= usual_frequency_table[i].freq + FREQ_RANGE) {
khz = usual_frequency_table[i].freq;
max_multiplier = usual_frequency_table[i].mult;
break;
}
}
if (param_max_multiplier) {
for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
if (clock_ratio[i].driver_data == param_max_multiplier) {
max_multiplier = param_max_multiplier;
goto have_max_multiplier;
}
}
printk(KERN_ERR "powernow-k6: invalid max_multiplier parameter, valid parameters 20, 30, 35, 40, 45, 50, 55, 60\n");
return -EINVAL;
}
if (!max_multiplier) {
printk(KERN_WARNING "powernow-k6: unknown frequency %u, cannot determine current multiplier\n", khz);
printk(KERN_WARNING "powernow-k6: use module parameters max_multiplier and bus_frequency\n");
return -EOPNOTSUPP;
}
have_max_multiplier:
param_max_multiplier = max_multiplier;
if (param_busfreq) {
if (param_busfreq >= 50000 && param_busfreq <= 150000) {
busfreq = param_busfreq / 10;
goto have_busfreq;
}
printk(KERN_ERR "powernow-k6: invalid bus_frequency parameter, allowed range 50000 - 150000 kHz\n");
return -EINVAL;
}
busfreq = khz / max_multiplier;
have_busfreq:
param_busfreq = busfreq * 10;
/* table init */ /* table init */
for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) {
...@@ -125,7 +218,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) ...@@ -125,7 +218,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy)
} }
/* cpuinfo and default policy values */ /* cpuinfo and default policy values */
policy->cpuinfo.transition_latency = 200000; policy->cpuinfo.transition_latency = 500000;
return cpufreq_table_validate_and_show(policy, clock_ratio); return cpufreq_table_validate_and_show(policy, clock_ratio);
} }
......
...@@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, ...@@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data,
cpufreq_cpu_put(policy); cpufreq_cpu_put(policy);
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
res = transition_fid_vid(data, fid, vid); res = transition_fid_vid(data, fid, vid);
if (res) cpufreq_notify_post_transition(policy, &freqs, res);
freqs.new = freqs.old;
else
freqs.new = find_khz_freq_from_fid(data->currfid);
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
return res; return res;
} }
......
...@@ -423,6 +423,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) ...@@ -423,6 +423,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver pxa_cpufreq_driver = { static struct cpufreq_driver pxa_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = pxa_set_target, .target_index = pxa_set_target,
.init = pxa_cpufreq_init, .init = pxa_cpufreq_init,
......
...@@ -201,6 +201,7 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy) ...@@ -201,6 +201,7 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver pxa3xx_cpufreq_driver = { static struct cpufreq_driver pxa3xx_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = pxa3xx_cpufreq_set, .target_index = pxa3xx_cpufreq_set,
.init = pxa3xx_cpufreq_init, .init = pxa3xx_cpufreq_init,
......
...@@ -481,7 +481,7 @@ static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy) ...@@ -481,7 +481,7 @@ static int __init s3c2416_cpufreq_driver_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver s3c2416_cpufreq_driver = { static struct cpufreq_driver s3c2416_cpufreq_driver = {
.flags = 0, .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = s3c2416_cpufreq_set_target, .target_index = s3c2416_cpufreq_set_target,
.get = s3c2416_cpufreq_get_speed, .get = s3c2416_cpufreq_get_speed,
......
...@@ -22,8 +22,6 @@ ...@@ -22,8 +22,6 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/io.h> #include <linux/io.h>
#include <mach/hardware.h>
#include <asm/mach/arch.h> #include <asm/mach/arch.h>
#include <asm/mach/map.h> #include <asm/mach/map.h>
...@@ -55,7 +53,7 @@ static inline int within_khz(unsigned long a, unsigned long b) ...@@ -55,7 +53,7 @@ static inline int within_khz(unsigned long a, unsigned long b)
* specified in @cfg. The values are stored in @cfg for later use * specified in @cfg. The values are stored in @cfg for later use
* by the relevant set routine if the request settings can be reached. * by the relevant set routine if the request settings can be reached.
*/ */
int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg) static int s3c2440_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
{ {
unsigned int hdiv, pdiv; unsigned int hdiv, pdiv;
unsigned long hclk, fclk, armclk; unsigned long hclk, fclk, armclk;
...@@ -242,7 +240,7 @@ static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg, ...@@ -242,7 +240,7 @@ static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg,
return ret; return ret;
} }
struct s3c_cpufreq_info s3c2440_cpufreq_info = { static struct s3c_cpufreq_info s3c2440_cpufreq_info = {
.max = { .max = {
.fclk = 400000000, .fclk = 400000000,
.hclk = 133333333, .hclk = 133333333,
......
...@@ -448,7 +448,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) ...@@ -448,7 +448,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy)
#endif #endif
static struct cpufreq_driver s3c24xx_driver = { static struct cpufreq_driver s3c24xx_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.target = s3c_cpufreq_target, .target = s3c_cpufreq_target,
.get = s3c_cpufreq_get, .get = s3c_cpufreq_get,
.init = s3c_cpufreq_init, .init = s3c_cpufreq_init,
...@@ -509,7 +509,7 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) ...@@ -509,7 +509,7 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board)
return 0; return 0;
} }
int __init s3c_cpufreq_auto_io(void) static int __init s3c_cpufreq_auto_io(void)
{ {
int ret; int ret;
......
...@@ -226,7 +226,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) ...@@ -226,7 +226,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver s3c64xx_cpufreq_driver = { static struct cpufreq_driver s3c64xx_cpufreq_driver = {
.flags = 0, .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = s3c64xx_cpufreq_set_target, .target_index = s3c64xx_cpufreq_set_target,
.get = s3c64xx_cpufreq_get_speed, .get = s3c64xx_cpufreq_get_speed,
......
...@@ -560,7 +560,7 @@ static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this, ...@@ -560,7 +560,7 @@ static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this,
} }
static struct cpufreq_driver s5pv210_driver = { static struct cpufreq_driver s5pv210_driver = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = s5pv210_target, .target_index = s5pv210_target,
.get = s5pv210_getspeed, .get = s5pv210_getspeed,
......
...@@ -201,7 +201,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy) ...@@ -201,7 +201,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver sa1100_driver __refdata = { static struct cpufreq_driver sa1100_driver __refdata = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = sa1100_target, .target_index = sa1100_target,
.get = sa11x0_getspeed, .get = sa11x0_getspeed,
......
...@@ -312,7 +312,7 @@ static int __init sa1110_cpu_init(struct cpufreq_policy *policy) ...@@ -312,7 +312,7 @@ static int __init sa1110_cpu_init(struct cpufreq_policy *policy)
/* sa1110_driver needs __refdata because it must remain after init registers /* sa1110_driver needs __refdata because it must remain after init registers
* it with cpufreq_register_driver() */ * it with cpufreq_register_driver() */
static struct cpufreq_driver sa1110_driver __refdata = { static struct cpufreq_driver sa1110_driver __refdata = {
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = sa1110_target, .target_index = sa1110_target,
.get = sa11x0_getspeed, .get = sa11x0_getspeed,
......
...@@ -138,7 +138,7 @@ static int spear_cpufreq_target(struct cpufreq_policy *policy, ...@@ -138,7 +138,7 @@ static int spear_cpufreq_target(struct cpufreq_policy *policy,
} }
newfreq = clk_round_rate(srcclk, newfreq * mult); newfreq = clk_round_rate(srcclk, newfreq * mult);
if (newfreq < 0) { if (newfreq <= 0) {
pr_err("clk_round_rate failed for cpu src clock\n"); pr_err("clk_round_rate failed for cpu src clock\n");
return newfreq; return newfreq;
} }
...@@ -162,7 +162,7 @@ static int spear_cpufreq_init(struct cpufreq_policy *policy) ...@@ -162,7 +162,7 @@ static int spear_cpufreq_init(struct cpufreq_policy *policy)
static struct cpufreq_driver spear_cpufreq_driver = { static struct cpufreq_driver spear_cpufreq_driver = {
.name = "cpufreq-spear", .name = "cpufreq-spear",
.flags = CPUFREQ_STICKY, .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = spear_cpufreq_target, .target_index = spear_cpufreq_target,
.get = spear_cpufreq_get, .get = spear_cpufreq_get,
......
...@@ -214,6 +214,7 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy) ...@@ -214,6 +214,7 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy)
} }
static struct cpufreq_driver tegra_cpufreq_driver = { static struct cpufreq_driver tegra_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify, .verify = cpufreq_generic_frequency_table_verify,
.target_index = tegra_target, .target_index = tegra_target,
.get = tegra_getspeed, .get = tegra_getspeed,
......
...@@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy, ...@@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation) unsigned int relation)
{ {
unsigned int cur = ucv2_getspeed(0);
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
struct clk *mclk = clk_get(NULL, "MAIN_CLK"); struct clk *mclk = clk_get(NULL, "MAIN_CLK");
int ret;
cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); freqs.old = policy->cur;
if (!clk_set_rate(mclk, target_freq * 1000)) {
freqs.old = cur;
freqs.new = target_freq; freqs.new = target_freq;
}
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
ret = clk_set_rate(mclk, target_freq * 1000);
cpufreq_notify_post_transition(policy, &freqs, ret);
return 0; return ret;
} }
static int __init ucv2_cpu_init(struct cpufreq_policy *policy) static int __init ucv2_cpu_init(struct cpufreq_policy *policy)
......
...@@ -252,6 +252,15 @@ struct cpufreq_driver { ...@@ -252,6 +252,15 @@ struct cpufreq_driver {
*/ */
#define CPUFREQ_ASYNC_NOTIFICATION (1 << 4) #define CPUFREQ_ASYNC_NOTIFICATION (1 << 4)
/*
* Set by drivers which want cpufreq core to check if CPU is running at a
* frequency present in freq-table exposed by the driver. For these drivers if
* CPU is found running at an out of table freq, we will try to set it to a freq
* from the table. And if that fails, we will stop further boot process by
* issuing a BUG_ON().
*/
#define CPUFREQ_NEED_INITIAL_FREQ_CHECK (1 << 5)
int cpufreq_register_driver(struct cpufreq_driver *driver_data); int cpufreq_register_driver(struct cpufreq_driver *driver_data);
int cpufreq_unregister_driver(struct cpufreq_driver *driver_data); int cpufreq_unregister_driver(struct cpufreq_driver *driver_data);
...@@ -306,6 +315,8 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list); ...@@ -306,6 +315,8 @@ int cpufreq_unregister_notifier(struct notifier_block *nb, unsigned int list);
void cpufreq_notify_transition(struct cpufreq_policy *policy, void cpufreq_notify_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, unsigned int state); struct cpufreq_freqs *freqs, unsigned int state);
void cpufreq_notify_post_transition(struct cpufreq_policy *policy,
struct cpufreq_freqs *freqs, int transition_failed);
#else /* CONFIG_CPU_FREQ */ #else /* CONFIG_CPU_FREQ */
static inline int cpufreq_register_notifier(struct notifier_block *nb, static inline int cpufreq_register_notifier(struct notifier_block *nb,
...@@ -439,6 +450,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, ...@@ -439,6 +450,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy,
unsigned int target_freq, unsigned int target_freq,
unsigned int relation, unsigned int relation,
unsigned int *index); unsigned int *index);
int cpufreq_frequency_table_get_index(struct cpufreq_policy *policy,
unsigned int freq);
void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy); void cpufreq_frequency_table_update_policy_cpu(struct cpufreq_policy *policy);
ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf); ssize_t cpufreq_show_cpus(const struct cpumask *mask, char *buf);
......
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