From 2209b0c964c7c402b07d0df6fcfbecf8ff53db30 Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 25 Nov 2013 18:01:18 -0800 Subject: cpufreq: cpufreq-cpu0: clk_round_rate() can return a zero upon error Treat both negative and zero return values from clk_round_rate() as errors. This is needed since subsequent patches will convert clk_round_rate()'s return value to be an unsigned type, rather than a signed type, since some clock sources can generate rates higher than (2^31)-1 Hz. Eventually, when calling clk_round_rate(), only a return value of zero will be considered a error. All other values will be considered valid rates. The comparison against values less than 0 is kept to preserve the correct behavior in the meantime. Signed-off-by: Paul Walmsley Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq-cpu0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index d4585ce..0faf756 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -44,7 +44,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) int ret; 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_exact = freq_Hz; -- cgit v1.1 From 189c9b8d925691cc58823ffdc3bf75dc8630ccdf Mon Sep 17 00:00:00 2001 From: Paul Walmsley Date: Mon, 25 Nov 2013 18:04:34 -0800 Subject: cpufreq: SPEAr: clk_round_rate() can return a zero upon error Treat both negative and zero return values from clk_round_rate() as errors. This is needed since subsequent patches will convert clk_round_rate()'s return value to be an unsigned type, rather than a signed type, since some clock sources can generate rates higher than (2^31)-1 Hz. Eventually, when calling clk_round_rate(), only a return value of zero will be considered a error. All other values will be considered valid rates. The comparison against values less than 0 is kept to preserve the correct behavior in the meantime. Signed-off-by: Paul Walmsley Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/spear-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index d02ccd1..45ea4c0 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -138,7 +138,7 @@ static int spear_cpufreq_target(struct cpufreq_policy *policy, } newfreq = clk_round_rate(srcclk, newfreq * mult); - if (newfreq < 0) { + if (newfreq <= 0) { pr_err("clk_round_rate failed for cpu src clock\n"); return newfreq; } -- cgit v1.1 From 007bea098b869945a462420a1f9d442ff169f722 Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Wed, 18 Dec 2013 10:32:39 -0800 Subject: intel_pstate: Add setting voltage value for baytrail P states. Baytrail requires setting P state and voltage pairs when adjusting the requested P state. Add function for retrieving the valid voltage values and modify *_set_pstate() functions to caluclate the appropriate voltage for the requested P state. Signed-off-by: Dirk Brandewie Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 58 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 54 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 5f1cbae..c84b280 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -35,6 +35,7 @@ #define SAMPLE_COUNT 3 #define BYT_RATIOS 0x66a +#define BYT_VIDS 0x66b #define FRAC_BITS 8 #define int_tofp(X) ((int64_t)(X) << FRAC_BITS) @@ -64,6 +65,12 @@ struct pstate_data { int turbo_pstate; }; +struct vid_data { + int32_t min; + int32_t max; + int32_t ratio; +}; + struct _pid { int setpoint; int32_t integral; @@ -82,6 +89,7 @@ struct cpudata { struct timer_list timer; struct pstate_data pstate; + struct vid_data vid; struct _pid pid; int min_pstate_count; @@ -106,7 +114,8 @@ struct pstate_funcs { int (*get_max)(void); int (*get_min)(void); int (*get_turbo)(void); - void (*set)(int pstate); + void (*set)(struct cpudata*, int pstate); + void (*get_vid)(struct cpudata *); }; struct cpu_defaults { @@ -358,6 +367,42 @@ static int byt_get_max_pstate(void) 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) { u64 value; @@ -384,7 +429,7 @@ static int core_get_turbo_pstate(void) return ret; } -static void core_set_pstate(int pstate) +static void core_set_pstate(struct cpudata *cpudata, int pstate) { u64 val; @@ -425,7 +470,8 @@ static struct cpu_defaults byt_params = { .get_max = byt_get_max_pstate, .get_min = byt_get_min_pstate, .get_turbo = byt_get_max_pstate, - .set = core_set_pstate, + .set = byt_set_pstate, + .get_vid = byt_get_vid, }, }; @@ -462,7 +508,7 @@ static void intel_pstate_set_pstate(struct cpudata *cpu, int 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) @@ -488,6 +534,9 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) cpu->pstate.max_pstate = pstate_funcs.get_max(); 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 * a module we will take care of it during normal operation @@ -776,6 +825,7 @@ static void copy_cpu_funcs(struct pstate_funcs *funcs) pstate_funcs.get_min = funcs->get_min; pstate_funcs.get_turbo = funcs->get_turbo; pstate_funcs.set = funcs->set; + pstate_funcs.get_vid = funcs->get_vid; } #if IS_ENABLED(CONFIG_ACPI) -- cgit v1.1 From 91a4cd4f3d8169d7398f9123683f64575927c682 Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Tue, 17 Dec 2013 09:42:07 -0800 Subject: intel_pstate: Remove periodic P state boost Remove the periodic P state boost. This code required for some corner case benchmark tests. The calculation of the required P state was incorrect/inaccurate and would not allow P state increase. This was fixed by a combination of commits: 2134ed4 cpufreq / intel_pstate: Change to scale off of max P-state d253d2a intel_pstate: Improve accuracy by not truncating until final result References: https://bugzilla.kernel.org/show_bug.cgi?id=64271 Reported-by: Doug Smythies Signed-off-by: Dirk Brandewie Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index c84b280..41e21bb 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -92,8 +92,6 @@ struct cpudata { struct vid_data vid; struct _pid pid; - int min_pstate_count; - u64 prev_aperf; u64 prev_mperf; int sample_ptr; @@ -617,15 +615,6 @@ static void intel_pstate_timer_func(unsigned long __data) intel_pstate_sample(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); } -- cgit v1.1 From b4f6b3a59460ad16e1b260d6b866e8328f690edb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Dec 2013 22:12:26 +0000 Subject: cpufreq: Make ARM big.LITTLE switcher depend on ARM The patch currently under review to enable ARM cpufreq drivers for ARM64 which is useful due to the large amount of shared IP between ARM and ARM64 SoCs. However the big.LITTLE switcher relies on an architecture interface to build which is not present on ARM64. Add a dependency until that is resolved. Signed-off-by: Mark Brown Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index ce52ed9..0a14110 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -4,7 +4,7 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK + depends on ARM && ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. -- cgit v1.1 From 109df086e002f253569de47e3a709403d3388a02 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Dec 2013 22:12:27 +0000 Subject: cpufreq: Select PM_OPP rather than depending on it PM_OPP is a helper library used by several of the existing cpufreq drivers. Some of the drivers select this symbol while others depend on it and rely on the architecture to enable it. Make this behaviour more consistent and obvious by having all the drivers select the symbol. This will also allow better build coverage of the affected drivers. Signed-off-by: Mark Brown Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig | 3 ++- drivers/cpufreq/Kconfig.arm | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 38093e2..386dbc9 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -181,7 +181,8 @@ config CPU_FREQ_GOV_CONSERVATIVE config GENERIC_CPUFREQ_CPU0 tristate "Generic CPU0 cpufreq driver" - depends on HAVE_CLK && REGULATOR && PM_OPP && OF + depends on HAVE_CLK && REGULATOR && OF + select PM_OPP help This adds a generic cpufreq driver for CPU0 frequency management. It supports both uniprocessor (UP) and symmetric multiprocessor (SMP) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0a14110..456ba5e 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -4,7 +4,8 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM && ARM_CPU_TOPOLOGY && PM_OPP && HAVE_CLK + depends on ARM && ARM_CPU_TOPOLOGY && HAVE_CLK + select PM_OPP help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. @@ -54,7 +55,8 @@ config ARM_EXYNOS5250_CPUFREQ config ARM_EXYNOS5440_CPUFREQ bool "SAMSUNG EXYNOS5440" depends on SOC_EXYNOS5440 - depends on HAVE_CLK && PM_OPP && OF + depends on HAVE_CLK && OF + select PM_OPP default y help This adds the CPUFreq driver for Samsung EXYNOS5440 -- cgit v1.1 From 998be8ee53f1197c37cbe792e2c0ac70b2f86ca8 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 3 Jan 2014 15:51:39 +0530 Subject: cpufreq: arm-big-little: Make driver dependent on CONFIG_BIG_LITTLE The arm_big_little cpufreq driver is only used by ARM bigLITTLE platforms and hence must depend on CONFIG_BIG_LITTLE. This was highlighted by Russell earlier when he reported this issue: drivers/built-in.o: In function `bL_cpufreq_set_rate': powercap_sys.c:(.text+0x5ed9a0): undefined reference to `bL_switch_request_cb' Reported-by: Russell King Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 456ba5e..3275e9c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -4,7 +4,7 @@ config ARM_BIG_LITTLE_CPUFREQ tristate "Generic ARM big LITTLE CPUfreq driver" - depends on ARM && ARM_CPU_TOPOLOGY && HAVE_CLK + depends on ARM && BIG_LITTLE && ARM_CPU_TOPOLOGY && HAVE_CLK select PM_OPP help This enables the Generic CPUfreq driver for ARM big.LITTLE platforms. -- cgit v1.1 From 2b2ec67fa390ff7355432035132d32270aca4c3c Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 Jan 2014 14:57:35 +0530 Subject: cpufreq: s3c2440: Remove hardware.h inclusion The contents of this header file are not referenced in the driver. Remove its inclusion. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/s3c2440-cpufreq.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/s3c2440-cpufreq.c b/drivers/cpufreq/s3c2440-cpufreq.c index 72b2cc8..717ef04 100644 --- a/drivers/cpufreq/s3c2440-cpufreq.c +++ b/drivers/cpufreq/s3c2440-cpufreq.c @@ -22,8 +22,6 @@ #include #include -#include - #include #include -- cgit v1.1 From 8f82b196866c4c3ec4cdc687667707d442d0c23b Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 Jan 2014 14:57:36 +0530 Subject: cpufreq: s3c2440: Staticize local variables Local variables used only in this file are made static. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/s3c2440-cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/s3c2440-cpufreq.c b/drivers/cpufreq/s3c2440-cpufreq.c index 717ef04..f84ed10 100644 --- a/drivers/cpufreq/s3c2440-cpufreq.c +++ b/drivers/cpufreq/s3c2440-cpufreq.c @@ -53,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 * 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 long hclk, fclk, armclk; @@ -240,7 +240,7 @@ static int s3c2440_cpufreq_calctable(struct s3c_cpufreq_config *cfg, return ret; } -struct s3c_cpufreq_info s3c2440_cpufreq_info = { +static struct s3c_cpufreq_info s3c2440_cpufreq_info = { .max = { .fclk = 400000000, .hclk = 133333333, -- cgit v1.1 From 87ae97f10c0d20cb5d9fc24c60eb1d1726d448c9 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Fri, 3 Jan 2014 14:57:37 +0530 Subject: cpufreq: s3c24xx: Staticize local variable Local variable used only in this file is made static. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/s3c24xx-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 4850882..35fa697 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -509,7 +509,7 @@ int __init s3c_cpufreq_setboard(struct s3c_cpufreq_board *board) return 0; } -int __init s3c_cpufreq_auto_io(void) +static int __init s3c_cpufreq_auto_io(void) { int ret; -- cgit v1.1 From 6f1e4efd882eccca10bac45b77e14bcb4979dc54 Mon Sep 17 00:00:00 2001 From: Jane Li Date: Fri, 3 Jan 2014 17:17:41 +0800 Subject: cpufreq: Fix timer/workqueue corruption by protecting reading governor_enabled When a CPU is hot removed we'll cancel all the delayed work items via gov_cancel_work(). Sometimes the delayed work function determines that it should adjust the delay for all other CPUs that the policy is managing. If this scenario occurs, the canceling CPU will cancel its own work but queue up the other CPUs works to run. Commit 3617f2 (cpufreq: Fix timer/workqueue corruption due to double queueing) has tried to fix this, but reading governor_enabled is not protected by cpufreq_governor_lock. Even though od_dbs_timer() checks governor_enabled before gov_queue_work(), this scenario may occur. For example: CPU0 CPU1 ---- ---- cpu_down() ... __cpufreq_remove_dev() od_dbs_timer() __cpufreq_governor() policy->governor_enabled policy->governor_enabled = false; cpufreq_governor_dbs() case CPUFREQ_GOV_STOP: gov_cancel_work(dbs_data, policy); cpu0 work is canceled timer is canceled cpu1 work is canceled gov_queue_work(*, *, true); cpu0 work queued cpu1 work queued cpu2 work queued ... cpu1 work is canceled cpu2 work is canceled ... At the end of the GOV_STOP case cpu0 still has a work queued to run although the code is expecting all of the works to be canceled. __cpufreq_remove_dev() will then proceed to re-initialize all the other CPUs works except for the CPU that is going down. The CPUFREQ_GOV_START case in cpufreq_governor_dbs() will trample over the queued work and debugobjects will spit out a warning: WARNING: at lib/debugobjects.c:260 debug_print_object+0x94/0xbc() ODEBUG: init active (active state 0) object type: timer_list hint: delayed_work_timer_fn+0x0/0x14 Modules linked in: CPU: 1 PID: 1205 Comm: sh Tainted: G W 3.10.0 #200 [] (unwind_backtrace+0x0/0xf8) from [] (show_stack+0x10/0x14) [] (show_stack+0x10/0x14) from [] (warn_slowpath_common+0x4c/0x68) [] (warn_slowpath_common+0x4c/0x68) from [] (warn_slowpath_fmt+0x30/0x40) [] (warn_slowpath_fmt+0x30/0x40) from [] (debug_print_object+0x94/0xbc) [] (debug_print_object+0x94/0xbc) from [] (__debug_object_init+0xc8/0x3c0) [] (__debug_object_init+0xc8/0x3c0) from [] (init_timer_key+0x20/0x104) [] (init_timer_key+0x20/0x104) from [] (cpufreq_governor_dbs+0x1dc/0x68c) [] (cpufreq_governor_dbs+0x1dc/0x68c) from [] (__cpufreq_governor+0x80/0x1b0) [] (__cpufreq_governor+0x80/0x1b0) from [] (__cpufreq_remove_dev.isra.12+0x22c/0x380) [] (__cpufreq_remove_dev.isra.12+0x22c/0x380) from [] (cpufreq_cpu_callback+0x48/0x5c) [] (cpufreq_cpu_callback+0x48/0x5c) from [] (notifier_call_chain+0x44/0x84) [] (notifier_call_chain+0x44/0x84) from [] (__cpu_notify+0x2c/0x48) [] (__cpu_notify+0x2c/0x48) from [] (_cpu_down+0x80/0x258) [] (_cpu_down+0x80/0x258) from [] (cpu_down+0x28/0x3c) [] (cpu_down+0x28/0x3c) from [] (store_online+0x30/0x74) [] (store_online+0x30/0x74) from [] (dev_attr_store+0x18/0x24) [] (dev_attr_store+0x18/0x24) from [] (sysfs_write_file+0x100/0x180) [] (sysfs_write_file+0x100/0x180) from [] (vfs_write+0xbc/0x184) [] (vfs_write+0xbc/0x184) from [] (SyS_write+0x40/0x68) [] (SyS_write+0x40/0x68) from [] (ret_fast_syscall+0x0/0x48) In gov_queue_work(), lock cpufreq_governor_lock before gov_queue_work, and unlock it after __gov_queue_work(). In this way, governor_enabled is guaranteed not changed in gov_queue_work(). Signed-off-by: Jane Li Acked-by: Viresh Kumar Reviewed-by: Dmitry Torokhov Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 2 +- drivers/cpufreq/cpufreq_governor.c | 6 +++++- drivers/cpufreq/cpufreq_governor.h | 2 ++ 3 files changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 8d19f7c..a42e6ae 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -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_fallback); static DEFINE_RWLOCK(cpufreq_driver_lock); -static DEFINE_MUTEX(cpufreq_governor_lock); +DEFINE_MUTEX(cpufreq_governor_lock); static LIST_HEAD(cpufreq_policy_list); #ifdef CONFIG_HOTPLUG_CPU diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index e6be635..ba43991 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -119,8 +119,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, { int i; + mutex_lock(&cpufreq_governor_lock); if (!policy->governor_enabled) - return; + goto out_unlock; if (!all_cpus) { /* @@ -135,6 +136,9 @@ void gov_queue_work(struct dbs_data *dbs_data, struct cpufreq_policy *policy, for_each_cpu(i, policy->cpus) __gov_queue_work(i, dbs_data, delay); } + +out_unlock: + mutex_unlock(&cpufreq_governor_lock); } EXPORT_SYMBOL_GPL(gov_queue_work); diff --git a/drivers/cpufreq/cpufreq_governor.h b/drivers/cpufreq/cpufreq_governor.h index b5f2b86..bfb9ae1 100644 --- a/drivers/cpufreq/cpufreq_governor.h +++ b/drivers/cpufreq/cpufreq_governor.h @@ -257,6 +257,8 @@ static ssize_t show_sampling_rate_min_gov_pol \ 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); bool need_load_eval(struct cpu_dbs_common_info *cdbs, unsigned int sampling_rate); -- cgit v1.1 From 26ab1c62b6e1ed4720d533320d646866027ade09 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Tue, 24 Dec 2013 15:35:24 +0530 Subject: cpufreq: exynos5250: Set APLL rate using CCF API Use common clock framework (CCF) APIs to set the clock rates instead of direct register manipulation. This now updates the sysfs entry (cpuinfo_cur_freq) correctly which did not reflect the correct value until now. While at it clean up the PLL s-div parameter setting as it is handled by the PLL driver. Signed-off-by: Sachin Kamat Acked-by: Viresh Kumar Reviewed-by: Lukasz Majewski Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/exynos5250-cpufreq.c | 74 +++++------------------------------- 1 file changed, 10 insertions(+), 64 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index 8feda86..86fb1a1 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -102,12 +102,12 @@ static void set_clkdiv(unsigned int div_index) cpu_relax(); } -static void set_apll(unsigned int new_index, - unsigned int old_index) +static void set_apll(unsigned int 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); do { @@ -116,24 +116,9 @@ static void set_apll(unsigned int new_index, tmp &= 0x7; } while (tmp != 0x2); - /* 2. Set APLL Lock time */ - pdiv = ((apll_freq_5250[new_index].mps >> 8) & 0x3f); - - __raw_writel((pdiv * 250), EXYNOS5_APLL_LOCK); + clk_set_rate(mout_apll, freq * 1000); - /* 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 */ - do { - cpu_relax(); - tmp = __raw_readl(EXYNOS5_APLL_CON0); - } while (!(tmp & (0x1 << 29))); - - /* 5. MUX_CORE_SEL = APLL */ + /* MUX_CORE_SEL = APLL */ clk_set_parent(moutcore, mout_apll); do { @@ -141,55 +126,17 @@ static void set_apll(unsigned int new_index, tmp = __raw_readl(EXYNOS5_CLKMUX_STATCPU); tmp &= (0x7 << 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, unsigned int new_index) { - unsigned int tmp; - if (old_index > new_index) { - if (!exynos5250_pms_change(old_index, new_index)) { - /* 1. Change the system clock divider values */ - set_clkdiv(new_index); - /* 2. 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); - - } 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); - } + set_clkdiv(new_index); + set_apll(new_index); } else if (old_index < new_index) { - if (!exynos5250_pms_change(old_index, 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); - } 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); - } + set_apll(new_index); + set_clkdiv(new_index); } } @@ -222,7 +169,6 @@ int exynos5250_cpufreq_init(struct exynos_dvfs_info *info) info->volt_table = exynos5250_volt_table; info->freq_table = exynos5250_freq_table; info->set_freq = exynos5250_set_frequency; - info->need_apll_change = exynos5250_pms_change; return 0; -- cgit v1.1 From f7ba3b41e27129575201f0f9656e83fb67e86c3b Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 2 Dec 2013 11:04:12 +0530 Subject: cpufreq: Introduce cpufreq_notify_post_transition() This introduces a new routine cpufreq_notify_post_transition() which can be used to send POSTCHANGE notification for new freq with or without both {PRE|POST}CHANGE notifications for last freq. This is useful at multiple places, especially for sending transition failure notifications. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a42e6ae..a123a73 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -320,6 +320,20 @@ void cpufreq_notify_transition(struct cpufreq_policy *policy, } 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 * -- cgit v1.1 From ab1b1c4e8223f9ee66aa93aaf64c36e77cadffac Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Mon, 2 Dec 2013 11:04:13 +0530 Subject: cpufreq: send new set of notification for transition failures In the current code, if we fail during a frequency transition, we simply send the POSTCHANGE notification with the old frequency. This isn't enough. One of the core users of these notifications is the code responsible for keeping loops_per_jiffy aligned with frequency changes. And mostly it is written as: if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) || (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) { update-loops-per-jiffy... } So, suppose we are changing to a higher frequency and failed during transition, then following will happen: - CPUFREQ_PRECHANGE notification with freq-new > freq-old - CPUFREQ_POSTCHANGE notification with freq-new == freq-old The first one will update loops_per_jiffy and second one will do nothing. Even if we send the 2nd notification by exchanging values of freq-new and old, some users of these notifications might get unstable. This can be fixed by simply calling cpufreq_notify_post_transition() with error code and this routine will take care of sending notifications in the correct order. Signed-off-by: Viresh Kumar [rjw: Folded 3 patches into one, rebased unicore2 changes] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 13 ++----------- drivers/cpufreq/pcc-cpufreq.c | 18 +++++++----------- drivers/cpufreq/powernow-k8.c | 7 +------ drivers/cpufreq/unicore2-cpufreq.c | 16 +++++++--------- 4 files changed, 17 insertions(+), 37 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index a123a73..d533c20 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1739,17 +1739,8 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, pr_err("%s: Failed to change cpu frequency: %d\n", __func__, retval); - if (notify) { - /* - * Notify with old freq in case we failed to change - * frequency - */ - if (retval) - freqs.new = freqs.old; - - cpufreq_notify_transition(policy, &freqs, - CPUFREQ_POSTCHANGE); - } + if (notify) + cpufreq_notify_post_transition(policy, &freqs, retval); } out: diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index e2b4f40..1c0f1067 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -213,6 +213,7 @@ static int pcc_cpufreq_target(struct cpufreq_policy *policy, cpu, target_freq, (pcch_virt_addr + pcc_cpu_data->input_offset)); + freqs.old = policy->cur; freqs.new = target_freq; cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); @@ -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); 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) { pr_debug("target: FAILED for cpu %d, with status: 0x%x\n", 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); - spin_unlock(&pcc_lock); 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) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 0023c7d..e10b646 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -964,14 +964,9 @@ static int transition_frequency_fidvid(struct powernow_k8_data *data, cpufreq_cpu_put(policy); cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - res = transition_fid_vid(data, fid, vid); - if (res) - freqs.new = freqs.old; - else - freqs.new = find_khz_freq_from_fid(data->currfid); + cpufreq_notify_post_transition(policy, &freqs, res); - cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); return res; } diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 653ae29..86f6cfe 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c @@ -46,20 +46,18 @@ static int ucv2_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - unsigned int cur = ucv2_getspeed(0); struct cpufreq_freqs freqs; struct clk *mclk = clk_get(NULL, "MAIN_CLK"); + int ret; - cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - - if (!clk_set_rate(mclk, target_freq * 1000)) { - freqs.old = cur; - freqs.new = target_freq; - } + freqs.old = policy->cur; + 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) -- cgit v1.1 From b4573d1d657aae28bedf3a9a1f5367e09c80d1d6 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Thu, 19 Dec 2013 09:16:47 -0500 Subject: cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed on i.MX6Q, cpu freq change need to follow below flows: 1. each setpoint has different VDDARM, VDDSOC/PU voltage, get the setpoint table from dts; 2. when cpu freq is scaling up, need to increase VDDSOC/PU voltage before VDDARM, if VDDPU is off, no need to change it; 3. when cpu freq is scaling down, need to decrease VDDARM voltage before VDDSOC/PU, if VDDPU is off, no need to change it; normally dts will pass vddsoc/pu freq/volt info to kernel, if not, will use fixed value for vddsoc/pu voltage setting. Signed-off-by: Anson Huang Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 106 +++++++++++++++++++++++++++++----------- 1 file changed, 77 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 4b3f18e..c29198f 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -35,6 +35,9 @@ static struct device *cpu_dev; static struct cpufreq_frequency_table *freq_table; static unsigned int transition_latency; +static u32 *imx6_soc_volt; +static u32 soc_opp_count; + static unsigned int imx6q_get_speed(unsigned int cpu) { return clk_get_rate(arm_clk) / 1000; @@ -69,23 +72,22 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) /* scaling up? scale voltage before frequency */ 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); if (ret) { dev_err(cpu_dev, "failed to scale vddarm up: %d\n", 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) "failed to scale vddarm down: %d\n", ret); ret = 0; } - - if (old_freq == FREQ_1P2_GHZ / 1000) { - regulator_set_voltage_tol(pu_reg, - PU_SOC_VOLTAGE_NORMAL, 0); - regulator_set_voltage_tol(soc_reg, - PU_SOC_VOLTAGE_NORMAL, 0); + ret = regulator_set_voltage_tol(soc_reg, imx6_soc_volt[index], 0); + if (ret) { + dev_warn(cpu_dev, "failed to scale vddsoc down: %d\n", ret); + ret = 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; } } @@ -153,6 +158,9 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) struct dev_pm_opp *opp; unsigned long min_volt, max_volt; int num, ret; + const struct property *prop; + const __be32 *val; + u32 nr, i, j; cpu_dev = get_cpu_device(0); if (!cpu_dev) { @@ -201,10 +209,62 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) 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 . + */ + 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)) 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 * freq_table initialised from OPP is therefore sorted in the * same order. @@ -221,18 +281,6 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) if (ret > 0) 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); if (ret) { dev_err(cpu_dev, "failed register driver: %d\n", ret); -- cgit v1.1 From 1d0eaae9b55e1ff2cfa2217b73af2027e07ca480 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 20 Dec 2013 10:12:16 +0800 Subject: cpufreq: imx6q-cpufreq driver is reused on i.MX6 series SoCs The imx6q-cpufreq driver nowadays is not only running on imx6q but also other i.MX6 series SoCs like imx6dl and imx6sl. Update Kconfig prompt and help text to make it clear to users. Signed-off-by: Shawn Guo Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig.arm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 3275e9c..0468ad1 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -81,11 +81,11 @@ config ARM_HIGHBANK_CPUFREQ If in doubt, say N. config ARM_IMX6Q_CPUFREQ - tristate "Freescale i.MX6Q cpufreq support" - depends on SOC_IMX6Q + tristate "Freescale i.MX6 cpufreq support" + depends on ARCH_MXC depends on REGULATOR_ANATOP 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. -- cgit v1.1 From 20b7cbe2981013cbe449cc642cefc101e23d0f8e Mon Sep 17 00:00:00 2001 From: John Tobias Date: Thu, 19 Dec 2013 22:56:28 -0800 Subject: cpufreq: imx6q: add of_init_opp_table Add a routine check to see if the platform supplied the OPP table. Incase there's no OPP table exist, it will try to initialise it. It's been tested on iMX6SL board where the platform doesn't have an OPP table. Signed-off-by: John Tobias Acked-by: Shawn Guo Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/imx6q-cpufreq.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index c29198f..564a265 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -195,12 +195,25 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) 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 = num; - dev_err(cpu_dev, "no OPP table is found: %d\n", ret); - goto put_node; + 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); + if (num < 0) { + ret = num; + dev_err(cpu_dev, "no OPP table is found: %d\n", ret); + goto put_node; + } } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); -- cgit v1.1 From ae6b427132ba39d023e332e7d920e9931ff05313 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 3 Dec 2013 11:20:45 +0530 Subject: cpufreq: Mark ARM drivers with CPUFREQ_NEED_INITIAL_FREQ_CHECK flag 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 frequency table. On some systems we can't really say what frequency we're running at the moment and so for these we shouldn't check if we are running at a frequency present in frequency table. And so we really can't force this for all the cpufreq drivers. Hence we are created another flag here: CPUFREQ_NEED_INITIAL_FREQ_CHECK that will be marked by platforms which want to go for this check at boot time. Initially this is done for all ARM platforms but others may follow if required. Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/arm_big_little.c | 3 ++- drivers/cpufreq/davinci-cpufreq.c | 2 +- drivers/cpufreq/dbx500-cpufreq.c | 3 ++- drivers/cpufreq/exynos-cpufreq.c | 2 +- drivers/cpufreq/exynos5440-cpufreq.c | 3 ++- drivers/cpufreq/imx6q-cpufreq.c | 1 + drivers/cpufreq/integrator-cpufreq.c | 1 + drivers/cpufreq/kirkwood-cpufreq.c | 1 + drivers/cpufreq/omap-cpufreq.c | 2 +- drivers/cpufreq/pxa2xx-cpufreq.c | 1 + drivers/cpufreq/pxa3xx-cpufreq.c | 1 + drivers/cpufreq/s3c2416-cpufreq.c | 2 +- drivers/cpufreq/s3c24xx-cpufreq.c | 2 +- drivers/cpufreq/s3c64xx-cpufreq.c | 2 +- drivers/cpufreq/s5pv210-cpufreq.c | 2 +- drivers/cpufreq/sa1100-cpufreq.c | 2 +- drivers/cpufreq/sa1110-cpufreq.c | 2 +- drivers/cpufreq/spear-cpufreq.c | 2 +- drivers/cpufreq/tegra-cpufreq.c | 1 + 19 files changed, 22 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/arm_big_little.c b/drivers/cpufreq/arm_big_little.c index 5519933..72f87e9 100644 --- a/drivers/cpufreq/arm_big_little.c +++ b/drivers/cpufreq/arm_big_little.c @@ -488,7 +488,8 @@ static int bL_cpufreq_exit(struct cpufreq_policy *policy) static struct cpufreq_driver bL_cpufreq_driver = { .name = "arm-big-little", .flags = CPUFREQ_STICKY | - CPUFREQ_HAVE_GOVERNOR_PER_POLICY, + CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = bL_cpufreq_set_target, .get = bL_cpufreq_get_rate, diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index 5e8a854..04f3390 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -126,7 +126,7 @@ static int davinci_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver davinci_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = davinci_verify_speed, .target_index = davinci_target, .get = davinci_getspeed, diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c index 0e67ab9..21d9898 100644 --- a/drivers/cpufreq/dbx500-cpufreq.c +++ b/drivers/cpufreq/dbx500-cpufreq.c @@ -48,7 +48,8 @@ static int dbx500_cpufreq_init(struct cpufreq_policy *policy) } 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, .target_index = dbx500_cpufreq_target, .get = dbx500_cpufreq_getspeed, diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index f3c2287..85e67dc 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -218,7 +218,7 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver exynos_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = exynos_target, .get = exynos_getspeed, diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index 76bef8b..ffe6fae 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -312,7 +312,8 @@ static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) } 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, .target_index = exynos_target, .get = exynos_getspeed, diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 564a265..2938257 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -143,6 +143,7 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver imx6q_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = imx6q_set_target, .get = imx6q_get_speed, diff --git a/drivers/cpufreq/integrator-cpufreq.c b/drivers/cpufreq/integrator-cpufreq.c index 7d8ab000d..0e27844 100644 --- a/drivers/cpufreq/integrator-cpufreq.c +++ b/drivers/cpufreq/integrator-cpufreq.c @@ -190,6 +190,7 @@ static int integrator_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver integrator_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = integrator_verify_policy, .target = integrator_set_target, .get = integrator_get, diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index 0767a4e..eb7abe3 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -97,6 +97,7 @@ static int kirkwood_cpufreq_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver kirkwood_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .get = kirkwood_cpufreq_get_cpu_frequency, .verify = cpufreq_generic_frequency_table_verify, .target_index = kirkwood_cpufreq_target, diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index a0acd0b..5de1e5f 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -162,7 +162,7 @@ static int omap_cpu_exit(struct cpufreq_policy *policy) } static struct cpufreq_driver omap_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = omap_target, .get = omap_getspeed, diff --git a/drivers/cpufreq/pxa2xx-cpufreq.c b/drivers/cpufreq/pxa2xx-cpufreq.c index 0a0f436..a9195a8 100644 --- a/drivers/cpufreq/pxa2xx-cpufreq.c +++ b/drivers/cpufreq/pxa2xx-cpufreq.c @@ -423,6 +423,7 @@ static int pxa_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver pxa_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = pxa_set_target, .init = pxa_cpufreq_init, diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 9384004..3785687 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -201,6 +201,7 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy) } static struct cpufreq_driver pxa3xx_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = pxa3xx_cpufreq_set, .init = pxa3xx_cpufreq_init, diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index 8d904a0..826b8be 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -481,7 +481,7 @@ err_hclk: } static struct cpufreq_driver s3c2416_cpufreq_driver = { - .flags = 0, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s3c2416_cpufreq_set_target, .get = s3c2416_cpufreq_get_speed, diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 35fa697..6a1bf96 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -448,7 +448,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) #endif static struct cpufreq_driver s3c24xx_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .target = s3c_cpufreq_target, .get = s3c_cpufreq_get, .init = s3c_cpufreq_init, diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 67e302e..8435f45 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -226,7 +226,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) } static struct cpufreq_driver s3c64xx_cpufreq_driver = { - .flags = 0, + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s3c64xx_cpufreq_set_target, .get = s3c64xx_cpufreq_get_speed, diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index e3973da..ccd548c 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -560,7 +560,7 @@ static int s5pv210_cpufreq_reboot_notifier_event(struct notifier_block *this, } static struct cpufreq_driver s5pv210_driver = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s5pv210_target, .get = s5pv210_getspeed, diff --git a/drivers/cpufreq/sa1100-cpufreq.c b/drivers/cpufreq/sa1100-cpufreq.c index 623da74..728eab7 100644 --- a/drivers/cpufreq/sa1100-cpufreq.c +++ b/drivers/cpufreq/sa1100-cpufreq.c @@ -201,7 +201,7 @@ static int __init sa1100_cpu_init(struct cpufreq_policy *policy) } static struct cpufreq_driver sa1100_driver __refdata = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1100_target, .get = sa11x0_getspeed, diff --git a/drivers/cpufreq/sa1110-cpufreq.c b/drivers/cpufreq/sa1110-cpufreq.c index 2c2b2e6..5463767 100644 --- a/drivers/cpufreq/sa1110-cpufreq.c +++ b/drivers/cpufreq/sa1110-cpufreq.c @@ -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 * it with cpufreq_register_driver() */ static struct cpufreq_driver sa1110_driver __refdata = { - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = sa1110_target, .get = sa11x0_getspeed, diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 45ea4c0..c7525fe 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -162,7 +162,7 @@ static int spear_cpufreq_init(struct cpufreq_policy *policy) static struct cpufreq_driver spear_cpufreq_driver = { .name = "cpufreq-spear", - .flags = CPUFREQ_STICKY, + .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = spear_cpufreq_target, .get = spear_cpufreq_get, diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c index b7309c3..01b5578 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra-cpufreq.c @@ -214,6 +214,7 @@ static int tegra_cpu_exit(struct cpufreq_policy *policy) } static struct cpufreq_driver tegra_cpufreq_driver = { + .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = tegra_target, .get = tegra_getspeed, -- cgit v1.1 From d3916691c90dfc9f08328d5cef8181e9ea508c55 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 3 Dec 2013 11:20:46 +0530 Subject: cpufreq: Make sure CPU is running on a freq from freq-table 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 affect 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). In case current frequency doesn't match any frequency from freq-table, we throw warnings to user, so that user can get this fixed in their bootloaders or freq-tables. Reported-by: Carlos Hernandez Reported-and-tested-by: Nishanth Menon Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 40 ++++++++++++++++++++++++++++++++++++++++ drivers/cpufreq/freq_table.c | 22 ++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d533c20..3509ca0 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1073,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 */ cpumask_or(policy->related_cpus, policy->related_cpus, policy->cpus); diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 3458d27..a8ac042 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -178,7 +178,29 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, } 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); + /** * show_available_freqs - show available frequencies for the specified CPU */ -- cgit v1.1 From d568b6f71df1c8bd2fc2b051cf03f613a65ffad6 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Thu, 28 Nov 2013 13:42:42 +0100 Subject: cpufreq: exynos: Convert exynos-cpufreq to platform driver To make the driver multiplatform-friendly, unconditional initialization in an initcall is replaced with a platform driver probed only if respective platform device is registered. Tested at: Exynos4210 (TRATS) and Exynos4412 (TRATS2) Signed-off-by: Lukasz Majewski Signed-off-by: Tomasz Figa Signed-off-by: Kyungmin Park Reviewed-by: Sachin Kamat Tested-by: Sachin Kamat Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/exynos-cpufreq.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 85e67dc..f7c322c 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -232,7 +233,7 @@ static struct cpufreq_driver exynos_driver = { #endif }; -static int __init exynos_cpufreq_init(void) +static int exynos_cpufreq_probe(struct platform_device *pdev) { int ret = -EINVAL; @@ -281,4 +282,12 @@ err_vdd_arm: kfree(exynos_info); 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); -- cgit v1.1 From e20e1d0ac02308e2211306fc67abcd0b2668fb8b Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 11 Dec 2013 19:38:32 -0500 Subject: powernow-k6: disable cache when changing frequency I found out that a system with k6-3+ processor is unstable during network server load. The system locks up or the network card stops receiving. The reason for the instability is the CPU frequency scaling. During frequency transition the processor is in "EPM Stop Grant" state. The documentation says that the processor doesn't respond to inquiry requests in this state. Consequently, coherency of processor caches and bus master devices is not maintained, causing the system instability. This patch flushes the cache during frequency transition. It fixes the instability. Other minor changes: * u64 invalue changed to unsigned long because the variable is 32-bit * move the logic to set the multiplier to a separate function powernow_k6_set_cpu_multiplier * preserve lower 5 bits of the powernow port instead of 4 (the voltage field has 5 bits) * mask interrupts when reading the multiplier, so that the port is not open during other activity (running other kernel code with the port open shouldn't cause any misbehavior, but we should better be safe and keep the port closed) This patch should be backported to all stable kernels. If it doesn't apply cleanly, change it, or ask me to change it. Signed-off-by: Mikulas Patocka Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernow-k6.c | 56 ++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index 643e795..1332b72 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -44,23 +44,58 @@ static struct cpufreq_frequency_table clock_ratio[] = { /** * powernow_k6_get_cpu_multiplier - returns the current FSB multiplier * - * Returns the current setting of the frequency multiplier. Core clock + * Returns the current setting of the frequency multiplier. Core clock * speed is frequency of the Front-Side Bus multiplied with this value. */ static int powernow_k6_get_cpu_multiplier(void) { - u64 invalue = 0; + unsigned long invalue = 0; u32 msrval; + local_irq_disable(); + msrval = POWERNOW_IOPORT + 0x1; wrmsr(MSR_K6_EPMR, msrval, 0); /* enable the PowerNow port */ invalue = inl(POWERNOW_IOPORT + 0x8); msrval = POWERNOW_IOPORT + 0x0; wrmsr(MSR_K6_EPMR, msrval, 0); /* disable it again */ + local_irq_enable(); + return clock_ratio[(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) | (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 @@ -71,8 +106,6 @@ static int powernow_k6_get_cpu_multiplier(void) static int powernow_k6_target(struct cpufreq_policy *policy, unsigned int best_i) { - unsigned long outvalue = 0, invalue = 0; - unsigned long msrval; struct cpufreq_freqs freqs; if (clock_ratio[best_i].driver_data > max_multiplier) { @@ -85,18 +118,7 @@ static int powernow_k6_target(struct cpufreq_policy *policy, cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - /* we now need to transform best_i to the BVC format, see AMD#23446 */ - - 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 */ + powernow_k6_set_cpu_multiplier(best_i); cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); @@ -125,7 +147,7 @@ static int powernow_k6_cpu_init(struct cpufreq_policy *policy) } /* cpuinfo and default policy values */ - policy->cpuinfo.transition_latency = 200000; + policy->cpuinfo.transition_latency = 500000; return cpufreq_table_validate_and_show(policy, clock_ratio); } -- cgit v1.1 From d82b922a4acc1781d368aceac2f9da43b038cab2 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 11 Dec 2013 19:38:53 -0500 Subject: powernow-k6: correctly initialize default parameters The powernow-k6 driver used to read the initial multiplier from the powernow register. However, there is a problem with this: * If there was a frequency transition before, the multiplier read from the register corresponds to the current multiplier. * If there was no frequency transition since reset, the field in the register always reads as zero, regardless of the current multiplier that is set using switches on the mainboard and that the CPU is running at. The zero value corresponds to multiplier 4.5, so as a consequence, the powernow-k6 driver always assumes multiplier 4.5. For example, if we have 550MHz CPU with bus frequency 100MHz and multiplier 5.5, the powernow-k6 driver thinks that the multiplier is 4.5 and bus frequency is 122MHz. The powernow-k6 driver then sets the multiplier to 4.5, underclocking the CPU to 450MHz, but reports the current frequency as 550MHz. There is no reliable way how to read the initial multiplier. I modified the driver so that it contains a table of known frequencies (based on parameters of existing CPUs and some common overclocking schemes) and sets the multiplier according to the frequency. If the frequency is unknown (because of unusual overclocking or underclocking), the user must supply the bus speed and maximum multiplier as module parameters. This patch should be backported to all stable kernels. If it doesn't apply cleanly, change it, or ask me to change it. Signed-off-by: Mikulas Patocka Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernow-k6.c | 76 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index 1332b72..16359ab 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -26,6 +26,14 @@ static unsigned int busfreq; /* FSB, in 10 kHz */ 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 */ static struct cpufreq_frequency_table clock_ratio[] = { @@ -40,6 +48,27 @@ static struct cpufreq_frequency_table clock_ratio[] = { {0, CPUFREQ_TABLE_END} }; +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 @@ -125,17 +154,56 @@ static int powernow_k6_target(struct cpufreq_policy *policy, return 0; } - static int powernow_k6_cpu_init(struct cpufreq_policy *policy) { unsigned int i, f; + unsigned khz; if (policy->cpu != 0) return -ENODEV; - /* get frequencies */ - max_multiplier = powernow_k6_get_cpu_multiplier(); - busfreq = cpu_khz / max_multiplier; + max_multiplier = 0; + khz = cpu_khz; + 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 */ for (i = 0; (clock_ratio[i].frequency != CPUFREQ_TABLE_END); i++) { -- cgit v1.1 From 22c73795b101597051924556dce019385a1e2fa0 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 11 Dec 2013 19:39:19 -0500 Subject: powernow-k6: reorder frequencies This patch reorders reported frequencies from the highest to the lowest, just like in other frequency drivers. Signed-off-by: Mikulas Patocka Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/powernow-k6.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index 16359ab..b9a444e 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -37,17 +37,20 @@ MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz"); /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */ 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}, + {45, /* 000 -> 4.5x */ 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}, + {30, /* 101 -> 3.0x */ 0}, + {20, /* 100 -> 2.0x */ 0}, {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; @@ -91,7 +94,7 @@ static int powernow_k6_get_cpu_multiplier(void) local_irq_enable(); - return clock_ratio[(invalue >> 5)&7].driver_data; + return clock_ratio[register_to_index[(invalue >> 5)&7]].driver_data; } static void powernow_k6_set_cpu_multiplier(unsigned int best_i) @@ -111,7 +114,7 @@ static void powernow_k6_set_cpu_multiplier(unsigned int best_i) write_cr0(cr0 | X86_CR0_CD); wbinvd(); - outvalue = (1<<12) | (1<<10) | (1<<9) | (best_i<<5); + 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 */ -- cgit v1.1 From 5650cef2ea4c660a3d38bb1ac44e9169d310f366 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Mon, 6 Jan 2014 21:30:12 +0100 Subject: cpufreq: speedstep: remove unused speedstep_get_state The only caller of speedstep_get_state() was removed in commit d4019f0a92ab ("cpufreq: move freq change notifications to cpufreq core"). So building speedstep-smi.o now triggers a GCC warning: drivers/cpufreq/speedstep-smi.c:148:12: warning: 'speedstep_get_state' defined but not used [-Wunused-function] Remove this unused function. Signed-off-by: Paul Bolle Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/speedstep-smi.c | 32 -------------------------------- 1 file changed, 32 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index 0f5326d..998c17b 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -141,38 +141,6 @@ static int speedstep_smi_get_freqs(unsigned int *low, unsigned int *high) } /** - * speedstep_get_state - set the SpeedStep state - * @state: processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) - * - */ -static int speedstep_get_state(void) -{ - u32 function = GET_SPEEDSTEP_STATE; - u32 result, state, edi, command, dummy; - - command = (smi_sig & 0xffffff00) | (smi_cmd & 0xff); - - pr_debug("trying to determine current setting with command %x " - "at port %x\n", command, smi_port); - - __asm__ __volatile__( - "push %%ebp\n" - "out %%al, (%%dx)\n" - "pop %%ebp\n" - : "=a" (result), - "=b" (state), "=D" (edi), - "=c" (dummy), "=d" (dummy), "=S" (dummy) - : "a" (command), "b" (function), "c" (0), - "d" (smi_port), "S" (0), "D" (0) - ); - - pr_debug("state is %x, result is %x\n", state, result); - - return state & 1; -} - - -/** * speedstep_set_state - set the SpeedStep state * @state: new processor frequency state (SPEEDSTEP_LOW or SPEEDSTEP_HIGH) * -- cgit v1.1 From fcd7af917abba798cd954419030142e95139359f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 7 Jan 2014 07:10:10 +0530 Subject: cpufreq: stats: handle cpufreq_unregister_driver() and suspend/resume properly There are several problems with cpufreq stats in the way it handles cpufreq_unregister_driver() and suspend/resume.. - We must not lose data collected so far when suspend/resume happens and so stats directories must not be removed/allocated during these operations, which is done currently. - cpufreq_stat has registered notifiers with both cpufreq and hotplug. It adds sysfs stats directory with a cpufreq notifier: CPUFREQ_NOTIFY and removes this directory with a notifier from hotplug core. In case cpufreq_unregister_driver() is called (on rmmod cpufreq driver), stats directories per cpu aren't removed as CPUs are still online. The only call cpufreq_stats gets is cpufreq_stats_update_policy_cpu() for all CPUs except the last of each policy. And pointer to stat information is stored in the entry for last CPU in the per-cpu cpufreq_stats_table. But policy structure would be freed inside cpufreq core and so that will result in memory leak inside cpufreq stats (as we are never freeing memory for stats). Now if we again insert the module cpufreq_register_driver() will be called and we will again allocate stats data and put it on for first CPU of every policy. In case we only have a single CPU per policy, we will return with a error from cpufreq_stats_create_table() due to this code: if (per_cpu(cpufreq_stats_table, cpu)) return -EBUSY; And so probably cpufreq stats directory would not show up anymore (as it was added inside last policies->kobj which doesn't exist anymore). I haven't tested it, though. Also the values in stats files wouldn't be refreshed as we are using the earlier stats structure. - CPUFREQ_NOTIFY is called from cpufreq_set_policy() which is called for scenarios where we don't really want cpufreq_stat_notifier_policy() to get called. For example whenever we are changing anything related to a policy: min/max/current freq, etc. cpufreq_set_policy() is called and so cpufreq stats is notified. Where we don't do any useful stuff other than simply returning with -EBUSY from cpufreq_stats_create_table(). And so this isn't the right notifier that cpufreq stats.. Due to all above reasons this patch does following changes: - Add new notifiers CPUFREQ_CREATE_POLICY and CPUFREQ_REMOVE_POLICY, which are only called when policy is created/destroyed. They aren't called for suspend/resume paths.. - Use these notifiers in cpufreq_stat_notifier_policy() to create/destory stats sysfs entries. And so cpufreq_unregister_driver() or suspend/resume shouldn't be a problem for cpufreq_stats. - Return early from cpufreq_stat_cpu_callback() for suspend/resume sequence, so that we don't free stats structure. Acked-by: Nicolas Pitre Tested-by: Nicolas Pitre Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 5 +++++ drivers/cpufreq/cpufreq_stats.c | 24 +++++++++++++++++------- 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 3509ca0..1afbe52 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -943,6 +943,9 @@ static void cpufreq_policy_put_kobj(struct cpufreq_policy *policy) struct kobject *kobj; struct completion *cmp; + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_REMOVE_POLICY, policy); + down_read(&policy->rwsem); kobj = &policy->kobj; cmp = &policy->kobj_unregister; @@ -1148,6 +1151,8 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, ret = cpufreq_add_dev_interface(policy, dev); if (ret) goto err_out_unregister; + blocking_notifier_call_chain(&cpufreq_policy_notifier_list, + CPUFREQ_CREATE_POLICY, policy); } write_lock_irqsave(&cpufreq_driver_lock, flags); diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 4cf0d28..0f71562 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -277,7 +277,7 @@ static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) static int cpufreq_stat_notifier_policy(struct notifier_block *nb, unsigned long val, void *data) { - int ret; + int ret = 0; struct cpufreq_policy *policy = data; struct cpufreq_frequency_table *table; unsigned int cpu = policy->cpu; @@ -287,15 +287,21 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, return 0; } - if (val != CPUFREQ_NOTIFY) - return 0; table = cpufreq_frequency_get_table(cpu); if (!table) return 0; - ret = cpufreq_stats_create_table(policy, table); - if (ret) - return ret; - return 0; + + if (val == CPUFREQ_CREATE_POLICY) + ret = cpufreq_stats_create_table(policy, table); + else if (val == CPUFREQ_REMOVE_POLICY) { + /* This might already be freed by cpu hotplug notifier */ + if (per_cpu(cpufreq_stats_table, cpu)) { + cpufreq_stats_free_sysfs(cpu); + cpufreq_stats_free_table(cpu); + } + } + + return ret; } static int cpufreq_stat_notifier_trans(struct notifier_block *nb, @@ -340,6 +346,10 @@ static int cpufreq_stat_cpu_callback(struct notifier_block *nfb, { unsigned int cpu = (unsigned long)hcpu; + /* Don't free/allocate stats during suspend/resume */ + if (action & CPU_TASKS_FROZEN) + return 0; + switch (action) { case CPU_DOWN_PREPARE: cpufreq_stats_free_sysfs(cpu); -- cgit v1.1 From 027cc2e4a6127fdf29e17a422aacb5ecd9830cbb Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 7 Jan 2014 07:10:11 +0530 Subject: cpufreq: stats: remove hotplug notifiers Either CPUs are hot-unplugged or suspend/resume occurs, cpufreq core will send notifications to cpufreq-stats and stats structure and sysfs entries would be correctly handled.. And so we don't actually need hotcpu notifiers in cpufreq-stats anymore. We were only handling cpu hot-unplug events here and that are already taken care of by POLICY notifiers. Acked-by: Nicolas Pitre Tested-by: Nicolas Pitre Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_stats.c | 38 ++------------------------------------ 1 file changed, 2 insertions(+), 36 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 0f71562..8fa5844 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -294,11 +294,8 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, if (val == CPUFREQ_CREATE_POLICY) ret = cpufreq_stats_create_table(policy, table); else if (val == CPUFREQ_REMOVE_POLICY) { - /* This might already be freed by cpu hotplug notifier */ - if (per_cpu(cpufreq_stats_table, cpu)) { - cpufreq_stats_free_sysfs(cpu); - cpufreq_stats_free_table(cpu); - } + cpufreq_stats_free_sysfs(cpu); + cpufreq_stats_free_table(cpu); } return ret; @@ -340,33 +337,6 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb, return 0; } -static int cpufreq_stat_cpu_callback(struct notifier_block *nfb, - unsigned long action, - void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - - /* Don't free/allocate stats during suspend/resume */ - if (action & CPU_TASKS_FROZEN) - return 0; - - switch (action) { - case CPU_DOWN_PREPARE: - cpufreq_stats_free_sysfs(cpu); - break; - case CPU_DEAD: - cpufreq_stats_free_table(cpu); - break; - } - return NOTIFY_OK; -} - -/* priority=1 so this will get called before cpufreq_remove_dev */ -static struct notifier_block cpufreq_stat_cpu_notifier __refdata = { - .notifier_call = cpufreq_stat_cpu_callback, - .priority = 1, -}; - static struct notifier_block notifier_policy_block = { .notifier_call = cpufreq_stat_notifier_policy }; @@ -386,14 +356,11 @@ static int __init cpufreq_stats_init(void) if (ret) return ret; - register_hotcpu_notifier(&cpufreq_stat_cpu_notifier); - ret = cpufreq_register_notifier(¬ifier_trans_block, CPUFREQ_TRANSITION_NOTIFIER); if (ret) { cpufreq_unregister_notifier(¬ifier_policy_block, CPUFREQ_POLICY_NOTIFIER); - unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier); for_each_online_cpu(cpu) cpufreq_stats_free_table(cpu); return ret; @@ -409,7 +376,6 @@ static void __exit cpufreq_stats_exit(void) CPUFREQ_POLICY_NOTIFIER); cpufreq_unregister_notifier(¬ifier_trans_block, CPUFREQ_TRANSITION_NOTIFIER); - unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier); for_each_online_cpu(cpu) { cpufreq_stats_free_table(cpu); cpufreq_stats_free_sysfs(cpu); -- cgit v1.1 From 2d13594dcb7eaa41fa180e8a3b27a8f10845381f Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 7 Jan 2014 07:10:12 +0530 Subject: cpufreq: stats: free table and remove sysfs entry in a single routine We don't have code paths now where we need to do these two things separately, so it is better do them in a single routine. Just as they are allocated in a single routine. Acked-by: Nicolas Pitre Tested-by: Nicolas Pitre Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_stats.c | 50 ++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 8fa5844..9dd5883 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -151,40 +151,32 @@ static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq) return -1; } -/* should be called late in the CPU removal sequence so that the stats - * memory is still available in case someone tries to use it. - */ -static void cpufreq_stats_free_table(unsigned int cpu) +static void __cpufreq_stats_free_table(struct cpufreq_policy *policy) { - struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu); + struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu); - if (stat) { - pr_debug("%s: Free stat table\n", __func__); - kfree(stat->time_in_state); - kfree(stat); - per_cpu(cpufreq_stats_table, cpu) = NULL; - } + if (!stat) + return; + + pr_debug("%s: Free stat table\n", __func__); + + sysfs_remove_group(&policy->kobj, &stats_attr_group); + kfree(stat->time_in_state); + kfree(stat); + per_cpu(cpufreq_stats_table, policy->cpu) = NULL; } -/* must be called early in the CPU removal sequence (before - * cpufreq_remove_dev) so that policy is still valid. - */ -static void cpufreq_stats_free_sysfs(unsigned int cpu) +static void cpufreq_stats_free_table(unsigned int cpu) { - struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + struct cpufreq_policy *policy; + policy = cpufreq_cpu_get(cpu); if (!policy) return; - if (!cpufreq_frequency_get_table(cpu)) - goto put_ref; - - if (!policy_is_shared(policy)) { - pr_debug("%s: Free sysfs stat\n", __func__); - sysfs_remove_group(&policy->kobj, &stats_attr_group); - } + if (cpufreq_frequency_get_table(policy->cpu)) + __cpufreq_stats_free_table(policy); -put_ref: cpufreq_cpu_put(policy); } @@ -293,10 +285,8 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, if (val == CPUFREQ_CREATE_POLICY) ret = cpufreq_stats_create_table(policy, table); - else if (val == CPUFREQ_REMOVE_POLICY) { - cpufreq_stats_free_sysfs(cpu); - cpufreq_stats_free_table(cpu); - } + else if (val == CPUFREQ_REMOVE_POLICY) + __cpufreq_stats_free_table(policy); return ret; } @@ -376,10 +366,8 @@ static void __exit cpufreq_stats_exit(void) CPUFREQ_POLICY_NOTIFIER); cpufreq_unregister_notifier(¬ifier_trans_block, CPUFREQ_TRANSITION_NOTIFIER); - for_each_online_cpu(cpu) { + for_each_online_cpu(cpu) cpufreq_stats_free_table(cpu); - cpufreq_stats_free_sysfs(cpu); - } } MODULE_AUTHOR("Zou Nan hai "); -- cgit v1.1 From b3f9ff88db6e149086dc111e5652106005fd5923 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Tue, 7 Jan 2014 07:10:13 +0530 Subject: cpufreq: stats: create sysfs entries when cpufreq_stats is a module When cpufreq_stats is compiled in as a module, cpufreq driver would have already been registered. And so the CPUFREQ_CREATE_POLICY notifiers wouldn't be called for it. Hence no sysfs entries for stats. :( This patch calls cpufreq_stats_create_table() for each online CPU from cpufreq_stats_init() and so if policy is already created for CPUx then we will register sysfs stats for it. When its not compiled as module, we will return early as policy wouldn't be found for any of the CPUs. Acked-by: Nicolas Pitre Tested-by: Nicolas Pitre Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_stats.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 9dd5883..5793e14 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -180,7 +180,7 @@ static void cpufreq_stats_free_table(unsigned int cpu) cpufreq_cpu_put(policy); } -static int cpufreq_stats_create_table(struct cpufreq_policy *policy, +static int __cpufreq_stats_create_table(struct cpufreq_policy *policy, struct cpufreq_frequency_table *table) { unsigned int i, j, count = 0, ret = 0; @@ -253,6 +253,26 @@ error_get_fail: return ret; } +static void cpufreq_stats_create_table(unsigned int cpu) +{ + struct cpufreq_policy *policy; + struct cpufreq_frequency_table *table; + + /* + * "likely(!policy)" because normally cpufreq_stats will be registered + * before cpufreq driver + */ + policy = cpufreq_cpu_get(cpu); + if (likely(!policy)) + return; + + table = cpufreq_frequency_get_table(policy->cpu); + if (likely(table)) + __cpufreq_stats_create_table(policy, table); + + cpufreq_cpu_put(policy); +} + static void cpufreq_stats_update_policy_cpu(struct cpufreq_policy *policy) { struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, @@ -284,7 +304,7 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb, return 0; if (val == CPUFREQ_CREATE_POLICY) - ret = cpufreq_stats_create_table(policy, table); + ret = __cpufreq_stats_create_table(policy, table); else if (val == CPUFREQ_REMOVE_POLICY) __cpufreq_stats_free_table(policy); @@ -346,6 +366,9 @@ static int __init cpufreq_stats_init(void) if (ret) return ret; + for_each_online_cpu(cpu) + cpufreq_stats_create_table(cpu); + ret = cpufreq_register_notifier(¬ifier_trans_block, CPUFREQ_TRANSITION_NOTIFIER); if (ret) { -- cgit v1.1 From 652ed95d5fa6074b3c4ea245deb0691f1acb6656 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 9 Jan 2014 20:38:43 +0530 Subject: cpufreq: introduce cpufreq_generic_get() routine CPUFreq drivers that use clock frameworks interface,i.e. clk_get_rate(), to get CPUs clk rate, have similar sort of code used in most of them. This patch adds a generic ->get() which will do the same thing for them. All those drivers are required to now is to set .get to cpufreq_generic_get() and set their clk pointer in policy->clk during ->init(). Acked-by: Hans-Christian Egtvedt Acked-by: Shawn Guo Acked-by: Linus Walleij Acked-by: Shawn Guo Acked-by: Stephen Warren Signed-off-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/at32ap-cpufreq.c | 17 ++++--------- drivers/cpufreq/cpufreq-cpu0.c | 8 ++---- drivers/cpufreq/cpufreq.c | 26 ++++++++++++++----- drivers/cpufreq/davinci-cpufreq.c | 14 +++------- drivers/cpufreq/dbx500-cpufreq.c | 19 ++------------ drivers/cpufreq/exynos-cpufreq.c | 10 +++----- drivers/cpufreq/exynos5440-cpufreq.c | 33 ++++++++++-------------- drivers/cpufreq/imx6q-cpufreq.c | 8 ++---- drivers/cpufreq/loongson2_cpufreq.c | 15 ++++------- drivers/cpufreq/omap-cpufreq.c | 32 ++++++++--------------- drivers/cpufreq/ppc-corenet-cpufreq.c | 17 +++---------- drivers/cpufreq/s3c24xx-cpufreq.c | 10 +++----- drivers/cpufreq/s3c64xx-cpufreq.c | 33 +++++++++--------------- drivers/cpufreq/s5pv210-cpufreq.c | 21 +++++---------- drivers/cpufreq/spear-cpufreq.c | 8 ++---- drivers/cpufreq/tegra-cpufreq.c | 48 ++++++----------------------------- drivers/cpufreq/unicore2-cpufreq.c | 19 +++++--------- 17 files changed, 109 insertions(+), 229 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c index 7c03dd8..a1c79f5 100644 --- a/drivers/cpufreq/at32ap-cpufreq.c +++ b/drivers/cpufreq/at32ap-cpufreq.c @@ -21,17 +21,8 @@ #include #include -static struct clk *cpuclk; static struct cpufreq_frequency_table *freq_table; -static unsigned int at32_get_speed(unsigned int cpu) -{ - /* No SMP support */ - if (cpu) - return 0; - return (unsigned int)((clk_get_rate(cpuclk) + 500) / 1000); -} - static unsigned int ref_freq; static unsigned long loops_per_jiffy_ref; @@ -39,7 +30,7 @@ static int at32_set_target(struct cpufreq_policy *policy, unsigned int index) { unsigned int old_freq, new_freq; - old_freq = at32_get_speed(0); + old_freq = policy->cur; new_freq = freq_table[index].frequency; if (!ref_freq) { @@ -50,7 +41,7 @@ static int at32_set_target(struct cpufreq_policy *policy, unsigned int index) if (old_freq < new_freq) boot_cpu_data.loops_per_jiffy = cpufreq_scale( loops_per_jiffy_ref, ref_freq, new_freq); - clk_set_rate(cpuclk, new_freq * 1000); + clk_set_rate(policy->clk, new_freq * 1000); if (new_freq < old_freq) boot_cpu_data.loops_per_jiffy = cpufreq_scale( loops_per_jiffy_ref, ref_freq, new_freq); @@ -61,6 +52,7 @@ static int at32_set_target(struct cpufreq_policy *policy, unsigned int index) static int at32_cpufreq_driver_init(struct cpufreq_policy *policy) { unsigned int frequency, rate, min_freq; + static struct clk *cpuclk; int retval, steps, i; if (policy->cpu != 0) @@ -103,6 +95,7 @@ static int at32_cpufreq_driver_init(struct cpufreq_policy *policy) frequency /= 2; } + policy->clk = cpuclk; freq_table[steps - 1].frequency = CPUFREQ_TABLE_END; retval = cpufreq_table_validate_and_show(policy, freq_table); @@ -123,7 +116,7 @@ static struct cpufreq_driver at32_driver = { .init = at32_cpufreq_driver_init, .verify = cpufreq_generic_frequency_table_verify, .target_index = at32_set_target, - .get = at32_get_speed, + .get = cpufreq_generic_get, .flags = CPUFREQ_STICKY, }; diff --git a/drivers/cpufreq/cpufreq-cpu0.c b/drivers/cpufreq/cpufreq-cpu0.c index 0faf756..bb7b308 100644 --- a/drivers/cpufreq/cpufreq-cpu0.c +++ b/drivers/cpufreq/cpufreq-cpu0.c @@ -30,11 +30,6 @@ static struct clk *cpu_clk; static struct regulator *cpu_reg; static struct cpufreq_frequency_table *freq_table; -static unsigned int cpu0_get_speed(unsigned int cpu) -{ - return clk_get_rate(cpu_clk) / 1000; -} - static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; @@ -100,6 +95,7 @@ static int cpu0_set_target(struct cpufreq_policy *policy, unsigned int index) static int cpu0_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = cpu_clk; return cpufreq_generic_init(policy, freq_table, transition_latency); } @@ -107,7 +103,7 @@ static struct cpufreq_driver cpu0_cpufreq_driver = { .flags = CPUFREQ_STICKY, .verify = cpufreq_generic_frequency_table_verify, .target_index = cpu0_set_target, - .get = cpu0_get_speed, + .get = cpufreq_generic_get, .init = cpu0_cpufreq_init, .exit = cpufreq_generic_exit, .name = "generic_cpu0", diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 1afbe52..d7efdfe 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -176,6 +176,20 @@ int cpufreq_generic_init(struct cpufreq_policy *policy, } EXPORT_SYMBOL_GPL(cpufreq_generic_init); +unsigned int cpufreq_generic_get(unsigned int cpu) +{ + struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); + + if (!policy || IS_ERR(policy->clk)) { + pr_err("%s: No %s associated to cpu: %d\n", __func__, + policy ? "clk" : "policy", cpu); + return 0; + } + + return clk_get_rate(policy->clk) / 1000; +} +EXPORT_SYMBOL_GPL(cpufreq_generic_get); + struct cpufreq_policy *cpufreq_cpu_get(unsigned int cpu) { struct cpufreq_policy *policy = NULL; @@ -1068,6 +1082,11 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, goto err_set_policy_cpu; } + write_lock_irqsave(&cpufreq_driver_lock, flags); + for_each_cpu(j, policy->cpus) + per_cpu(cpufreq_cpu_data, j) = policy; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + if (cpufreq_driver->get) { policy->cur = cpufreq_driver->get(policy->cpu); if (!policy->cur) { @@ -1142,11 +1161,6 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, } #endif - write_lock_irqsave(&cpufreq_driver_lock, flags); - for_each_cpu(j, policy->cpus) - per_cpu(cpufreq_cpu_data, j) = policy; - write_unlock_irqrestore(&cpufreq_driver_lock, flags); - if (!frozen) { ret = cpufreq_add_dev_interface(policy, dev); if (ret) @@ -1174,12 +1188,12 @@ static int __cpufreq_add_dev(struct device *dev, struct subsys_interface *sif, return 0; err_out_unregister: +err_get_freq: write_lock_irqsave(&cpufreq_driver_lock, flags); for_each_cpu(j, policy->cpus) per_cpu(cpufreq_cpu_data, j) = NULL; write_unlock_irqrestore(&cpufreq_driver_lock, flags); -err_get_freq: if (cpufreq_driver->exit) cpufreq_driver->exit(policy); err_set_policy_cpu: diff --git a/drivers/cpufreq/davinci-cpufreq.c b/drivers/cpufreq/davinci-cpufreq.c index 04f3390..2cf3384 100644 --- a/drivers/cpufreq/davinci-cpufreq.c +++ b/drivers/cpufreq/davinci-cpufreq.c @@ -58,14 +58,6 @@ static int davinci_verify_speed(struct cpufreq_policy *policy) return 0; } -static unsigned int davinci_getspeed(unsigned int cpu) -{ - if (cpu) - return 0; - - return clk_get_rate(cpufreq.armclk) / 1000; -} - static int davinci_target(struct cpufreq_policy *policy, unsigned int idx) { struct davinci_cpufreq_config *pdata = cpufreq.dev->platform_data; @@ -73,7 +65,7 @@ static int davinci_target(struct cpufreq_policy *policy, unsigned int idx) unsigned int old_freq, new_freq; int ret = 0; - old_freq = davinci_getspeed(0); + old_freq = policy->cur; new_freq = pdata->freq_table[idx].frequency; /* if moving to higher frequency, up the voltage beforehand */ @@ -116,6 +108,8 @@ static int davinci_cpu_init(struct cpufreq_policy *policy) return result; } + policy->clk = cpufreq.armclk; + /* * Time measurement across the target() function yields ~1500-1800us * time taken with no drivers on notification list. @@ -129,7 +123,7 @@ static struct cpufreq_driver davinci_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = davinci_verify_speed, .target_index = davinci_target, - .get = davinci_getspeed, + .get = cpufreq_generic_get, .init = davinci_cpu_init, .exit = cpufreq_generic_exit, .name = "davinci", diff --git a/drivers/cpufreq/dbx500-cpufreq.c b/drivers/cpufreq/dbx500-cpufreq.c index 21d9898..412a78b 100644 --- a/drivers/cpufreq/dbx500-cpufreq.c +++ b/drivers/cpufreq/dbx500-cpufreq.c @@ -26,24 +26,9 @@ static int dbx500_cpufreq_target(struct cpufreq_policy *policy, return clk_set_rate(armss_clk, freq_table[index].frequency * 1000); } -static unsigned int dbx500_cpufreq_getspeed(unsigned int cpu) -{ - int i = 0; - unsigned long freq = clk_get_rate(armss_clk) / 1000; - - /* The value is rounded to closest frequency in the defined table. */ - while (freq_table[i + 1].frequency != CPUFREQ_TABLE_END) { - if (freq < freq_table[i].frequency + - (freq_table[i + 1].frequency - freq_table[i].frequency) / 2) - return freq_table[i].frequency; - i++; - } - - return freq_table[i].frequency; -} - static int dbx500_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = armss_clk; return cpufreq_generic_init(policy, freq_table, 20 * 1000); } @@ -52,7 +37,7 @@ static struct cpufreq_driver dbx500_cpufreq_driver = { CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = dbx500_cpufreq_target, - .get = dbx500_cpufreq_getspeed, + .get = cpufreq_generic_get, .init = dbx500_cpufreq_init, .name = "DBX500", .attr = cpufreq_generic_attr, diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index f7c322c..4ee3804 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -31,11 +31,6 @@ static unsigned int locking_frequency; static bool frequency_locked; static DEFINE_MUTEX(cpufreq_lock); -static unsigned int exynos_getspeed(unsigned int cpu) -{ - return clk_get_rate(exynos_info->cpu_clk) / 1000; -} - static int exynos_cpufreq_get_index(unsigned int freq) { struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; @@ -215,6 +210,7 @@ static struct notifier_block exynos_cpufreq_nb = { static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { + policy->clk = exynos_info->cpu_clk; return cpufreq_generic_init(policy, exynos_info->freq_table, 100000); } @@ -222,7 +218,7 @@ static struct cpufreq_driver exynos_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = exynos_target, - .get = exynos_getspeed, + .get = cpufreq_generic_get, .init = exynos_cpufreq_cpu_init, .exit = cpufreq_generic_exit, .name = "exynos_cpufreq", @@ -264,7 +260,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_vdd_arm; } - locking_frequency = exynos_getspeed(0); + locking_frequency = clk_get_rate(exynos_info->cpu_clk) / 1000; register_pm_notifier(&exynos_cpufreq_nb); diff --git a/drivers/cpufreq/exynos5440-cpufreq.c b/drivers/cpufreq/exynos5440-cpufreq.c index ffe6fae..49b75601 100644 --- a/drivers/cpufreq/exynos5440-cpufreq.c +++ b/drivers/cpufreq/exynos5440-cpufreq.c @@ -100,7 +100,6 @@ struct exynos_dvfs_data { struct resource *mem; int irq; struct clk *cpu_clk; - unsigned int cur_frequency; unsigned int latency; struct cpufreq_frequency_table *freq_table; unsigned int freq_count; @@ -165,7 +164,7 @@ static int init_div_table(void) return 0; } -static void exynos_enable_dvfs(void) +static void exynos_enable_dvfs(unsigned int cur_frequency) { unsigned int tmp, i, cpu; struct cpufreq_frequency_table *freq_table = dvfs_info->freq_table; @@ -184,18 +183,18 @@ static void exynos_enable_dvfs(void) /* Set initial performance index */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) - if (freq_table[i].frequency == dvfs_info->cur_frequency) + if (freq_table[i].frequency == cur_frequency) break; if (freq_table[i].frequency == CPUFREQ_TABLE_END) { dev_crit(dvfs_info->dev, "Boot up frequency not supported\n"); /* Assign the highest frequency */ i = 0; - dvfs_info->cur_frequency = freq_table[i].frequency; + cur_frequency = freq_table[i].frequency; } dev_info(dvfs_info->dev, "Setting dvfs initial frequency = %uKHZ", - dvfs_info->cur_frequency); + cur_frequency); for (cpu = 0; cpu < CONFIG_NR_CPUS; cpu++) { tmp = __raw_readl(dvfs_info->base + XMU_C0_3_PSTATE + cpu * 4); @@ -209,11 +208,6 @@ static void exynos_enable_dvfs(void) dvfs_info->base + XMU_DVFS_CTRL); } -static unsigned int exynos_getspeed(unsigned int cpu) -{ - return dvfs_info->cur_frequency; -} - static int exynos_target(struct cpufreq_policy *policy, unsigned int index) { unsigned int tmp; @@ -222,7 +216,7 @@ static int exynos_target(struct cpufreq_policy *policy, unsigned int index) mutex_lock(&cpufreq_lock); - freqs.old = dvfs_info->cur_frequency; + freqs.old = policy->cur; freqs.new = freq_table[index].frequency; cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); @@ -250,7 +244,7 @@ static void exynos_cpufreq_work(struct work_struct *work) goto skip_work; mutex_lock(&cpufreq_lock); - freqs.old = dvfs_info->cur_frequency; + freqs.old = policy->cur; cur_pstate = __raw_readl(dvfs_info->base + XMU_P_STATUS); if (cur_pstate >> C0_3_PSTATE_VALID_SHIFT & 0x1) @@ -260,10 +254,9 @@ static void exynos_cpufreq_work(struct work_struct *work) if (likely(index < dvfs_info->freq_count)) { freqs.new = freq_table[index].frequency; - dvfs_info->cur_frequency = freqs.new; } else { dev_crit(dvfs_info->dev, "New frequency out of range\n"); - freqs.new = dvfs_info->cur_frequency; + freqs.new = freqs.old; } cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE); @@ -307,6 +300,7 @@ static void exynos_sort_descend_freq_table(void) static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) { + policy->clk = dvfs_info->cpu_clk; return cpufreq_generic_init(policy, dvfs_info->freq_table, dvfs_info->latency); } @@ -316,7 +310,7 @@ static struct cpufreq_driver exynos_driver = { CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = exynos_target, - .get = exynos_getspeed, + .get = cpufreq_generic_get, .init = exynos_cpufreq_cpu_init, .exit = cpufreq_generic_exit, .name = CPUFREQ_NAME, @@ -336,6 +330,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) int ret = -EINVAL; struct device_node *np; struct resource res; + unsigned int cur_frequency; np = pdev->dev.of_node; if (!np) @@ -392,13 +387,13 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_free_table; } - dvfs_info->cur_frequency = clk_get_rate(dvfs_info->cpu_clk); - if (!dvfs_info->cur_frequency) { + cur_frequency = clk_get_rate(dvfs_info->cpu_clk); + if (!cur_frequency) { dev_err(dvfs_info->dev, "Failed to get clock rate\n"); ret = -EINVAL; goto err_free_table; } - dvfs_info->cur_frequency /= 1000; + cur_frequency /= 1000; INIT_WORK(&dvfs_info->irq_work, exynos_cpufreq_work); ret = devm_request_irq(dvfs_info->dev, dvfs_info->irq, @@ -415,7 +410,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) goto err_free_table; } - exynos_enable_dvfs(); + exynos_enable_dvfs(cur_frequency); ret = cpufreq_register_driver(&exynos_driver); if (ret) { dev_err(dvfs_info->dev, diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 2938257..ce69059 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -38,11 +38,6 @@ static unsigned int transition_latency; static u32 *imx6_soc_volt; static u32 soc_opp_count; -static unsigned int imx6q_get_speed(unsigned int cpu) -{ - return clk_get_rate(arm_clk) / 1000; -} - static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) { struct dev_pm_opp *opp; @@ -139,6 +134,7 @@ static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) static int imx6q_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = arm_clk; return cpufreq_generic_init(policy, freq_table, transition_latency); } @@ -146,7 +142,7 @@ static struct cpufreq_driver imx6q_cpufreq_driver = { .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = imx6q_set_target, - .get = imx6q_get_speed, + .get = cpufreq_generic_get, .init = imx6q_cpufreq_init, .exit = cpufreq_generic_exit, .name = "imx6q-cpufreq", diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index a436092..b6581ab 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -24,8 +24,6 @@ static uint nowait; -static struct clk *cpuclk; - static void (*saved_cpu_wait) (void); static int loongson2_cpu_freq_notifier(struct notifier_block *nb, @@ -44,11 +42,6 @@ static int loongson2_cpu_freq_notifier(struct notifier_block *nb, return 0; } -static unsigned int loongson2_cpufreq_get(unsigned int cpu) -{ - return clk_get_rate(cpuclk); -} - /* * Here we notify other drivers of the proposed change and the final change. */ @@ -69,13 +62,14 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, set_cpus_allowed_ptr(current, &cpus_allowed); /* setting the cpu frequency */ - clk_set_rate(cpuclk, freq); + clk_set_rate(policy->clk, freq); return 0; } static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) { + static struct clk *cpuclk; int i; unsigned long rate; int ret; @@ -104,13 +98,14 @@ static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) return ret; } + policy->clk = cpuclk; return cpufreq_generic_init(policy, &loongson2_clockmod_table[0], 0); } static int loongson2_cpufreq_exit(struct cpufreq_policy *policy) { cpufreq_frequency_table_put_attr(policy->cpu); - clk_put(cpuclk); + clk_put(policy->clk); return 0; } @@ -119,7 +114,7 @@ static struct cpufreq_driver loongson2_cpufreq_driver = { .init = loongson2_cpufreq_cpu_init, .verify = cpufreq_generic_frequency_table_verify, .target_index = loongson2_cpufreq_target, - .get = loongson2_cpufreq_get, + .get = cpufreq_generic_get, .exit = loongson2_cpufreq_exit, .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 5de1e5f..590f5b6 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -36,21 +36,9 @@ static struct cpufreq_frequency_table *freq_table; static atomic_t freq_table_users = ATOMIC_INIT(0); -static struct clk *mpu_clk; static struct device *mpu_dev; static struct regulator *mpu_reg; -static unsigned int omap_getspeed(unsigned int cpu) -{ - unsigned long rate; - - if (cpu >= NR_CPUS) - return 0; - - rate = clk_get_rate(mpu_clk) / 1000; - return rate; -} - static int omap_target(struct cpufreq_policy *policy, unsigned int index) { int r, ret; @@ -58,11 +46,11 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int index) unsigned long freq, volt = 0, volt_old = 0, tol = 0; unsigned int old_freq, new_freq; - old_freq = omap_getspeed(policy->cpu); + old_freq = policy->cur; new_freq = freq_table[index].frequency; freq = new_freq * 1000; - ret = clk_round_rate(mpu_clk, freq); + ret = clk_round_rate(policy->clk, freq); if (IS_ERR_VALUE(ret)) { dev_warn(mpu_dev, "CPUfreq: Cannot find matching frequency for %lu\n", @@ -100,7 +88,7 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int index) } } - ret = clk_set_rate(mpu_clk, new_freq * 1000); + ret = clk_set_rate(policy->clk, new_freq * 1000); /* scaling down? scale voltage after frequency */ if (mpu_reg && (new_freq < old_freq)) { @@ -108,7 +96,7 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int index) if (r < 0) { dev_warn(mpu_dev, "%s: unable to scale voltage down.\n", __func__); - clk_set_rate(mpu_clk, old_freq * 1000); + clk_set_rate(policy->clk, old_freq * 1000); return r; } } @@ -126,9 +114,9 @@ static int omap_cpu_init(struct cpufreq_policy *policy) { int result; - mpu_clk = clk_get(NULL, "cpufreq_ck"); - if (IS_ERR(mpu_clk)) - return PTR_ERR(mpu_clk); + policy->clk = clk_get(NULL, "cpufreq_ck"); + if (IS_ERR(policy->clk)) + return PTR_ERR(policy->clk); if (!freq_table) { result = dev_pm_opp_init_cpufreq_table(mpu_dev, &freq_table); @@ -149,7 +137,7 @@ static int omap_cpu_init(struct cpufreq_policy *policy) freq_table_free(); fail: - clk_put(mpu_clk); + clk_put(policy->clk); return result; } @@ -157,7 +145,7 @@ static int omap_cpu_exit(struct cpufreq_policy *policy) { cpufreq_frequency_table_put_attr(policy->cpu); freq_table_free(); - clk_put(mpu_clk); + clk_put(policy->clk); return 0; } @@ -165,7 +153,7 @@ static struct cpufreq_driver omap_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = omap_target, - .get = omap_getspeed, + .get = cpufreq_generic_get, .init = omap_cpu_init, .exit = omap_cpu_exit, .name = "omap", diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 3f7be46..051000f 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -24,12 +24,10 @@ /** * struct cpu_data - per CPU data struct - * @clk: the clk of CPU * @parent: the parent node of cpu clock * @table: frequency table */ struct cpu_data { - struct clk *clk; struct device_node *parent; struct cpufreq_frequency_table *table; }; @@ -81,13 +79,6 @@ static inline const struct cpumask *cpu_core_mask(int cpu) } #endif -static unsigned int corenet_cpufreq_get_speed(unsigned int cpu) -{ - struct cpu_data *data = per_cpu(cpu_data, cpu); - - return clk_get_rate(data->clk) / 1000; -} - /* reduce the duplicated frequencies in frequency table */ static void freq_table_redup(struct cpufreq_frequency_table *freq_table, int count) @@ -158,8 +149,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_np; } - data->clk = of_clk_get(np, 0); - if (IS_ERR(data->clk)) { + policy->clk = of_clk_get(np, 0); + if (IS_ERR(policy->clk)) { pr_err("%s: no clock information\n", __func__); goto err_nomem2; } @@ -255,7 +246,7 @@ static int corenet_cpufreq_target(struct cpufreq_policy *policy, struct cpu_data *data = per_cpu(cpu_data, policy->cpu); parent = of_clk_get(data->parent, data->table[index].driver_data); - return clk_set_parent(data->clk, parent); + return clk_set_parent(policy->clk, parent); } static struct cpufreq_driver ppc_corenet_cpufreq_driver = { @@ -265,7 +256,7 @@ static struct cpufreq_driver ppc_corenet_cpufreq_driver = { .exit = __exit_p(corenet_cpufreq_cpu_exit), .verify = cpufreq_generic_frequency_table_verify, .target_index = corenet_cpufreq_target, - .get = corenet_cpufreq_get_speed, + .get = cpufreq_generic_get, .attr = cpufreq_generic_attr, }; diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index 6a1bf96..2506974 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -355,11 +355,6 @@ static int s3c_cpufreq_target(struct cpufreq_policy *policy, return -EINVAL; } -static unsigned int s3c_cpufreq_get(unsigned int cpu) -{ - return clk_get_rate(clk_arm) / 1000; -} - struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) { struct clk *clk; @@ -373,6 +368,7 @@ struct clk *s3c_cpufreq_clk_get(struct device *dev, const char *name) static int s3c_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = clk_arm; return cpufreq_generic_init(policy, ftab, cpu_cur.info->latency); } @@ -408,7 +404,7 @@ static int s3c_cpufreq_suspend(struct cpufreq_policy *policy) { suspend_pll.frequency = clk_get_rate(_clk_mpll); suspend_pll.driver_data = __raw_readl(S3C2410_MPLLCON); - suspend_freq = s3c_cpufreq_get(0) * 1000; + suspend_freq = clk_get_rate(clk_arm); return 0; } @@ -450,7 +446,7 @@ static int s3c_cpufreq_resume(struct cpufreq_policy *policy) static struct cpufreq_driver s3c24xx_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .target = s3c_cpufreq_target, - .get = s3c_cpufreq_get, + .get = cpufreq_generic_get, .init = s3c_cpufreq_init, .suspend = s3c_cpufreq_suspend, .resume = s3c_cpufreq_resume, diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 8435f45..c4226de 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -19,7 +19,6 @@ #include #include -static struct clk *armclk; static struct regulator *vddarm; static unsigned long regulator_latency; @@ -54,14 +53,6 @@ static struct cpufreq_frequency_table s3c64xx_freq_table[] = { }; #endif -static unsigned int s3c64xx_cpufreq_get_speed(unsigned int cpu) -{ - if (cpu != 0) - return 0; - - return clk_get_rate(armclk) / 1000; -} - static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int index) { @@ -69,7 +60,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, unsigned int old_freq, new_freq; int ret; - old_freq = clk_get_rate(armclk) / 1000; + old_freq = clk_get_rate(policy->clk) / 1000; new_freq = s3c64xx_freq_table[index].frequency; dvfs = &s3c64xx_dvfs_table[s3c64xx_freq_table[index].driver_data]; @@ -86,7 +77,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, } #endif - ret = clk_set_rate(armclk, new_freq * 1000); + ret = clk_set_rate(policy->clk, new_freq * 1000); if (ret < 0) { pr_err("Failed to set rate %dkHz: %d\n", new_freq, ret); @@ -101,7 +92,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, if (ret != 0) { pr_err("Failed to set VDDARM for %dkHz: %d\n", new_freq, ret); - if (clk_set_rate(armclk, old_freq * 1000) < 0) + if (clk_set_rate(policy->clk, old_freq * 1000) < 0) pr_err("Failed to restore original clock rate\n"); return ret; @@ -110,7 +101,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, #endif pr_debug("Set actual frequency %lukHz\n", - clk_get_rate(armclk) / 1000); + clk_get_rate(policy->clk) / 1000); return 0; } @@ -169,11 +160,11 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) return -ENODEV; } - armclk = clk_get(NULL, "armclk"); - if (IS_ERR(armclk)) { + policy->clk = clk_get(NULL, "armclk"); + if (IS_ERR(policy->clk)) { pr_err("Unable to obtain ARMCLK: %ld\n", - PTR_ERR(armclk)); - return PTR_ERR(armclk); + PTR_ERR(policy->clk)); + return PTR_ERR(policy->clk); } #ifdef CONFIG_REGULATOR @@ -193,7 +184,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) unsigned long r; /* Check for frequencies we can generate */ - r = clk_round_rate(armclk, freq->frequency * 1000); + r = clk_round_rate(policy->clk, freq->frequency * 1000); r /= 1000; if (r != freq->frequency) { pr_debug("%dkHz unsupported by clock\n", @@ -203,7 +194,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) /* If we have no regulator then assume startup * frequency is the maximum we can support. */ - if (!vddarm && freq->frequency > s3c64xx_cpufreq_get_speed(0)) + if (!vddarm && freq->frequency > clk_get_rate(policy->clk) / 1000) freq->frequency = CPUFREQ_ENTRY_INVALID; freq++; @@ -219,7 +210,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) pr_err("Failed to configure frequency table: %d\n", ret); regulator_put(vddarm); - clk_put(armclk); + clk_put(policy->clk); } return ret; @@ -229,7 +220,7 @@ static struct cpufreq_driver s3c64xx_cpufreq_driver = { .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s3c64xx_cpufreq_set_target, - .get = s3c64xx_cpufreq_get_speed, + .get = cpufreq_generic_get, .init = s3c64xx_cpufreq_driver_init, .name = "s3c", }; diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index ccd548c..55a8e9f 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -23,7 +23,6 @@ #include #include -static struct clk *cpu_clk; static struct clk *dmc0_clk; static struct clk *dmc1_clk; static DEFINE_MUTEX(set_freq_lock); @@ -164,14 +163,6 @@ static void s5pv210_set_refresh(enum s5pv210_dmc_port ch, unsigned long freq) __raw_writel(tmp1, reg); } -static unsigned int s5pv210_getspeed(unsigned int cpu) -{ - if (cpu) - return 0; - - return clk_get_rate(cpu_clk) / 1000; -} - static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) { unsigned long reg; @@ -193,7 +184,7 @@ static int s5pv210_target(struct cpufreq_policy *policy, unsigned int index) goto exit; } - old_freq = s5pv210_getspeed(0); + old_freq = policy->cur; new_freq = s5pv210_freq_table[index].frequency; /* Finding current running level index */ @@ -471,9 +462,9 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) unsigned long mem_type; int ret; - cpu_clk = clk_get(NULL, "armclk"); - if (IS_ERR(cpu_clk)) - return PTR_ERR(cpu_clk); + policy->clk = clk_get(NULL, "armclk"); + if (IS_ERR(policy->clk)) + return PTR_ERR(policy->clk); dmc0_clk = clk_get(NULL, "sclk_dmc0"); if (IS_ERR(dmc0_clk)) { @@ -516,7 +507,7 @@ static int __init s5pv210_cpu_init(struct cpufreq_policy *policy) out_dmc1: clk_put(dmc0_clk); out_dmc0: - clk_put(cpu_clk); + clk_put(policy->clk); return ret; } @@ -563,7 +554,7 @@ static struct cpufreq_driver s5pv210_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = s5pv210_target, - .get = s5pv210_getspeed, + .get = cpufreq_generic_get, .init = s5pv210_cpu_init, .name = "s5pv210", #ifdef CONFIG_PM diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index c7525fe..5c86e3f 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -30,11 +30,6 @@ static struct { u32 cnt; } spear_cpufreq; -static unsigned int spear_cpufreq_get(unsigned int cpu) -{ - return clk_get_rate(spear_cpufreq.clk) / 1000; -} - static struct clk *spear1340_cpu_get_possible_parent(unsigned long newfreq) { struct clk *sys_pclk; @@ -156,6 +151,7 @@ static int spear_cpufreq_target(struct cpufreq_policy *policy, static int spear_cpufreq_init(struct cpufreq_policy *policy) { + policy->clk = spear_cpufreq.clk; return cpufreq_generic_init(policy, spear_cpufreq.freq_tbl, spear_cpufreq.transition_latency); } @@ -165,7 +161,7 @@ static struct cpufreq_driver spear_cpufreq_driver = { .flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = spear_cpufreq_target, - .get = spear_cpufreq_get, + .get = cpufreq_generic_get, .init = spear_cpufreq_init, .exit = cpufreq_generic_exit, .attr = cpufreq_generic_attr, diff --git a/drivers/cpufreq/tegra-cpufreq.c b/drivers/cpufreq/tegra-cpufreq.c index 01b5578..e652c1b 100644 --- a/drivers/cpufreq/tegra-cpufreq.c +++ b/drivers/cpufreq/tegra-cpufreq.c @@ -47,21 +47,9 @@ static struct clk *pll_x_clk; static struct clk *pll_p_clk; static struct clk *emc_clk; -static unsigned long target_cpu_speed[NUM_CPUS]; static DEFINE_MUTEX(tegra_cpu_lock); static bool is_suspended; -static unsigned int tegra_getspeed(unsigned int cpu) -{ - unsigned long rate; - - if (cpu >= NUM_CPUS) - return 0; - - rate = clk_get_rate(cpu_clk) / 1000; - return rate; -} - static int tegra_cpu_clk_set_rate(unsigned long rate) { int ret; @@ -103,9 +91,6 @@ static int tegra_update_cpu_speed(struct cpufreq_policy *policy, { int ret = 0; - if (tegra_getspeed(0) == rate) - return ret; - /* * Vote on memory bus frequency based on cpu frequency * This sets the minimum frequency, display or avp may request higher @@ -125,33 +110,16 @@ static int tegra_update_cpu_speed(struct cpufreq_policy *policy, return ret; } -static unsigned long tegra_cpu_highest_speed(void) -{ - unsigned long rate = 0; - int i; - - for_each_online_cpu(i) - rate = max(rate, target_cpu_speed[i]); - return rate; -} - static int tegra_target(struct cpufreq_policy *policy, unsigned int index) { - unsigned int freq; - int ret = 0; + int ret = -EBUSY; mutex_lock(&tegra_cpu_lock); - if (is_suspended) - goto out; - - freq = freq_table[index].frequency; + if (!is_suspended) + ret = tegra_update_cpu_speed(policy, + freq_table[index].frequency); - target_cpu_speed[policy->cpu] = freq; - - ret = tegra_update_cpu_speed(policy, tegra_cpu_highest_speed()); - -out: mutex_unlock(&tegra_cpu_lock); return ret; } @@ -165,7 +133,8 @@ static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, is_suspended = true; pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n", freq_table[0].frequency); - tegra_update_cpu_speed(policy, freq_table[0].frequency); + if (clk_get_rate(cpu_clk) / 1000 != freq_table[0].frequency) + tegra_update_cpu_speed(policy, freq_table[0].frequency); cpufreq_cpu_put(policy); } else if (event == PM_POST_SUSPEND) { is_suspended = false; @@ -189,8 +158,6 @@ static int tegra_cpu_init(struct cpufreq_policy *policy) clk_prepare_enable(emc_clk); clk_prepare_enable(cpu_clk); - target_cpu_speed[policy->cpu] = tegra_getspeed(policy->cpu); - /* FIXME: what's the actual transition time? */ ret = cpufreq_generic_init(policy, freq_table, 300 * 1000); if (ret) { @@ -202,6 +169,7 @@ static int tegra_cpu_init(struct cpufreq_policy *policy) if (policy->cpu == 0) register_pm_notifier(&tegra_cpu_pm_notifier); + policy->clk = cpu_clk; return 0; } @@ -217,7 +185,7 @@ static struct cpufreq_driver tegra_cpufreq_driver = { .flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK, .verify = cpufreq_generic_frequency_table_verify, .target_index = tegra_target, - .get = tegra_getspeed, + .get = cpufreq_generic_get, .init = tegra_cpu_init, .exit = tegra_cpu_exit, .name = "tegra", diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 86f6cfe..36cc330 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c @@ -11,6 +11,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -33,28 +34,18 @@ static int ucv2_verify_speed(struct cpufreq_policy *policy) return 0; } -static unsigned int ucv2_getspeed(unsigned int cpu) -{ - struct clk *mclk = clk_get(NULL, "MAIN_CLK"); - - if (cpu) - return 0; - return clk_get_rate(mclk)/1000; -} - static int ucv2_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { struct cpufreq_freqs freqs; - struct clk *mclk = clk_get(NULL, "MAIN_CLK"); int ret; freqs.old = policy->cur; freqs.new = target_freq; cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE); - ret = clk_set_rate(mclk, target_freq * 1000); + ret = clk_set_rate(policy->mclk, target_freq * 1000); cpufreq_notify_post_transition(policy, &freqs, ret); return ret; @@ -64,9 +55,13 @@ static int __init ucv2_cpu_init(struct cpufreq_policy *policy) { if (policy->cpu != 0) return -EINVAL; + policy->min = policy->cpuinfo.min_freq = 250000; policy->max = policy->cpuinfo.max_freq = 1000000; policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL; + policy->clk = clk_get(NULL, "MAIN_CLK"); + if (IS_ERR(policy->clk)) + return PTR_ERR(policy->clk); return 0; } @@ -74,7 +69,7 @@ static struct cpufreq_driver ucv2_driver = { .flags = CPUFREQ_STICKY, .verify = ucv2_verify_speed, .target = ucv2_target, - .get = ucv2_getspeed, + .get = cpufreq_generic_get, .init = ucv2_cpu_init, .name = "UniCore-II", }; -- cgit v1.1 From b69880f9ccf7e13b2e2cb38f49a2451d7aa548b3 Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Thu, 16 Jan 2014 10:32:25 -0800 Subject: intel_pstate: Add trace point to report internal state. Add perf trace event "power:pstate_sample" to report driver state to aid in diagnosing issues reported against intel_pstate. Signed-off-by: Dirk Brandewie Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index fe91dad..7e257b2 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -51,6 +51,8 @@ static inline int32_t div_fp(int32_t x, int32_t y) return div_s64((int64_t)x << FRAC_BITS, (int64_t)y); } +static u64 energy_divisor; + struct sample { int32_t core_pct_busy; u64 aperf; @@ -559,6 +561,7 @@ static inline void intel_pstate_sample(struct cpudata *cpu) rdmsrl(MSR_IA32_APERF, aperf); rdmsrl(MSR_IA32_MPERF, mperf); + cpu->sample_ptr = (cpu->sample_ptr + 1) % SAMPLE_COUNT; cpu->samples[cpu->sample_ptr].aperf = aperf; cpu->samples[cpu->sample_ptr].mperf = mperf; @@ -603,6 +606,7 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) ctl = pid_calc(pid, busy_scaled); steps = abs(ctl); + if (ctl < 0) intel_pstate_pstate_increase(cpu, steps); else @@ -612,9 +616,24 @@ static inline void intel_pstate_adjust_busy_pstate(struct cpudata *cpu) static void intel_pstate_timer_func(unsigned long __data) { struct cpudata *cpu = (struct cpudata *) __data; + struct sample *sample; + u64 energy; intel_pstate_sample(cpu); + + sample = &cpu->samples[cpu->sample_ptr]; + rdmsrl(MSR_PKG_ENERGY_STATUS, energy); + intel_pstate_adjust_busy_pstate(cpu); + + trace_pstate_sample(fp_toint(sample->core_pct_busy), + fp_toint(intel_pstate_get_scaled_busy(cpu)), + cpu->pstate.current_pstate, + sample->mperf, + sample->aperf, + div64_u64(energy, energy_divisor), + sample->freq); + intel_pstate_set_sample_time(cpu); } @@ -894,6 +913,7 @@ static int __init intel_pstate_init(void) int cpu, rc = 0; const struct x86_cpu_id *id; struct cpu_defaults *cpu_info; + u64 units; if (no_load) return -ENODEV; @@ -927,8 +947,12 @@ static int __init intel_pstate_init(void) if (rc) goto out; + rdmsrl(MSR_RAPL_POWER_UNIT, units); + energy_divisor = 1 << ((units >> 8) & 0x1f); /* bits{12:8} */ + intel_pstate_debug_expose_params(); intel_pstate_sysfs_expose_params(); + return rc; out: get_online_cpus(); -- cgit v1.1 From 6f19efc0a1ca08bc61841b971d8b85ab505d95c8 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:49 +0100 Subject: cpufreq: Add boost frequency support in core This commit adds boost frequency support in cpufreq core (Hardware & Software). Some SoCs (like Exynos4 - e.g. 4x12) allow setting frequency above its normal operation limits. Such mode shall be only used for a short time. Overclocking (boost) support is essentially provided by platform dependent cpufreq driver. This commit unifies support for SW and HW (Intel) overclocking solutions in the core cpufreq driver. Previously the "boost" sysfs attribute was defined in the ACPI processor driver code. By default boost is disabled. One global attribute is available at: /sys/devices/system/cpu/cpufreq/boost. It only shows up when cpufreq driver supports overclocking. Under the hood frequencies dedicated for boosting are marked with a special flag (CPUFREQ_BOOST_FREQ) at driver's frequency table. It is the user's concern to enable/disable overclocking with a proper call to sysfs. The cpufreq_boost_trigger_state() function is defined non static on purpose. It is used later with thermal subsystem to provide automatic enable/disable of the BOOST feature. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 118 ++++++++++++++++++++++++++++++++++++++++++- drivers/cpufreq/freq_table.c | 56 +++++++++++++++++--- 2 files changed, 166 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d7efdfe..08ca8c9 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -352,6 +352,33 @@ EXPORT_SYMBOL_GPL(cpufreq_notify_post_transition); /********************************************************************* * SYSFS INTERFACE * *********************************************************************/ +ssize_t show_boost(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", cpufreq_driver->boost_enabled); +} + +static ssize_t store_boost(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + int ret, enable; + + ret = sscanf(buf, "%d", &enable); + if (ret != 1 || enable < 0 || enable > 1) + return -EINVAL; + + if (cpufreq_boost_trigger_state(enable)) { + pr_err("%s: Cannot %s BOOST!\n", __func__, + enable ? "enable" : "disable"); + return -EINVAL; + } + + pr_debug("%s: cpufreq BOOST %s\n", __func__, + enable ? "enabled" : "disabled"); + + return count; +} +define_one_global_rw(boost); static struct cpufreq_governor *__find_governor(const char *str_governor) { @@ -2184,6 +2211,73 @@ static struct notifier_block __refdata cpufreq_cpu_notifier = { }; /********************************************************************* + * BOOST * + *********************************************************************/ +static int cpufreq_boost_set_sw(int state) +{ + struct cpufreq_frequency_table *freq_table; + struct cpufreq_policy *policy; + int ret = -EINVAL; + + list_for_each_entry(policy, &cpufreq_policy_list, policy_list) { + freq_table = cpufreq_frequency_get_table(policy->cpu); + if (freq_table) { + ret = cpufreq_frequency_table_cpuinfo(policy, + freq_table); + if (ret) { + pr_err("%s: Policy frequency update failed\n", + __func__); + break; + } + policy->user_policy.max = policy->max; + __cpufreq_governor(policy, CPUFREQ_GOV_LIMITS); + } + } + + return ret; +} + +int cpufreq_boost_trigger_state(int state) +{ + unsigned long flags; + int ret = 0; + + if (cpufreq_driver->boost_enabled == state) + return 0; + + write_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_driver->boost_enabled = state; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + + ret = cpufreq_driver->set_boost(state); + if (ret) { + write_lock_irqsave(&cpufreq_driver_lock, flags); + cpufreq_driver->boost_enabled = !state; + write_unlock_irqrestore(&cpufreq_driver_lock, flags); + + pr_err("%s: Cannot %s BOOST\n", __func__, + state ? "enable" : "disable"); + } + + return ret; +} + +int cpufreq_boost_supported(void) +{ + if (likely(cpufreq_driver)) + return cpufreq_driver->boost_supported; + + return 0; +} +EXPORT_SYMBOL_GPL(cpufreq_boost_supported); + +int cpufreq_boost_enabled(void) +{ + return cpufreq_driver->boost_enabled; +} +EXPORT_SYMBOL_GPL(cpufreq_boost_enabled); + +/********************************************************************* * REGISTER / UNREGISTER CPUFREQ DRIVER * *********************************************************************/ @@ -2223,9 +2317,25 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) cpufreq_driver = driver_data; write_unlock_irqrestore(&cpufreq_driver_lock, flags); + if (cpufreq_boost_supported()) { + /* + * Check if driver provides function to enable boost - + * if not, use cpufreq_boost_set_sw as default + */ + if (!cpufreq_driver->set_boost) + cpufreq_driver->set_boost = cpufreq_boost_set_sw; + + ret = cpufreq_sysfs_create_file(&boost.attr); + if (ret) { + pr_err("%s: cannot register global BOOST sysfs file\n", + __func__); + goto err_null_driver; + } + } + ret = subsys_interface_register(&cpufreq_interface); if (ret) - goto err_null_driver; + goto err_boost_unreg; if (!(cpufreq_driver->flags & CPUFREQ_STICKY)) { int i; @@ -2252,6 +2362,9 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data) return 0; err_if_unreg: subsys_interface_unregister(&cpufreq_interface); +err_boost_unreg: + if (cpufreq_boost_supported()) + cpufreq_sysfs_remove_file(&boost.attr); err_null_driver: write_lock_irqsave(&cpufreq_driver_lock, flags); cpufreq_driver = NULL; @@ -2278,6 +2391,9 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver) pr_debug("unregistering driver %s\n", driver->name); subsys_interface_unregister(&cpufreq_interface); + if (cpufreq_boost_supported()) + cpufreq_sysfs_remove_file(&boost.attr); + unregister_hotcpu_notifier(&cpufreq_cpu_notifier); down_write(&cpufreq_rwsem); diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index a8ac042..8e54f97 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -32,6 +32,10 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, continue; } + if (!cpufreq_boost_enabled() + && table[i].driver_data == CPUFREQ_BOOST_FREQ) + continue; + pr_debug("table entry %u: %u kHz, %u driver_data\n", i, freq, table[i].driver_data); if (freq < min_freq) @@ -204,7 +208,8 @@ static DEFINE_PER_CPU(struct cpufreq_frequency_table *, cpufreq_show_table); /** * show_available_freqs - show available frequencies for the specified CPU */ -static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf) +static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf, + bool show_boost) { unsigned int i = 0; unsigned int cpu = policy->cpu; @@ -219,6 +224,20 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf) for (i = 0; (table[i].frequency != CPUFREQ_TABLE_END); i++) { if (table[i].frequency == CPUFREQ_ENTRY_INVALID) continue; + /* + * show_boost = true and driver_data = BOOST freq + * display BOOST freqs + * + * show_boost = false and driver_data = BOOST freq + * show_boost = true and driver_data != BOOST freq + * continue - do not display anything + * + * show_boost = false and driver_data != BOOST freq + * display NON BOOST freqs + */ + if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ)) + continue; + count += sprintf(&buf[count], "%d ", table[i].frequency); } count += sprintf(&buf[count], "\n"); @@ -227,16 +246,39 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf) } -struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { - .attr = { .name = "scaling_available_frequencies", - .mode = 0444, - }, - .show = show_available_freqs, -}; +#define cpufreq_attr_available_freq(_name) \ +struct freq_attr cpufreq_freq_attr_##_name##_freqs = \ +__ATTR_RO(_name##_frequencies) + +/** + * show_scaling_available_frequencies - show available normal frequencies for + * the specified CPU + */ +static ssize_t scaling_available_frequencies_show(struct cpufreq_policy *policy, + char *buf) +{ + return show_available_freqs(policy, buf, false); +} +cpufreq_attr_available_freq(scaling_available); EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_available_freqs); +/** + * show_available_boost_freqs - show available boost frequencies for + * the specified CPU + */ +static ssize_t scaling_boost_frequencies_show(struct cpufreq_policy *policy, + char *buf) +{ + return show_available_freqs(policy, buf, true); +} +cpufreq_attr_available_freq(scaling_boost); +EXPORT_SYMBOL_GPL(cpufreq_freq_attr_scaling_boost_freqs); + struct freq_attr *cpufreq_generic_attr[] = { &cpufreq_freq_attr_scaling_available_freqs, +#ifdef CONFIG_CPU_FREQ_BOOST_SW + &cpufreq_freq_attr_scaling_boost_freqs, +#endif NULL, }; EXPORT_SYMBOL_GPL(cpufreq_generic_attr); -- cgit v1.1 From cfc9c8ed03e4d908f2388af8815f44c87b503aaf Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:50 +0100 Subject: acpi-cpufreq: Adjust the code to use the common boost attribute Modify acpi-cpufreq's hardware-based boost solution to work with the common cpufreq boost framework. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Viresh Kumar [rjw: Subject and changelog] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/acpi-cpufreq.c | 86 ++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 57 deletions(-) (limited to 'drivers') diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index caf41eb..79e5608 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -80,7 +80,6 @@ static struct acpi_processor_performance __percpu *acpi_perf_data; static struct cpufreq_driver acpi_cpufreq_driver; static unsigned int acpi_pstate_strict; -static bool boost_enabled, boost_supported; static struct msr __percpu *msrs; static bool boost_state(unsigned int cpu) @@ -133,49 +132,16 @@ static void boost_set_msrs(bool enable, const struct cpumask *cpumask) wrmsr_on_cpus(cpumask, msr_addr, msrs); } -static ssize_t _store_boost(const char *buf, size_t count) +static int _store_boost(int val) { - int ret; - unsigned long val = 0; - - if (!boost_supported) - return -EINVAL; - - ret = kstrtoul(buf, 10, &val); - if (ret || (val > 1)) - return -EINVAL; - - if ((val && boost_enabled) || (!val && !boost_enabled)) - return count; - get_online_cpus(); - boost_set_msrs(val, cpu_online_mask); - put_online_cpus(); - - boost_enabled = val; pr_debug("Core Boosting %sabled.\n", val ? "en" : "dis"); - return count; -} - -static ssize_t store_global_boost(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - return _store_boost(buf, count); -} - -static ssize_t show_global_boost(struct kobject *kobj, - struct attribute *attr, char *buf) -{ - return sprintf(buf, "%u\n", boost_enabled); + return 0; } -static struct global_attr global_boost = __ATTR(boost, 0644, - show_global_boost, - store_global_boost); - static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) { struct acpi_cpufreq_data *data = per_cpu(acfreq_data, policy->cpu); @@ -186,15 +152,32 @@ static ssize_t show_freqdomain_cpus(struct cpufreq_policy *policy, char *buf) cpufreq_freq_attr_ro(freqdomain_cpus); #ifdef CONFIG_X86_ACPI_CPUFREQ_CPB +static ssize_t store_boost(const char *buf, size_t count) +{ + int ret; + unsigned long val = 0; + + if (!acpi_cpufreq_driver.boost_supported) + return -EINVAL; + + ret = kstrtoul(buf, 10, &val); + if (ret || (val > 1)) + return -EINVAL; + + _store_boost((int) val); + + return count; +} + static ssize_t store_cpb(struct cpufreq_policy *policy, const char *buf, size_t count) { - return _store_boost(buf, count); + return store_boost(buf, count); } static ssize_t show_cpb(struct cpufreq_policy *policy, char *buf) { - return sprintf(buf, "%u\n", boost_enabled); + return sprintf(buf, "%u\n", acpi_cpufreq_driver.boost_enabled); } cpufreq_freq_attr_rw(cpb); @@ -554,7 +537,7 @@ static int boost_notify(struct notifier_block *nb, unsigned long action, switch (action) { case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: - boost_set_msrs(boost_enabled, cpumask); + boost_set_msrs(acpi_cpufreq_driver.boost_enabled, cpumask); break; case CPU_DOWN_PREPARE: @@ -911,6 +894,7 @@ static struct cpufreq_driver acpi_cpufreq_driver = { .resume = acpi_cpufreq_resume, .name = "acpi-cpufreq", .attr = acpi_cpufreq_attr, + .set_boost = _store_boost, }; static void __init acpi_cpufreq_boost_init(void) @@ -921,33 +905,22 @@ static void __init acpi_cpufreq_boost_init(void) if (!msrs) return; - boost_supported = true; - boost_enabled = boost_state(0); - + acpi_cpufreq_driver.boost_supported = true; + acpi_cpufreq_driver.boost_enabled = boost_state(0); get_online_cpus(); /* Force all MSRs to the same value */ - boost_set_msrs(boost_enabled, cpu_online_mask); + boost_set_msrs(acpi_cpufreq_driver.boost_enabled, + cpu_online_mask); register_cpu_notifier(&boost_nb); put_online_cpus(); - } else - global_boost.attr.mode = 0444; - - /* We create the boost file in any case, though for systems without - * hardware support it will be read-only and hardwired to return 0. - */ - if (cpufreq_sysfs_create_file(&(global_boost.attr))) - pr_warn(PFX "could not register global boost sysfs file\n"); - else - pr_debug("registered global boost sysfs file\n"); + } } static void __exit acpi_cpufreq_boost_exit(void) { - cpufreq_sysfs_remove_file(&(global_boost.attr)); - if (msrs) { unregister_cpu_notifier(&boost_nb); @@ -993,12 +966,11 @@ static int __init acpi_cpufreq_init(void) *iter = &cpb; } #endif + acpi_cpufreq_boost_init(); ret = cpufreq_register_driver(&acpi_cpufreq_driver); if (ret) free_acpi_perf_data(); - else - acpi_cpufreq_boost_init(); return ret; } -- cgit v1.1 From 2fb4719b25602a03fb6c7da77d029db03103663d Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:51 +0100 Subject: cpufreq / boost: Kconfig: Support for software-managed BOOST Add CONFIG_CPU_FREQ_BOOST_SW Kconfig option such that software-managed boost is enabled only after selecting "EXYNOS Frequency Overclocking - Software". It also depends on the thermal subsystem to be compiled in, which is necessary for disabling boost and cooling down the device when overheating is detected. Software-managed boost _MUST_ _NOT_ be enabled without thermal subsystem with properly defined overheating temperature thresholds. This option doesn't affect the x86's hardware-driven boost support in the acpi-cpufreq driver. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Viresh Kumar [rjw: Subject and changelog] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig | 4 ++++ drivers/cpufreq/Kconfig.arm | 15 +++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index 386dbc9..d100926 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -20,6 +20,10 @@ if CPU_FREQ config CPU_FREQ_GOV_COMMON bool +config CPU_FREQ_BOOST_SW + bool + depends on THERMAL + config CPU_FREQ_STAT tristate "CPU frequency translation statistics" default y diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 0468ad1..3129749 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -66,6 +66,21 @@ config ARM_EXYNOS5440_CPUFREQ If in doubt, say N. +config ARM_EXYNOS_CPU_FREQ_BOOST_SW + bool "EXYNOS Frequency Overclocking - Software" + depends on ARM_EXYNOS_CPUFREQ + select CPU_FREQ_BOOST_SW + select EXYNOS_THERMAL + help + This driver supports software managed overclocking (BOOST). + It allows usage of special frequencies for Samsung Exynos + processors if thermal conditions are appropriate. + + It reguires, for safe operation, thermal framework with properly + defined trip points. + + If in doubt, say N. + config ARM_HIGHBANK_CPUFREQ tristate "Calxeda Highbank-based" depends on ARCH_HIGHBANK -- cgit v1.1 From c683c2c96315d4bfed40f96d6fb3d35513f74632 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:52 +0100 Subject: cpufreq: exynos: Extend Exynos cpufreq driver to support boost The cpufreq_driver's boost_supported flag is true only when boost support is explicitly enabled. Boost related attributes are exported only under the same condition. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Viresh Kumar Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/exynos-cpufreq.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 4ee3804..fcd2914 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -223,6 +223,9 @@ static struct cpufreq_driver exynos_driver = { .exit = cpufreq_generic_exit, .name = "exynos_cpufreq", .attr = cpufreq_generic_attr, +#ifdef CONFIG_ARM_EXYNOS_CPU_FREQ_BOOST_SW + .boost_supported = true, +#endif #ifdef CONFIG_PM .suspend = exynos_cpufreq_suspend, .resume = exynos_cpufreq_resume, -- cgit v1.1 From d2228b91fc7709fddeb34450c0ce059fe0a19c60 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:54 +0100 Subject: cpufreq: exynos4x12: Change L0 driver data to CPUFREQ_BOOST_FREQ Add a special driver data flag (CPUFREQ_BOOST_FREQ) to indicate a frequency that can be only enabled for BOOST mode. This frequency will be used only for limited time, since running with it for too long may cause the target device to overheat. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Viresh Kumar [rjw: Changelog] Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/exynos4x12-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index efad5e6..77ae129 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -32,7 +32,7 @@ static unsigned int exynos4x12_volt_table[] = { }; static struct cpufreq_frequency_table exynos4x12_freq_table[] = { - {L0, CPUFREQ_ENTRY_INVALID}, + {CPUFREQ_BOOST_FREQ, 1500 * 1000}, {L1, 1400 * 1000}, {L2, 1300 * 1000}, {L3, 1200 * 1000}, -- cgit v1.1 From 4f11b85a5f2cc279860da3b9977a9586ff2df167 Mon Sep 17 00:00:00 2001 From: Lukasz Majewski Date: Fri, 20 Dec 2013 15:24:55 +0100 Subject: thermal: exynos: boost: Automatic enable/disable of BOOST feature (at Exynos4412) This patch provides auto disable/enable operation for boost. It uses already present thermal infrastructure to provide BOOST hysteresis. The TMU data is modified to work properly with or without BOOST. Hence, the two first trip points with corresponding clip frequencies are adjusted. The first one is reduced from 85 to 70 degrees and the clip frequency is increased to 1.4 GHz from 800 MHz. This trip point is in fact responsible for providing BOOST hysteresis. When temperature exceeds 70 deg, the maximal non BOOST frequency for Exynos4412 is imposed. Since the first trigger level has been "stolen" for BOOST, the second one needs to be a compromise for the previously used two for non BOOST configuration. The 95 deg with modified clip freq (to 400 MHz) should provide a good balance between cooling down the overheated device and throughput on an acceptable level. Two last trigger levels are not modified since, they cause platform shutdown on emergency overheat to happen. The third trip point passage results in SW managed shut down of the system. If the last trip point is crossed, the PMU HW generates the power off signal. Signed-off-by: Lukasz Majewski Signed-off-by: Myungjoo Ham Acked-by: Eduardo Valentin Reviewed-by: Zhang Rui [rjw: Changelog] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/samsung/exynos_tmu_data.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/thermal/samsung/exynos_tmu_data.c b/drivers/thermal/samsung/exynos_tmu_data.c index 073c292..476b768 100644 --- a/drivers/thermal/samsung/exynos_tmu_data.c +++ b/drivers/thermal/samsung/exynos_tmu_data.c @@ -131,8 +131,8 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = { #define EXYNOS4412_TMU_DATA \ .threshold_falling = 10, \ - .trigger_levels[0] = 85, \ - .trigger_levels[1] = 103, \ + .trigger_levels[0] = 70, \ + .trigger_levels[1] = 95, \ .trigger_levels[2] = 110, \ .trigger_levels[3] = 120, \ .trigger_enable[0] = true, \ @@ -155,12 +155,12 @@ static const struct exynos_tmu_registers exynos4412_tmu_registers = { .second_point_trim = 85, \ .default_temp_offset = 50, \ .freq_tab[0] = { \ - .freq_clip_max = 800 * 1000, \ - .temp_level = 85, \ + .freq_clip_max = 1400 * 1000, \ + .temp_level = 70, \ }, \ .freq_tab[1] = { \ - .freq_clip_max = 200 * 1000, \ - .temp_level = 103, \ + .freq_clip_max = 400 * 1000, \ + .temp_level = 95, \ }, \ .freq_tab_count = 2, \ .registers = &exynos4412_tmu_registers, \ -- cgit v1.1