From 731e0cc639364646d36981d90ab0b6af12b8face Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Wed, 11 Aug 2010 17:02:43 -0700 Subject: cpufreq: OMAP: cleanup for multi-SoC support, move into drivers/cpufreq Move OMAP cpufreq driver from arch/arm/mach-omap2 into drivers/cpufreq, along with a few cleanups: - generalize support for better handling of different SoCs in the OMAP - use OPP layer instead of OMAP clock internals for frequency table init Signed-off-by: Santosh Shilimkar [khilman@ti.com: move to drivers] Signed-off-by: Kevin Hilman --- arch/arm/plat-omap/Makefile | 1 - arch/arm/plat-omap/cpu-omap.c | 171 ------------------------------------- drivers/cpufreq/Makefile | 1 + drivers/cpufreq/omap-cpufreq.c | 188 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 172 deletions(-) delete mode 100644 arch/arm/plat-omap/cpu-omap.c create mode 100644 drivers/cpufreq/omap-cpufreq.c diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 985262242f25..a53eca33627f 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_ARCH_OMAP4) += omap_device.o obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o -obj-$(CONFIG_CPU_FREQ) += cpu-omap.o obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o diff --git a/arch/arm/plat-omap/cpu-omap.c b/arch/arm/plat-omap/cpu-omap.c deleted file mode 100644 index da4f68dbba1d..000000000000 --- a/arch/arm/plat-omap/cpu-omap.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * linux/arch/arm/plat-omap/cpu-omap.c - * - * CPU frequency scaling for OMAP - * - * Copyright (C) 2005 Nokia Corporation - * Written by Tony Lindgren - * - * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define VERY_HI_RATE 900000000 - -static struct cpufreq_frequency_table *freq_table; - -#ifdef CONFIG_ARCH_OMAP1 -#define MPU_CLK "mpu" -#else -#define MPU_CLK "virt_prcm_set" -#endif - -static struct clk *mpu_clk; - -/* TODO: Add support for SDRAM timing changes */ - -static int omap_verify_speed(struct cpufreq_policy *policy) -{ - if (freq_table) - return cpufreq_frequency_table_verify(policy, freq_table); - - if (policy->cpu) - return -EINVAL; - - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - - policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; - policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - return 0; -} - -static unsigned int omap_getspeed(unsigned int cpu) -{ - unsigned long rate; - - if (cpu) - return 0; - - rate = clk_get_rate(mpu_clk) / 1000; - return rate; -} - -static int omap_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - struct cpufreq_freqs freqs; - int ret = 0; - - /* Ensure desired rate is within allowed range. Some govenors - * (ondemand) will just pass target_freq=0 to get the minimum. */ - if (target_freq < policy->min) - target_freq = policy->min; - if (target_freq > policy->max) - target_freq = policy->max; - - freqs.old = omap_getspeed(0); - freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; - freqs.cpu = 0; - - if (freqs.old == freqs.new) - return ret; - - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); -#ifdef CONFIG_CPU_FREQ_DEBUG - printk(KERN_DEBUG "cpufreq-omap: transition: %u --> %u\n", - freqs.old, freqs.new); -#endif - ret = clk_set_rate(mpu_clk, freqs.new * 1000); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return ret; -} - -static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) -{ - int result = 0; - - mpu_clk = clk_get(NULL, MPU_CLK); - if (IS_ERR(mpu_clk)) - return PTR_ERR(mpu_clk); - - if (policy->cpu != 0) - return -EINVAL; - - policy->cur = policy->min = policy->max = omap_getspeed(0); - - clk_init_cpufreq_table(&freq_table); - if (freq_table) { - result = cpufreq_frequency_table_cpuinfo(policy, freq_table); - if (!result) - cpufreq_frequency_table_get_attr(freq_table, - policy->cpu); - } else { - policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; - policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, - VERY_HI_RATE) / 1000; - } - - /* FIXME: what's the actual transition time? */ - policy->cpuinfo.transition_latency = 300 * 1000; - - return 0; -} - -static int omap_cpu_exit(struct cpufreq_policy *policy) -{ - clk_exit_cpufreq_table(&freq_table); - clk_put(mpu_clk); - return 0; -} - -static struct freq_attr *omap_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver omap_driver = { - .flags = CPUFREQ_STICKY, - .verify = omap_verify_speed, - .target = omap_target, - .get = omap_getspeed, - .init = omap_cpu_init, - .exit = omap_cpu_exit, - .name = "omap", - .attr = omap_cpufreq_attr, -}; - -static int __init omap_cpufreq_init(void) -{ - return cpufreq_register_driver(&omap_driver); -} - -arch_initcall(omap_cpufreq_init); - -/* - * if ever we want to remove this, upon cleanup call: - * - * cpufreq_unregister_driver() - * cpufreq_frequency_table_put_attr() - */ - diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index a48bc02cd765..ce75fcbcca4f 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o +obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o ################################################################################## # PowerPC platform drivers diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c new file mode 100644 index 000000000000..a6b2be7ea5a9 --- /dev/null +++ b/drivers/cpufreq/omap-cpufreq.c @@ -0,0 +1,188 @@ +/* + * CPU frequency scaling for OMAP + * + * Copyright (C) 2005 Nokia Corporation + * Written by Tony Lindgren + * + * Based on cpu-sa1110.c, Copyright (C) 2001 Russell King + * + * Copyright (C) 2007-2011 Texas Instruments, Inc. + * - OMAP3/4 support by Rajendra Nayak, Santosh Shilimkar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#define VERY_HI_RATE 900000000 + +static struct cpufreq_frequency_table *freq_table; +static struct clk *mpu_clk; + +static int omap_verify_speed(struct cpufreq_policy *policy) +{ + if (freq_table) + return cpufreq_frequency_table_verify(policy, freq_table); + + if (policy->cpu) + return -EINVAL; + + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + + policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; + policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; + cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, + policy->cpuinfo.max_freq); + return 0; +} + +static unsigned int omap_getspeed(unsigned int cpu) +{ + unsigned long rate; + + if (cpu) + return 0; + + rate = clk_get_rate(mpu_clk) / 1000; + return rate; +} + +static int omap_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int ret = 0; + struct cpufreq_freqs freqs; + + /* Ensure desired rate is within allowed range. Some govenors + * (ondemand) will just pass target_freq=0 to get the minimum. */ + if (target_freq < policy->min) + target_freq = policy->min; + if (target_freq > policy->max) + target_freq = policy->max; + + freqs.old = omap_getspeed(0); + freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; + freqs.cpu = 0; + + if (freqs.old == freqs.new) + return ret; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + +#ifdef CONFIG_CPU_FREQ_DEBUG + pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); +#endif + + ret = clk_set_rate(mpu_clk, freqs.new * 1000); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return ret; +} + +static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) +{ + int result = 0; + struct device *mpu_dev; + + if (cpu_is_omap24xx()) + mpu_clk = clk_get(NULL, "virt_prcm_set"); + else if (cpu_is_omap34xx()) + mpu_clk = clk_get(NULL, "dpll1_ck"); + else if (cpu_is_omap44xx()) + mpu_clk = clk_get(NULL, "dpll_mpu_ck"); + + if (IS_ERR(mpu_clk)) + return PTR_ERR(mpu_clk); + + if (policy->cpu != 0) + return -EINVAL; + + policy->cur = policy->min = policy->max = omap_getspeed(0); + + mpu_dev = omap2_get_mpuss_device(); + if (!mpu_dev) { + pr_warning("%s: unable to get the mpu device\n", __func__); + return -EINVAL; + } + opp_init_cpufreq_table(mpu_dev, &freq_table); + + if (freq_table) { + result = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (!result) + cpufreq_frequency_table_get_attr(freq_table, + policy->cpu); + } else { + policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; + policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, + VERY_HI_RATE) / 1000; + } + + policy->min = policy->cpuinfo.min_freq; + policy->max = policy->cpuinfo.max_freq; + policy->cur = omap_getspeed(0); + + /* FIXME: what's the actual transition time? */ + policy->cpuinfo.transition_latency = 300 * 1000; + + return 0; +} + +static int omap_cpu_exit(struct cpufreq_policy *policy) +{ + clk_exit_cpufreq_table(&freq_table); + clk_put(mpu_clk); + return 0; +} + +static struct freq_attr *omap_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver omap_driver = { + .flags = CPUFREQ_STICKY, + .verify = omap_verify_speed, + .target = omap_target, + .get = omap_getspeed, + .init = omap_cpu_init, + .exit = omap_cpu_exit, + .name = "omap", + .attr = omap_cpufreq_attr, +}; + +static int __init omap_cpufreq_init(void) +{ + return cpufreq_register_driver(&omap_driver); +} + +static void __exit omap_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&omap_driver); +} + +MODULE_DESCRIPTION("cpufreq driver for OMAP SoCs"); +MODULE_LICENSE("GPL"); +module_init(omap_cpufreq_init); +module_exit(omap_cpufreq_exit); -- cgit v1.2.3-59-g8ed1b From 46c12216c81b470b957d7fdefd8630efc2edddd0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 21 Sep 2011 16:53:00 -0700 Subject: cpufreq: OMAP: Add SMP support for OMAP4+ On OMAP SMP configuartion, both processors share the voltage and clock. So both CPUs needs to be scaled together and hence needs software co-ordination. Also, update lpj with reference value to avoid progressive error. Adjust _both_ the per-cpu loops_per_jiffy and global lpj. Calibrate them with with reference to the initial values to avoid a progressively bigger and bigger error in the value over time. While at this, re-use the notifiers for UP/SMP since on UP machine or UP_ON_SMP policy->cpus mask would contain only the boot CPU. Based on initial SMP support by Santosh Shilimkar. Signed-off-by: Russell King Signed-off-by: Santosh Shilimkar [khilman@ti.com: due to overlap/rework, combined original Santosh patch and Russell's rework] Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 81 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 10 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index a6b2be7ea5a9..1953f9d082ad 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -23,9 +23,11 @@ #include #include #include +#include #include #include +#include #include #include @@ -35,6 +37,16 @@ #define VERY_HI_RATE 900000000 +#ifdef CONFIG_SMP +struct lpj_info { + unsigned long ref; + unsigned int freq; +}; + +static DEFINE_PER_CPU(struct lpj_info, lpj_ref); +static struct lpj_info global_lpj_ref; +#endif + static struct cpufreq_frequency_table *freq_table; static struct clk *mpu_clk; @@ -60,7 +72,7 @@ static unsigned int omap_getspeed(unsigned int cpu) { unsigned long rate; - if (cpu) + if (cpu >= NR_CPUS) return 0; rate = clk_get_rate(mpu_clk) / 1000; @@ -71,7 +83,7 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - int ret = 0; + int i, ret = 0; struct cpufreq_freqs freqs; /* Ensure desired rate is within allowed range. Some govenors @@ -81,22 +93,57 @@ static int omap_target(struct cpufreq_policy *policy, if (target_freq > policy->max) target_freq = policy->max; - freqs.old = omap_getspeed(0); + freqs.old = omap_getspeed(policy->cpu); freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; - freqs.cpu = 0; + freqs.cpu = policy->cpu; if (freqs.old == freqs.new) return ret; - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + /* notifiers */ + for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + } #ifdef CONFIG_CPU_FREQ_DEBUG pr_info("cpufreq-omap: transition: %u --> %u\n", freqs.old, freqs.new); #endif ret = clk_set_rate(mpu_clk, freqs.new * 1000); + freqs.new = omap_getspeed(policy->cpu); + +#ifdef CONFIG_SMP + /* + * Note that loops_per_jiffy is not updated on SMP systems in + * cpufreq driver. So, update the per-CPU loops_per_jiffy value + * on frequency transition. We need to update all dependent CPUs. + */ + for_each_cpu(i, policy->cpus) { + struct lpj_info *lpj = &per_cpu(lpj_ref, i); + if (!lpj->freq) { + lpj->ref = per_cpu(cpu_data, i).loops_per_jiffy; + lpj->freq = freqs.old; + } + + per_cpu(cpu_data, i).loops_per_jiffy = + cpufreq_scale(lpj->ref, lpj->freq, freqs.new); + } - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + /* And don't forget to adjust the global one */ + if (!global_lpj_ref.freq) { + global_lpj_ref.ref = loops_per_jiffy; + global_lpj_ref.freq = freqs.old; + } + loops_per_jiffy = cpufreq_scale(global_lpj_ref.ref, global_lpj_ref.freq, + freqs.new); +#endif + + /* notifiers */ + for_each_cpu(i, policy->cpus) { + freqs.cpu = i; + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } return ret; } @@ -105,6 +152,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) { int result = 0; struct device *mpu_dev; + static cpumask_var_t cpumask; if (cpu_is_omap24xx()) mpu_clk = clk_get(NULL, "virt_prcm_set"); @@ -116,12 +164,12 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) if (IS_ERR(mpu_clk)) return PTR_ERR(mpu_clk); - if (policy->cpu != 0) + if (policy->cpu >= NR_CPUS) return -EINVAL; - policy->cur = policy->min = policy->max = omap_getspeed(0); - + policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); mpu_dev = omap2_get_mpuss_device(); + if (!mpu_dev) { pr_warning("%s: unable to get the mpu device\n", __func__); return -EINVAL; @@ -141,7 +189,20 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; - policy->cur = omap_getspeed(0); + policy->cur = omap_getspeed(policy->cpu); + + /* + * On OMAP SMP configuartion, both processors share the voltage + * and clock. So both CPUs needs to be scaled together and hence + * needs software co-ordination. Use cpufreq affected_cpus + * interface to handle this scenario. Additional is_smp() check + * is to keep SMP_ON_UP build working. + */ + if (is_smp()) { + policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; + cpumask_or(cpumask, cpumask_of(policy->cpu), cpumask); + cpumask_copy(policy->cpus, cpumask); + } /* FIXME: what's the actual transition time? */ policy->cpuinfo.transition_latency = 300 * 1000; -- cgit v1.2.3-59-g8ed1b From ed8ce00c52fb49aca299b79513bbfcee975442bc Mon Sep 17 00:00:00 2001 From: Todd Poynor Date: Tue, 7 Jun 2011 13:57:52 -0700 Subject: cpufreq: OMAP: Enable all CPUs in shared policy mask Enable all CPUs in the shared policy in the CPU init callback. Otherwise, the governor CPUFREQ_GOV_START event is invoked with a policy that only includes the first CPU, leaving other CPUs uninitialized by the governor. Signed-off-by: Todd Poynor Acked-by: Santosh Shilimkar Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 1953f9d082ad..3f5a816a64be 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -152,7 +152,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) { int result = 0; struct device *mpu_dev; - static cpumask_var_t cpumask; if (cpu_is_omap24xx()) mpu_clk = clk_get(NULL, "virt_prcm_set"); @@ -200,8 +199,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) */ if (is_smp()) { policy->shared_type = CPUFREQ_SHARED_TYPE_ANY; - cpumask_or(cpumask, cpumask_of(policy->cpu), cpumask); - cpumask_copy(policy->cpus, cpumask); + cpumask_setall(policy->cpus); } /* FIXME: what's the actual transition time? */ -- cgit v1.2.3-59-g8ed1b From 022ac03b45d6899219539894cff3c7ce5bd990f9 Mon Sep 17 00:00:00 2001 From: Colin Cross Date: Mon, 6 Jun 2011 21:05:29 -0500 Subject: cpufreq: OMAP: notify even with bad boot frequency Sometimes, bootloaders starts up with a frequency which is not in the OPP table. At cpu_init, policy->cur contains the frequency we pick at boot. It is possible that system might have fixed it's boot frequency later on as part of power initialization. After this condition, the first call to omap_target results in the following: omap_getspeed(actual device frequency) != policy->cur(frequency that cpufreq thinks that the system is at), and it is possible that freqs.old == freqs.new (because the governor requested a scale down). We exit without triggering the notifiers in the current code, which does'nt let code which depends on cpufreq_notify_transition to have accurate information as to what the system frequency is. Instead, we do a normal transition if policy->cur is wrong, then, freqs.old will be the actual cpu frequency, freqs.new will be the actual new cpu frequency and all required notifiers have the accurate information. Acked-by: Nishanth Menon Signed-off-by: Colin Cross Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 3f5a816a64be..0a5d95c4f8eb 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -97,7 +97,7 @@ static int omap_target(struct cpufreq_policy *policy, freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; freqs.cpu = policy->cpu; - if (freqs.old == freqs.new) + if (freqs.old == freqs.new && policy->cur == freqs.new) return ret; /* notifiers */ -- cgit v1.2.3-59-g8ed1b From 08ca3e3b8ddf0e75f734d46b31518b97256d2c17 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 25 May 2011 16:38:46 -0700 Subject: cpufreq: OMAP: move clk name decision to init Clk name does'nt need to dynamically detected during clk init. move them off to driver initialization, if we dont have a clk name, there is no point in registering the driver anyways. The actual clk get and put is left at cpu_init and exit functions. Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 0a5d95c4f8eb..3651825e7fb9 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -49,6 +49,7 @@ static struct lpj_info global_lpj_ref; static struct cpufreq_frequency_table *freq_table; static struct clk *mpu_clk; +static char *mpu_clk_name; static int omap_verify_speed(struct cpufreq_policy *policy) { @@ -153,13 +154,7 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) int result = 0; struct device *mpu_dev; - if (cpu_is_omap24xx()) - mpu_clk = clk_get(NULL, "virt_prcm_set"); - else if (cpu_is_omap34xx()) - mpu_clk = clk_get(NULL, "dpll1_ck"); - else if (cpu_is_omap44xx()) - mpu_clk = clk_get(NULL, "dpll_mpu_ck"); - + mpu_clk = clk_get(NULL, mpu_clk_name); if (IS_ERR(mpu_clk)) return PTR_ERR(mpu_clk); @@ -233,6 +228,17 @@ static struct cpufreq_driver omap_driver = { static int __init omap_cpufreq_init(void) { + if (cpu_is_omap24xx()) + mpu_clk_name = "virt_prcm_set"; + else if (cpu_is_omap34xx()) + mpu_clk_name = "dpll1_ck"; + else if (cpu_is_omap44xx()) + mpu_clk_name = "dpll_mpu_ck"; + + if (!mpu_clk_name) { + pr_err("%s: unsupported Silicon?\n", __func__); + return -EINVAL; + } return cpufreq_register_driver(&omap_driver); } -- cgit v1.2.3-59-g8ed1b From a820ffa8fdbcaa4f5fe32e88db58acca27abbc76 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Wed, 25 May 2011 16:38:47 -0700 Subject: cpufreq: OMAP: deny initialization if no mpudev if we do not have mpu_dev we normally fail in cpu_init. It is better to fail driver registration if the devices are not available. Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 3651825e7fb9..dda32fd0343e 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -50,6 +50,7 @@ static struct lpj_info global_lpj_ref; static struct cpufreq_frequency_table *freq_table; static struct clk *mpu_clk; static char *mpu_clk_name; +static struct device *mpu_dev; static int omap_verify_speed(struct cpufreq_policy *policy) { @@ -152,7 +153,6 @@ static int omap_target(struct cpufreq_policy *policy, static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) { int result = 0; - struct device *mpu_dev; mpu_clk = clk_get(NULL, mpu_clk_name); if (IS_ERR(mpu_clk)) @@ -162,12 +162,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) return -EINVAL; policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); - mpu_dev = omap2_get_mpuss_device(); - - if (!mpu_dev) { - pr_warning("%s: unable to get the mpu device\n", __func__); - return -EINVAL; - } opp_init_cpufreq_table(mpu_dev, &freq_table); if (freq_table) { @@ -239,6 +233,13 @@ static int __init omap_cpufreq_init(void) pr_err("%s: unsupported Silicon?\n", __func__); return -EINVAL; } + + mpu_dev = omap2_get_mpuss_device(); + if (!mpu_dev) { + pr_warning("%s: unable to get the mpu device\n", __func__); + return -EINVAL; + } + return cpufreq_register_driver(&omap_driver); } -- cgit v1.2.3-59-g8ed1b From bf2a359d504bca3ef71a65e8759d51af4b17055a Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 May 2011 19:39:17 -0700 Subject: cpufreq: OMAP: dont support !freq_table OMAP2+ all have frequency tables, hence the hacks we had for older silicon do not need to be carried forward. As part of this change, use cpufreq_frequency_table_target to find the best match for frequency requested. Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 67 +++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index dda32fd0343e..eecb0961c6b3 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -35,8 +35,6 @@ #include -#define VERY_HI_RATE 900000000 - #ifdef CONFIG_SMP struct lpj_info { unsigned long ref; @@ -54,20 +52,9 @@ static struct device *mpu_dev; static int omap_verify_speed(struct cpufreq_policy *policy) { - if (freq_table) - return cpufreq_frequency_table_verify(policy, freq_table); - - if (policy->cpu) + if (!freq_table) return -EINVAL; - - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - - policy->min = clk_round_rate(mpu_clk, policy->min * 1000) / 1000; - policy->max = clk_round_rate(mpu_clk, policy->max * 1000) / 1000; - cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq, - policy->cpuinfo.max_freq); - return 0; + return cpufreq_frequency_table_verify(policy, freq_table); } static unsigned int omap_getspeed(unsigned int cpu) @@ -85,18 +72,31 @@ static int omap_target(struct cpufreq_policy *policy, unsigned int target_freq, unsigned int relation) { - int i, ret = 0; + unsigned int i; + int ret = 0; struct cpufreq_freqs freqs; - /* Ensure desired rate is within allowed range. Some govenors - * (ondemand) will just pass target_freq=0 to get the minimum. */ - if (target_freq < policy->min) - target_freq = policy->min; - if (target_freq > policy->max) - target_freq = policy->max; + if (!freq_table) { + dev_err(mpu_dev, "%s: cpu%d: no freq table!\n", __func__, + policy->cpu); + return -EINVAL; + } + + ret = cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &i); + if (ret) { + dev_dbg(mpu_dev, "%s: cpu%d: no freq match for %d(ret=%d)\n", + __func__, policy->cpu, target_freq, ret); + return ret; + } + freqs.new = freq_table[i].frequency; + if (!freqs.new) { + dev_err(mpu_dev, "%s: cpu%d: no match for freq %d\n", __func__, + policy->cpu, target_freq); + return -EINVAL; + } freqs.old = omap_getspeed(policy->cpu); - freqs.new = clk_round_rate(mpu_clk, target_freq * 1000) / 1000; freqs.cpu = policy->cpu; if (freqs.old == freqs.new && policy->cur == freqs.new) @@ -162,19 +162,18 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) return -EINVAL; policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); - opp_init_cpufreq_table(mpu_dev, &freq_table); - - if (freq_table) { - result = cpufreq_frequency_table_cpuinfo(policy, freq_table); - if (!result) - cpufreq_frequency_table_get_attr(freq_table, - policy->cpu); - } else { - policy->cpuinfo.min_freq = clk_round_rate(mpu_clk, 0) / 1000; - policy->cpuinfo.max_freq = clk_round_rate(mpu_clk, - VERY_HI_RATE) / 1000; + result = opp_init_cpufreq_table(mpu_dev, &freq_table); + + if (result) { + dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n", + __func__, policy->cpu, result); + return result; } + result = cpufreq_frequency_table_cpuinfo(policy, freq_table); + if (!result) + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; policy->cur = omap_getspeed(policy->cpu); -- cgit v1.2.3-59-g8ed1b From ffe4f0f115420e3843aa0d8dc1baf31ea5b6fcf2 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 May 2011 19:39:18 -0700 Subject: cpufreq: OMAP: only supports OPP library OMAP2 is the only family using clk_[init|exit]_cpufreq_table, however, the cpufreq code does not currently use clk_init_cpufreq_table. As a result, it is unusuable for OMAP2 and only usable only on platforms using OPP library. Remove the unbalanced clk_exit_cpufreq_table(). Any platforms where OPPs are not availble will fail on init because a freq table will not be properly initialized. Signed-off-by: Nishanth Menon [khilman@ti.com: changelog edits, and graceful failure mode changes] Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index eecb0961c6b3..8f778b9dbb46 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -1,5 +1,5 @@ /* - * CPU frequency scaling for OMAP + * CPU frequency scaling for OMAP using OPP information * * Copyright (C) 2005 Nokia Corporation * Written by Tony Lindgren @@ -198,7 +198,6 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) static int omap_cpu_exit(struct cpufreq_policy *policy) { - clk_exit_cpufreq_table(&freq_table); clk_put(mpu_clk); return 0; } -- cgit v1.2.3-59-g8ed1b From 11e04fdd98f0fd6edf1ad6eccb0db4d2f965c392 Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 May 2011 19:39:19 -0700 Subject: cpufreq: OMAP: put clk if cpu_init failed Release the mpu_clk in fail paths. Reported-by: Todd Poynor Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 8f778b9dbb46..8c5419201ac5 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -158,8 +158,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) if (IS_ERR(mpu_clk)) return PTR_ERR(mpu_clk); - if (policy->cpu >= NR_CPUS) - return -EINVAL; + if (policy->cpu >= NR_CPUS) { + result = -EINVAL; + goto fail_ck; + } policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); result = opp_init_cpufreq_table(mpu_dev, &freq_table); @@ -167,12 +169,14 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) if (result) { dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n", __func__, policy->cpu, result); - return result; + goto fail_ck; } result = cpufreq_frequency_table_cpuinfo(policy, freq_table); if (!result) cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + else + goto fail_ck; policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; @@ -194,6 +198,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.transition_latency = 300 * 1000; return 0; + +fail_ck: + clk_put(mpu_clk); + return result; } static int omap_cpu_exit(struct cpufreq_policy *policy) -- cgit v1.2.3-59-g8ed1b From 1c78217fc8c0983f5768a2d1c17c022f1079751e Mon Sep 17 00:00:00 2001 From: Nishanth Menon Date: Thu, 26 May 2011 19:39:20 -0700 Subject: cpufreq: OMAP: fix freq_table leak We use a single frequency table for multiple CPUs. But, with OMAP4, since we have multiple CPUs, the cpu_init call for CPU1 causes freq_table previously allocated for CPU0 to be overwritten. In addition, we dont free the table on exit path. We solve this by maintaining an atomic type counter to ensure just a single table exists at a given time. Signed-off-by: Nishanth Menon Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index 8c5419201ac5..ad94b4f2892c 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -46,6 +46,7 @@ static struct lpj_info global_lpj_ref; #endif static struct cpufreq_frequency_table *freq_table; +static atomic_t freq_table_users = ATOMIC_INIT(0); static struct clk *mpu_clk; static char *mpu_clk_name; static struct device *mpu_dev; @@ -150,6 +151,12 @@ static int omap_target(struct cpufreq_policy *policy, return ret; } +static inline void freq_table_free(void) +{ + if (atomic_dec_and_test(&freq_table_users)) + opp_free_cpufreq_table(mpu_dev, &freq_table); +} + static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) { int result = 0; @@ -164,7 +171,9 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) } policy->cur = policy->min = policy->max = omap_getspeed(policy->cpu); - result = opp_init_cpufreq_table(mpu_dev, &freq_table); + + if (atomic_inc_return(&freq_table_users) == 1) + result = opp_init_cpufreq_table(mpu_dev, &freq_table); if (result) { dev_err(mpu_dev, "%s: cpu%d: failed creating freq table[%d]\n", @@ -173,10 +182,10 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) } result = cpufreq_frequency_table_cpuinfo(policy, freq_table); - if (!result) - cpufreq_frequency_table_get_attr(freq_table, policy->cpu); - else - goto fail_ck; + if (result) + goto fail_table; + + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; @@ -199,6 +208,8 @@ static int __cpuinit omap_cpu_init(struct cpufreq_policy *policy) return 0; +fail_table: + freq_table_free(); fail_ck: clk_put(mpu_clk); return result; @@ -206,6 +217,7 @@ fail_ck: static int omap_cpu_exit(struct cpufreq_policy *policy) { + freq_table_free(); clk_put(mpu_clk); return 0; } -- cgit v1.2.3-59-g8ed1b From c1b547bc222f4027d9394b6bd8f4a6bb0bd7b1b4 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 30 Sep 2011 10:41:26 -0700 Subject: cpufreq: OMAP: fixup for omap_device changes, include Minor fixups to work starting with v3.2: - use the new omap_device API for getting a device by name. - need to include Signed-off-by: Kevin Hilman --- drivers/cpufreq/omap-cpufreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index ad94b4f2892c..5d04c57aae30 100644 --- a/drivers/cpufreq/omap-cpufreq.c +++ b/drivers/cpufreq/omap-cpufreq.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include @@ -252,7 +254,7 @@ static int __init omap_cpufreq_init(void) return -EINVAL; } - mpu_dev = omap2_get_mpuss_device(); + mpu_dev = omap_device_get_by_hwmod_name("mpu"); if (!mpu_dev) { pr_warning("%s: unable to get the mpu device\n", __func__); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From a6a434124457fe64bb3980ceb2170505207db6e5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Dec 2011 18:22:01 +0000 Subject: [CPUFREQ] s3c64xx: Use pr_fmt() for consistent log messages They're already consistent but it saves remembering to do so. Signed-off-by: Mark Brown Signed-off-by: Dave Jones --- drivers/cpufreq/s3c64xx-cpufreq.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index 3475f65aeec6..a5e72cb5f53c 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -8,6 +8,8 @@ * published by the Free Software Foundation. */ +#define pr_fmt(fmt) "cpufreq: " fmt + #include #include #include @@ -91,7 +93,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, if (freqs.old == freqs.new) return 0; - pr_debug("cpufreq: Transition %d-%dkHz\n", freqs.old, freqs.new); + pr_debug("Transition %d-%dkHz\n", freqs.old, freqs.new); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -101,7 +103,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { - pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n", + pr_err("Failed to set VDDARM for %dkHz: %d\n", freqs.new, ret); goto err; } @@ -110,7 +112,7 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, ret = clk_set_rate(armclk, freqs.new * 1000); if (ret < 0) { - pr_err("cpufreq: Failed to set rate %dkHz: %d\n", + pr_err("Failed to set rate %dkHz: %d\n", freqs.new, ret); goto err; } @@ -123,14 +125,14 @@ static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy, dvfs->vddarm_min, dvfs->vddarm_max); if (ret != 0) { - pr_err("cpufreq: Failed to set VDDARM for %dkHz: %d\n", + pr_err("Failed to set VDDARM for %dkHz: %d\n", freqs.new, ret); goto err_clk; } } #endif - pr_debug("cpufreq: Set actual frequency %lukHz\n", + pr_debug("Set actual frequency %lukHz\n", clk_get_rate(armclk) / 1000); return 0; @@ -153,7 +155,7 @@ static void __init s3c64xx_cpufreq_config_regulator(void) count = regulator_count_voltages(vddarm); if (count < 0) { - pr_err("cpufreq: Unable to check supported voltages\n"); + pr_err("Unable to check supported voltages\n"); } freq = s3c64xx_freq_table; @@ -171,7 +173,7 @@ static void __init s3c64xx_cpufreq_config_regulator(void) } if (!found) { - pr_debug("cpufreq: %dkHz unsupported by regulator\n", + pr_debug("%dkHz unsupported by regulator\n", freq->frequency); freq->frequency = CPUFREQ_ENTRY_INVALID; } @@ -194,13 +196,13 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) return -EINVAL; if (s3c64xx_freq_table == NULL) { - pr_err("cpufreq: No frequency information for this CPU\n"); + pr_err("No frequency information for this CPU\n"); return -ENODEV; } armclk = clk_get(NULL, "armclk"); if (IS_ERR(armclk)) { - pr_err("cpufreq: Unable to obtain ARMCLK: %ld\n", + pr_err("Unable to obtain ARMCLK: %ld\n", PTR_ERR(armclk)); return PTR_ERR(armclk); } @@ -209,12 +211,19 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) vddarm = regulator_get(NULL, "vddarm"); if (IS_ERR(vddarm)) { ret = PTR_ERR(vddarm); - pr_err("cpufreq: Failed to obtain VDDARM: %d\n", ret); - pr_err("cpufreq: Only frequency scaling available\n"); + pr_err("Failed to obtain VDDARM: %d\n", ret); + pr_err("Only frequency scaling available\n"); vddarm = NULL; } else { s3c64xx_cpufreq_config_regulator(); } + + vddint = regulator_get(NULL, "vddint"); + if (IS_ERR(vddint)) { + ret = PTR_ERR(vddint); + pr_err("Failed to obtain VDDINT: %d\n", ret); + vddint = NULL; + } #endif freq = s3c64xx_freq_table; @@ -225,7 +234,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) r = clk_round_rate(armclk, freq->frequency * 1000); r /= 1000; if (r != freq->frequency) { - pr_debug("cpufreq: %dkHz unsupported by clock\n", + pr_debug("%dkHz unsupported by clock\n", freq->frequency); freq->frequency = CPUFREQ_ENTRY_INVALID; } @@ -248,7 +257,7 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy) ret = cpufreq_frequency_table_cpuinfo(policy, s3c64xx_freq_table); if (ret != 0) { - pr_err("cpufreq: Failed to configure frequency table: %d\n", + pr_err("Failed to configure frequency table: %d\n", ret); regulator_put(vddarm); clk_put(armclk); -- cgit v1.2.3-59-g8ed1b From c8c430e2f65adf124b3a2b6cfffa4dfc8a6e49c2 Mon Sep 17 00:00:00 2001 From: Jaecheol Lee Date: Wed, 7 Dec 2011 11:43:42 +0900 Subject: [CPUFREQ] EXYNOS4210: Remove code about bus on cpufreq This patch removes code for bus on cpufreq because the code for bus frequency changing moves to busfreq driver. So code about bus on cpufreq is not necessary. Signed-off-by: Jaecheol Lee Signed-off-by: Jongpill Lee Signed-off-by: Kukjin Kim Signed-off-by: Dave Jones --- drivers/cpufreq/exynos4210-cpufreq.c | 174 +---------------------------------- 1 file changed, 1 insertion(+), 173 deletions(-) diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index ab9741fab92e..578956c5ee71 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -33,21 +33,13 @@ static struct clk *mout_mpll; static struct clk *mout_apll; static struct regulator *arm_regulator; -static struct regulator *int_regulator; static struct cpufreq_freqs freqs; -static unsigned int memtype; static unsigned int locking_frequency; static bool frequency_locked; static DEFINE_MUTEX(cpufreq_lock); -enum exynos4_memory_type { - DDR2 = 4, - LPDDR2, - DDR3, -}; - enum cpufreq_level_index { L0, L1, L2, L3, CPUFREQ_LEVEL_END, }; @@ -99,87 +91,24 @@ static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = { { 3, 0 }, }; -static unsigned int clkdiv_dmc0[CPUFREQ_LEVEL_END][8] = { - /* - * Clock divider value for following - * { DIVACP, DIVACP_PCLK, DIVDPHY, DIVDMC, DIVDMCD - * DIVDMCP, DIVCOPY2, DIVCORE_TIMERS } - */ - - /* DMC L0: 400MHz */ - { 3, 1, 1, 1, 1, 1, 3, 1 }, - - /* DMC L1: 400MHz */ - { 3, 1, 1, 1, 1, 1, 3, 1 }, - - /* DMC L2: 266.7MHz */ - { 7, 1, 1, 2, 1, 1, 3, 1 }, - - /* DMC L3: 200MHz */ - { 7, 1, 1, 3, 1, 1, 3, 1 }, -}; - -static unsigned int clkdiv_top[CPUFREQ_LEVEL_END][5] = { - /* - * Clock divider value for following - * { DIVACLK200, DIVACLK100, DIVACLK160, DIVACLK133, DIVONENAND } - */ - - /* ACLK200 L0: 200MHz */ - { 3, 7, 4, 5, 1 }, - - /* ACLK200 L1: 200MHz */ - { 3, 7, 4, 5, 1 }, - - /* ACLK200 L2: 160MHz */ - { 4, 7, 5, 7, 1 }, - - /* ACLK200 L3: 133.3MHz */ - { 5, 7, 7, 7, 1 }, -}; - -static unsigned int clkdiv_lr_bus[CPUFREQ_LEVEL_END][2] = { - /* - * Clock divider value for following - * { DIVGDL/R, DIVGPL/R } - */ - - /* ACLK_GDL/R L0: 200MHz */ - { 3, 1 }, - - /* ACLK_GDL/R L1: 200MHz */ - { 3, 1 }, - - /* ACLK_GDL/R L2: 160MHz */ - { 4, 1 }, - - /* ACLK_GDL/R L3: 133.3MHz */ - { 5, 1 }, -}; - struct cpufreq_voltage_table { unsigned int index; /* any */ unsigned int arm_volt; /* uV */ - unsigned int int_volt; }; static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = { { .index = L0, .arm_volt = 1200000, - .int_volt = 1100000, }, { .index = L1, .arm_volt = 1100000, - .int_volt = 1100000, }, { .index = L2, .arm_volt = 1000000, - .int_volt = 1000000, }, { .index = L3, .arm_volt = 900000, - .int_volt = 1000000, }, }; @@ -248,80 +177,6 @@ static void exynos4_set_clkdiv(unsigned int div_index) do { tmp = __raw_readl(S5P_CLKDIV_STATCPU1); } while (tmp & 0x11); - - /* Change Divider - DMC0 */ - - tmp = __raw_readl(S5P_CLKDIV_DMC0); - - tmp &= ~(S5P_CLKDIV_DMC0_ACP_MASK | S5P_CLKDIV_DMC0_ACPPCLK_MASK | - S5P_CLKDIV_DMC0_DPHY_MASK | S5P_CLKDIV_DMC0_DMC_MASK | - S5P_CLKDIV_DMC0_DMCD_MASK | S5P_CLKDIV_DMC0_DMCP_MASK | - S5P_CLKDIV_DMC0_COPY2_MASK | S5P_CLKDIV_DMC0_CORETI_MASK); - - tmp |= ((clkdiv_dmc0[div_index][0] << S5P_CLKDIV_DMC0_ACP_SHIFT) | - (clkdiv_dmc0[div_index][1] << S5P_CLKDIV_DMC0_ACPPCLK_SHIFT) | - (clkdiv_dmc0[div_index][2] << S5P_CLKDIV_DMC0_DPHY_SHIFT) | - (clkdiv_dmc0[div_index][3] << S5P_CLKDIV_DMC0_DMC_SHIFT) | - (clkdiv_dmc0[div_index][4] << S5P_CLKDIV_DMC0_DMCD_SHIFT) | - (clkdiv_dmc0[div_index][5] << S5P_CLKDIV_DMC0_DMCP_SHIFT) | - (clkdiv_dmc0[div_index][6] << S5P_CLKDIV_DMC0_COPY2_SHIFT) | - (clkdiv_dmc0[div_index][7] << S5P_CLKDIV_DMC0_CORETI_SHIFT)); - - __raw_writel(tmp, S5P_CLKDIV_DMC0); - - do { - tmp = __raw_readl(S5P_CLKDIV_STAT_DMC0); - } while (tmp & 0x11111111); - - /* Change Divider - TOP */ - - tmp = __raw_readl(S5P_CLKDIV_TOP); - - tmp &= ~(S5P_CLKDIV_TOP_ACLK200_MASK | S5P_CLKDIV_TOP_ACLK100_MASK | - S5P_CLKDIV_TOP_ACLK160_MASK | S5P_CLKDIV_TOP_ACLK133_MASK | - S5P_CLKDIV_TOP_ONENAND_MASK); - - tmp |= ((clkdiv_top[div_index][0] << S5P_CLKDIV_TOP_ACLK200_SHIFT) | - (clkdiv_top[div_index][1] << S5P_CLKDIV_TOP_ACLK100_SHIFT) | - (clkdiv_top[div_index][2] << S5P_CLKDIV_TOP_ACLK160_SHIFT) | - (clkdiv_top[div_index][3] << S5P_CLKDIV_TOP_ACLK133_SHIFT) | - (clkdiv_top[div_index][4] << S5P_CLKDIV_TOP_ONENAND_SHIFT)); - - __raw_writel(tmp, S5P_CLKDIV_TOP); - - do { - tmp = __raw_readl(S5P_CLKDIV_STAT_TOP); - } while (tmp & 0x11111); - - /* Change Divider - LEFTBUS */ - - tmp = __raw_readl(S5P_CLKDIV_LEFTBUS); - - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | - (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, S5P_CLKDIV_LEFTBUS); - - do { - tmp = __raw_readl(S5P_CLKDIV_STAT_LEFTBUS); - } while (tmp & 0x11); - - /* Change Divider - RIGHTBUS */ - - tmp = __raw_readl(S5P_CLKDIV_RIGHTBUS); - - tmp &= ~(S5P_CLKDIV_BUS_GDLR_MASK | S5P_CLKDIV_BUS_GPLR_MASK); - - tmp |= ((clkdiv_lr_bus[div_index][0] << S5P_CLKDIV_BUS_GDLR_SHIFT) | - (clkdiv_lr_bus[div_index][1] << S5P_CLKDIV_BUS_GPLR_SHIFT)); - - __raw_writel(tmp, S5P_CLKDIV_RIGHTBUS); - - do { - tmp = __raw_readl(S5P_CLKDIV_STAT_RIGHTBUS); - } while (tmp & 0x11); } static void exynos4_set_apll(unsigned int index) @@ -410,7 +265,7 @@ static int exynos4_target(struct cpufreq_policy *policy, unsigned int relation) { unsigned int index, old_index; - unsigned int arm_volt, int_volt; + unsigned int arm_volt; int err = -EINVAL; freqs.old = exynos4_getspeed(policy->cpu); @@ -440,7 +295,6 @@ static int exynos4_target(struct cpufreq_policy *policy, /* get the voltage value */ arm_volt = exynos4_volt_table[index].arm_volt; - int_volt = exynos4_volt_table[index].int_volt; cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); @@ -448,7 +302,6 @@ static int exynos4_target(struct cpufreq_policy *policy, if (freqs.new > freqs.old) { /* Voltage up */ regulator_set_voltage(arm_regulator, arm_volt, arm_volt); - regulator_set_voltage(int_regulator, int_volt, int_volt); } /* Clock Configuration Procedure */ @@ -458,7 +311,6 @@ static int exynos4_target(struct cpufreq_policy *policy, if (freqs.new < freqs.old) { /* Voltage down */ regulator_set_voltage(arm_regulator, arm_volt, arm_volt); - regulator_set_voltage(int_regulator, int_volt, int_volt); } cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); @@ -636,27 +488,6 @@ static int __init exynos4_cpufreq_init(void) goto out; } - int_regulator = regulator_get(NULL, "vdd_int"); - if (IS_ERR(int_regulator)) { - printk(KERN_ERR "failed to get resource %s\n", "vdd_int"); - goto out; - } - - /* - * Check DRAM type. - * Because DVFS level is different according to DRAM type. - */ - memtype = __raw_readl(S5P_VA_DMC0 + S5P_DMC0_MEMCON_OFFSET); - memtype = (memtype >> S5P_DMC0_MEMTYPE_SHIFT); - memtype &= S5P_DMC0_MEMTYPE_MASK; - - if ((memtype < DDR2) && (memtype > DDR3)) { - printk(KERN_ERR "%s: wrong memtype= 0x%x\n", __func__, memtype); - goto out; - } else { - printk(KERN_DEBUG "%s: memtype= 0x%x\n", __func__, memtype); - } - register_pm_notifier(&exynos4_cpufreq_nb); return cpufreq_register_driver(&exynos4_driver); @@ -677,9 +508,6 @@ out: if (!IS_ERR(arm_regulator)) regulator_put(arm_regulator); - if (!IS_ERR(int_regulator)) - regulator_put(int_regulator); - printk(KERN_ERR "%s: failed initialization\n", __func__); return -EINVAL; -- cgit v1.2.3-59-g8ed1b From ba9d78031116a216d0e53aa629a584932e813375 Mon Sep 17 00:00:00 2001 From: Jaecheol Lee Date: Wed, 7 Dec 2011 11:43:56 +0900 Subject: [CPUFREQ] EXYNOS4210: Update frequency table for cpu divider This patch is changes frequency table for cpu divider for stable frequency. Signed-off-by: Jaecheol Lee Signed-off-by: Jongpill Lee Signed-off-by: Kukjin Kim Signed-off-by: Dave Jones --- drivers/cpufreq/exynos4210-cpufreq.c | 69 +++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 578956c5ee71..ba579e0a5c3f 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -41,14 +41,15 @@ static bool frequency_locked; static DEFINE_MUTEX(cpufreq_lock); enum cpufreq_level_index { - L0, L1, L2, L3, CPUFREQ_LEVEL_END, + L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END, }; static struct cpufreq_frequency_table exynos4_freq_table[] = { - {L0, 1000*1000}, - {L1, 800*1000}, - {L2, 400*1000}, - {L3, 100*1000}, + {L0, 1200*1000}, + {L1, 1000*1000}, + {L2, 800*1000}, + {L3, 500*1000}, + {L4, 200*1000}, {0, CPUFREQ_TABLE_END}, }; @@ -59,17 +60,20 @@ static unsigned int clkdiv_cpu0[CPUFREQ_LEVEL_END][7] = { * DIVATB, DIVPCLK_DBG, DIVAPLL } */ - /* ARM L0: 1000MHz */ - { 0, 3, 7, 3, 3, 0, 1 }, + /* ARM L0: 1200MHz */ + { 0, 3, 7, 3, 4, 1, 7 }, - /* ARM L1: 800MHz */ - { 0, 3, 7, 3, 3, 0, 1 }, + /* ARM L1: 1000MHz */ + { 0, 3, 7, 3, 4, 1, 7 }, - /* ARM L2: 400MHz */ - { 0, 1, 3, 1, 3, 0, 1 }, + /* ARM L2: 800MHz */ + { 0, 3, 7, 3, 3, 1, 7 }, - /* ARM L3: 100MHz */ - { 0, 0, 1, 0, 3, 1, 1 }, + /* ARM L3: 500MHz */ + { 0, 3, 7, 3, 3, 1, 7 }, + + /* ARM L4: 200MHz */ + { 0, 1, 3, 1, 3, 1, 0 }, }; static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = { @@ -78,16 +82,19 @@ static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = { * { DIVCOPY, DIVHPM } */ - /* ARM L0: 1000MHz */ - { 3, 0 }, + /* ARM L0: 1200MHz */ + { 5, 0 }, + + /* ARM L1: 1000MHz */ + { 4, 0 }, - /* ARM L1: 800MHz */ + /* ARM L2: 800MHz */ { 3, 0 }, - /* ARM L2: 400MHz */ + /* ARM L3: 500MHz */ { 3, 0 }, - /* ARM L3: 100MHz */ + /* ARM L4: 200MHz */ { 3, 0 }, }; @@ -99,31 +106,37 @@ struct cpufreq_voltage_table { static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = { { .index = L0, - .arm_volt = 1200000, + .arm_volt = 1350000, }, { .index = L1, - .arm_volt = 1100000, + .arm_volt = 1300000, }, { .index = L2, - .arm_volt = 1000000, + .arm_volt = 1200000, }, { .index = L3, - .arm_volt = 900000, + .arm_volt = 1100000, + }, { + .index = L4, + .arm_volt = 1050000, }, }; static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = { - /* APLL FOUT L0: 1000MHz */ + /* APLL FOUT L0: 1200MHz */ + ((150 << 16) | (3 << 8) | 1), + + /* APLL FOUT L1: 1000MHz */ ((250 << 16) | (6 << 8) | 1), - /* APLL FOUT L1: 800MHz */ + /* APLL FOUT L2: 800MHz */ ((200 << 16) | (6 << 8) | 1), - /* APLL FOUT L2 : 400MHz */ - ((200 << 16) | (6 << 8) | 2), + /* APLL FOUT L3: 500MHz */ + ((250 << 16) | (6 << 8) | 2), - /* APLL FOUT L3: 100MHz */ - ((200 << 16) | (6 << 8) | 4), + /* APLL FOUT L4: 200MHz */ + ((200 << 16) | (6 << 8) | 3), }; static int exynos4_verify_speed(struct cpufreq_policy *policy) -- cgit v1.2.3-59-g8ed1b From 27f805dcb058178444a9a4e380c7dcb2fe2d3a94 Mon Sep 17 00:00:00 2001 From: Jaecheol Lee Date: Wed, 7 Dec 2011 11:44:09 +0900 Subject: [CPUFREQ] EXYNOS4210: cpufreq code is changed for stable working This patch is modify code for stable working 1. Remove unused register access code 2. Change sequence for frequency changing Signed-off-by: Jaecheol Lee Signed-off-by: Jonghwan Choi Signed-off-by: Jongpill Lee Signed-off-by: Kukjin Kim Signed-off-by: Dave Jones --- drivers/cpufreq/exynos4210-cpufreq.c | 101 ++++++++++++++++++++++------------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index ba579e0a5c3f..a0af2d4448a9 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -36,6 +36,10 @@ static struct regulator *arm_regulator; static struct cpufreq_freqs freqs; +struct cpufreq_clkdiv { + unsigned int clkdiv; +}; + static unsigned int locking_frequency; static bool frequency_locked; static DEFINE_MUTEX(cpufreq_lock); @@ -44,6 +48,8 @@ enum cpufreq_level_index { L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END, }; +static struct cpufreq_clkdiv exynos4_clkdiv_table[CPUFREQ_LEVEL_END]; + static struct cpufreq_frequency_table exynos4_freq_table[] = { {L0, 1200*1000}, {L1, 1000*1000}, @@ -155,20 +161,7 @@ static void exynos4_set_clkdiv(unsigned int div_index) /* Change Divider - CPU0 */ - tmp = __raw_readl(S5P_CLKDIV_CPU); - - tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | S5P_CLKDIV_CPU0_COREM0_MASK | - S5P_CLKDIV_CPU0_COREM1_MASK | S5P_CLKDIV_CPU0_PERIPH_MASK | - S5P_CLKDIV_CPU0_ATB_MASK | S5P_CLKDIV_CPU0_PCLKDBG_MASK | - S5P_CLKDIV_CPU0_APLL_MASK); - - tmp |= ((clkdiv_cpu0[div_index][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | - (clkdiv_cpu0[div_index][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | - (clkdiv_cpu0[div_index][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | - (clkdiv_cpu0[div_index][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | - (clkdiv_cpu0[div_index][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | - (clkdiv_cpu0[div_index][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | - (clkdiv_cpu0[div_index][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); + tmp = exynos4_clkdiv_table[div_index].clkdiv; __raw_writel(tmp, S5P_CLKDIV_CPU); @@ -233,14 +226,12 @@ static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index unsigned int tmp; if (old_index > new_index) { - /* The frequency changing to L0 needs to change apll */ - if (freqs.new == exynos4_freq_table[L0].frequency) { - /* 1. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); - - /* 2. Change the apll m,p,s value */ - exynos4_set_apll(new_index); - } else { + /* + * L1/L3, L2/L4 Level change require + * to only change s divider value + */ + if (((old_index == L3) && (new_index == L1)) || + ((old_index == L4) && (new_index == L2))) { /* 1. Change the system clock divider values */ exynos4_set_clkdiv(new_index); @@ -249,24 +240,32 @@ static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index tmp &= ~(0x7 << 0); tmp |= (exynos4_apll_pms_table[new_index] & 0x7); __raw_writel(tmp, S5P_APLL_CON0); - } - } - - else if (old_index < new_index) { - /* The frequency changing from L0 needs to change apll */ - if (freqs.old == exynos4_freq_table[L0].frequency) { - /* 1. Change the apll m,p,s value */ - exynos4_set_apll(new_index); - - /* 2. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); } else { + /* Clock Configuration Procedure */ + /* 1. Change the system clock divider values */ + exynos4_set_clkdiv(new_index); + /* 2. Change the apll m,p,s value */ + exynos4_set_apll(new_index); + } + } else if (old_index < new_index) { + /* + * L1/L3, L2/L4 Level change require + * to only change s divider value + */ + if (((old_index == L1) && (new_index == L3)) || + ((old_index == L2) && (new_index == L4))) { /* 1. Change just s value in apll m,p,s value */ tmp = __raw_readl(S5P_APLL_CON0); tmp &= ~(0x7 << 0); tmp |= (exynos4_apll_pms_table[new_index] & 0x7); __raw_writel(tmp, S5P_APLL_CON0); + /* 2. Change the system clock divider values */ + exynos4_set_clkdiv(new_index); + } else { + /* Clock Configuration Procedure */ + /* 1. Change the apll m,p,s value */ + exynos4_set_apll(new_index); /* 2. Change the system clock divider values */ exynos4_set_clkdiv(new_index); } @@ -320,14 +319,14 @@ static int exynos4_target(struct cpufreq_policy *policy, /* Clock Configuration Procedure */ exynos4_set_frequency(old_index, index); + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + /* control regulator */ if (freqs.new < freqs.old) { /* Voltage down */ regulator_set_voltage(arm_regulator, arm_volt, arm_volt); } - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - out: mutex_unlock(&cpufreq_lock); return err; @@ -438,7 +437,12 @@ static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) * Each cpu is bound to the same speed. * So the affected cpu is all of the cpus. */ - cpumask_setall(policy->cpus); + if (!cpu_online(1)) { + cpumask_copy(policy->related_cpus, cpu_possible_mask); + cpumask_copy(policy->cpus, cpu_online_mask); + } else { + cpumask_setall(policy->cpus); + } ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table); if (ret) @@ -477,6 +481,9 @@ static struct cpufreq_driver exynos4_driver = { static int __init exynos4_cpufreq_init(void) { + int i; + unsigned int tmp; + cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) return PTR_ERR(cpu_clk); @@ -503,6 +510,28 @@ static int __init exynos4_cpufreq_init(void) register_pm_notifier(&exynos4_cpufreq_nb); + tmp = __raw_readl(S5P_CLKDIV_CPU); + + for (i = L0; i < CPUFREQ_LEVEL_END; i++) { + tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | + S5P_CLKDIV_CPU0_COREM0_MASK | + S5P_CLKDIV_CPU0_COREM1_MASK | + S5P_CLKDIV_CPU0_PERIPH_MASK | + S5P_CLKDIV_CPU0_ATB_MASK | + S5P_CLKDIV_CPU0_PCLKDBG_MASK | + S5P_CLKDIV_CPU0_APLL_MASK); + + tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | + (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | + (clkdiv_cpu0[i][2] << S5P_CLKDIV_CPU0_COREM1_SHIFT) | + (clkdiv_cpu0[i][3] << S5P_CLKDIV_CPU0_PERIPH_SHIFT) | + (clkdiv_cpu0[i][4] << S5P_CLKDIV_CPU0_ATB_SHIFT) | + (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | + (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); + + exynos4_clkdiv_table[i].clkdiv = tmp; + } + return cpufreq_register_driver(&exynos4_driver); out: -- cgit v1.2.3-59-g8ed1b From 21f2e3c86b3746aaa462f9a2734363f4f41a641c Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Fri, 9 Dec 2011 16:18:42 +0530 Subject: [CPUFREQ] Remove wall variable from cpufreq_gov_dbs_init() CPUFREQ Remove wall variable from cpufreq_gov_dbs_init() Remove wall variable from cpufreq_gov_dbs_init() as get_cpu_idle_time_us() no longer updates the last_update_time unconditionally. Passing non-NULL last_update_time address will result in accounting additional idle time with update_ts_time_stats() before returning idle_sleeptime. Signed-off-by: Kamalesh Babulal Signed-off-by: Dave Jones -- drivers/cpufreq/cpufreq_ondemand.c | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-) --- drivers/cpufreq/cpufreq_ondemand.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index fa8af4ebb1d6..53ad4c780744 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -715,11 +715,10 @@ static int cpufreq_governor_dbs(struct cpufreq_policy *policy, static int __init cpufreq_gov_dbs_init(void) { - cputime64_t wall; u64 idle_time; int cpu = get_cpu(); - idle_time = get_cpu_idle_time_us(cpu, &wall); + idle_time = get_cpu_idle_time_us(cpu, NULL); put_cpu(); if (idle_time != -1ULL) { /* Idle micro accounting is supported. Use finer thresholds */ -- cgit v1.2.3-59-g8ed1b From 226dd0193f9b8524789a86505ba05b1a74d916c1 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Wed, 4 Jan 2012 10:52:31 +0530 Subject: [CPUFREQ] cpufreq:userspace: fix cpu_cur_freq updation CPU frequency is guranteed to be changed on notifier callback with CPUFREQ_POSTCHANGE. Notifier callback with CPUFREQ_PRECHANGE does not gurantee a change in frequency; after it, if cpufreq driver is unable to change CPU to new frequency. This results in wrong information being fed to user (if setting CPU frequency fails) upon doing like, cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed Hence in userspace governer update cpu_cur_freq only if notifier has been called with POSTCHANGE. Signed-off-by: Afzal Mohammed Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq_userspace.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index f231015904c0..bedac1aa9be3 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -47,9 +47,11 @@ userspace_cpufreq_notifier(struct notifier_block *nb, unsigned long val, if (!per_cpu(cpu_is_managed, freq->cpu)) return 0; - pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", - freq->cpu, freq->new); - per_cpu(cpu_cur_freq, freq->cpu) = freq->new; + if (val == CPUFREQ_POSTCHANGE) { + pr_debug("saving cpu_cur_freq of cpu %u to be %u kHz\n", + freq->cpu, freq->new); + per_cpu(cpu_cur_freq, freq->cpu) = freq->new; + } return 0; } -- cgit v1.2.3-59-g8ed1b From d08de0c19c3fc5b9cf557ce3b42795d036ad5da9 Mon Sep 17 00:00:00 2001 From: Afzal Mohammed Date: Wed, 4 Jan 2012 10:52:46 +0530 Subject: [CPUFREQ] update lpj only if frequency has changed During scaling up of cpu frequency, loops_per_jiffy is updated upon invoking PRECHANGE notifier. If setting to new frequency fails in cpufreq driver, lpj is left at incorrect value. Hence update lpj only if cpu frequency is changed, i.e. upon invoking POSTCHANGE notifier. Penalty would be that during time period between changing cpu frequency & invocation of POSTCHANGE notifier, udelay(x) may not gurantee minimal delay of 'x' us for frequency scaling up operation. Perhaps a better solution would be to define CPUFREQ_ABORTCHANGE & handle accordingly, but then it would be more intrusive (using ABORTCHANGE may help drivers also; if any has registered notifier and expect POST for a PRECHANGE, their needs can be taken care using ABORT) Signed-off-by: Afzal Mohammed Signed-off-by: Dave Jones --- drivers/cpufreq/cpufreq.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 987a165ede26..2f5801adc739 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -204,8 +204,7 @@ static void adjust_jiffies(unsigned long val, struct cpufreq_freqs *ci) pr_debug("saving %lu as reference value for loops_per_jiffy; " "freq is %u kHz\n", l_p_j_ref, l_p_j_ref_freq); } - if ((val == CPUFREQ_PRECHANGE && ci->old < ci->new) || - (val == CPUFREQ_POSTCHANGE && ci->old > ci->new) || + if ((val == CPUFREQ_POSTCHANGE && ci->old != ci->new) || (val == CPUFREQ_RESUMECHANGE || val == CPUFREQ_SUSPENDCHANGE)) { loops_per_jiffy = cpufreq_scale(l_p_j_ref, l_p_j_ref_freq, ci->new); -- cgit v1.2.3-59-g8ed1b From 201bf0f129e1715a33568d1563d9a75b840ab4d3 Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 6 Jan 2012 15:56:31 +0100 Subject: [CPUFREQ] powernow-k8: Avoid Pstate MSR accesses on systems supporting CPB Due to CPB we can't directly map SW Pstates to Pstate MSRs. Get rid of the paranoia check. (assuming that the ACPI Pstate information is correct.) Signed-off-by: Andreas Herrmann Signed-off-by: Dave Jones --- drivers/cpufreq/powernow-k8.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index bce576d7478e..e0329f9fa40e 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -926,23 +926,24 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, invalidate_entry(powernow_table, i); continue; } - rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); - if (!(hi & HW_PSTATE_VALID_MASK)) { - pr_debug("invalid pstate %d, ignoring\n", index); - invalidate_entry(powernow_table, i); - continue; - } - - powernow_table[i].index = index; - /* Frequency may be rounded for these */ if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || boot_cpu_data.x86 == 0x11) { + + rdmsr(MSR_PSTATE_DEF_BASE + index, lo, hi); + if (!(hi & HW_PSTATE_VALID_MASK)) { + pr_debug("invalid pstate %d, ignoring\n", index); + invalidate_entry(powernow_table, i); + continue; + } + powernow_table[i].frequency = freq_from_fid_did(lo & 0x3f, (lo >> 6) & 7); } else powernow_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; + + powernow_table[i].index = index; } return 0; } -- cgit v1.2.3-59-g8ed1b From a8eb28480e9b637cc78b9aa5e08612ba97e1317a Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 6 Jan 2012 15:57:55 +0100 Subject: [CPUFREQ] powernow-k8: Fix indexing issue The driver uses the pstate number from the status register as index in its table of ACPI pstates (powernow_table). This is wrong as this is not a 1-to-1 mapping. For example we can have _PSS information to just utilize Pstate 0 and Pstate 4, ie. powernow-k8: Core Performance Boosting: on. powernow-k8: 0 : pstate 0 (2200 MHz) powernow-k8: 1 : pstate 4 (1400 MHz) In this example the driver's powernow_table has just 2 entries. Using the pstate number (4) as index into this table is just plain wrong. Signed-off-by: Andreas Herrmann Signed-off-by: Dave Jones --- drivers/cpufreq/powernow-k8.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index e0329f9fa40e..ad683ec2c57e 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -54,6 +54,9 @@ static DEFINE_PER_CPU(struct powernow_k8_data *, powernow_data); static int cpu_family = CPU_OPTERON; +/* array to map SW pstate number to acpi state */ +static u32 ps_to_as[8]; + /* core performance boost */ static bool cpb_capable, cpb_enabled; static struct msr __percpu *msrs; @@ -80,9 +83,9 @@ static u32 find_khz_freq_from_fid(u32 fid) } static u32 find_khz_freq_from_pstate(struct cpufreq_frequency_table *data, - u32 pstate) + u32 pstate) { - return data[pstate].frequency; + return data[ps_to_as[pstate]].frequency; } /* Return the vco fid for an input fid @@ -926,6 +929,9 @@ static int fill_powernow_table_pstate(struct powernow_k8_data *data, invalidate_entry(powernow_table, i); continue; } + + ps_to_as[index] = i; + /* Frequency may be rounded for these */ if ((boot_cpu_data.x86 == 0x10 && boot_cpu_data.x86_model < 10) || boot_cpu_data.x86 == 0x11) { @@ -1190,7 +1196,8 @@ static int powernowk8_target(struct cpufreq_policy *pol, powernow_k8_acpi_pst_values(data, newstate); if (cpu_family == CPU_HW_PSTATE) - ret = transition_frequency_pstate(data, newstate); + ret = transition_frequency_pstate(data, + data->powernow_table[newstate].index); else ret = transition_frequency_fidvid(data, newstate); if (ret) { @@ -1203,7 +1210,7 @@ static int powernowk8_target(struct cpufreq_policy *pol, if (cpu_family == CPU_HW_PSTATE) pol->cur = find_khz_freq_from_pstate(data->powernow_table, - newstate); + data->powernow_table[newstate].index); else pol->cur = find_khz_freq_from_fid(data->currfid); ret = 0; -- cgit v1.2.3-59-g8ed1b From b2bd68e1d5568a3911e991fc71e083f439886d8c Mon Sep 17 00:00:00 2001 From: Andreas Herrmann Date: Fri, 6 Jan 2012 15:59:33 +0100 Subject: [CPUFREQ] powernow-k8: Update copyright, maintainer and documentation information Signed-off-by: Andreas Herrmann Signed-off-by: Dave Jones --- drivers/cpufreq/powernow-k8.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index ad683ec2c57e..8f9b2ceeec85 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -1,10 +1,11 @@ /* - * (c) 2003-2010 Advanced Micro Devices, Inc. + * (c) 2003-2012 Advanced Micro Devices, Inc. * Your use of this code is subject to the terms and conditions of the * GNU general public license version 2. See "COPYING" or * http://www.gnu.org/licenses/gpl.html * - * Support : mark.langsdorf@amd.com + * Maintainer: + * Andreas Herrmann * * Based on the powernow-k7.c module written by Dave Jones. * (C) 2003 Dave Jones on behalf of SuSE Labs @@ -16,12 +17,14 @@ * Valuable input gratefully received from Dave Jones, Pavel Machek, * Dominik Brodowski, Jacob Shin, and others. * Originally developed by Paul Devriendt. - * Processor information obtained from Chapter 9 (Power and Thermal Management) - * of the "BIOS and Kernel Developer's Guide for the AMD Athlon 64 and AMD - * Opteron Processors" available for download from www.amd.com * - * Tables for specific CPUs can be inferred from - * http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/30430.pdf + * Processor information obtained from Chapter 9 (Power and Thermal + * Management) of the "BIOS and Kernel Developer's Guide (BKDG) for + * the AMD Athlon 64 and AMD Opteron Processors" and section "2.x + * Power Management" in BKDGs for newer AMD CPU families. + * + * Tables for specific CPUs can be inferred from AMD's processor + * power and thermal data sheets, (e.g. 30417.pdf, 30430.pdf, 43375.pdf) */ #include -- cgit v1.2.3-59-g8ed1b From a125a17fa61afe2fa4e52b239dd20af8ce90c9f7 Mon Sep 17 00:00:00 2001 From: Jaecheol Lee Date: Sat, 7 Jan 2012 20:18:35 +0900 Subject: [CPUFREQ] EXYNOS: Make EXYNOS common cpufreq driver To support various EXYNOS series SoCs commonly, added exynos common structure. exynos-cpufreq.c => EXYNOS series common cpufreq driver exynos4210-cpufreq.c => EXYNOS4210 support cpufreq driver Signed-off-by: Jaecheol Lee Signed-off-by: Kukjin Kim Signed-off-by: Dave Jones --- arch/arm/mach-exynos/include/mach/cpufreq.h | 34 +++ drivers/cpufreq/Kconfig.arm | 15 +- drivers/cpufreq/Makefile | 1 + drivers/cpufreq/exynos-cpufreq.c | 296 +++++++++++++++++++++ drivers/cpufreq/exynos4210-cpufreq.c | 385 ++++++---------------------- 5 files changed, 414 insertions(+), 317 deletions(-) create mode 100644 arch/arm/mach-exynos/include/mach/cpufreq.h create mode 100644 drivers/cpufreq/exynos-cpufreq.c diff --git a/arch/arm/mach-exynos/include/mach/cpufreq.h b/arch/arm/mach-exynos/include/mach/cpufreq.h new file mode 100644 index 000000000000..3df27f2d5034 --- /dev/null +++ b/arch/arm/mach-exynos/include/mach/cpufreq.h @@ -0,0 +1,34 @@ +/* linux/arch/arm/mach-exynos/include/mach/cpufreq.h + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - CPUFreq support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +enum cpufreq_level_index { + L0, L1, L2, L3, L4, + L5, L6, L7, L8, L9, + L10, L11, L12, L13, L14, + L15, L16, L17, L18, L19, + L20, +}; + +struct exynos_dvfs_info { + unsigned long mpll_freq_khz; + unsigned int pll_safe_idx; + unsigned int pm_lock_idx; + unsigned int max_support_idx; + unsigned int min_support_idx; + struct clk *cpu_clk; + unsigned int *volt_table; + struct cpufreq_frequency_table *freq_table; + void (*set_freq)(unsigned int, unsigned int); + bool (*need_apll_change)(unsigned int, unsigned int); +}; + +extern int exynos4210_cpufreq_init(struct exynos_dvfs_info *); diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 72a0044c1baa..e0664fed018a 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -21,12 +21,19 @@ config ARM_S5PV210_CPUFREQ If in doubt, say N. +config ARM_EXYNOS_CPUFREQ + bool "SAMSUNG EXYNOS SoCs" + depends on ARCH_EXYNOS + select ARM_EXYNOS4210_CPUFREQ if CPU_EXYNOS4210 + default y + help + This adds the CPUFreq driver common part for Samsung + EXYNOS SoCs. + + If in doubt, say N. + config ARM_EXYNOS4210_CPUFREQ bool "Samsung EXYNOS4210" - depends on CPU_EXYNOS4210 - default y help This adds the CPUFreq driver for Samsung EXYNOS4210 SoC (S5PV310 or S5PC210). - - If in doubt, say N. diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index ce75fcbcca4f..ac000fa76bbb 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_X86_CPUFREQ_NFORCE2) += cpufreq-nforce2.o obj-$(CONFIG_UX500_SOC_DB8500) += db8500-cpufreq.o obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o obj-$(CONFIG_ARM_S5PV210_CPUFREQ) += s5pv210-cpufreq.o +obj-$(CONFIG_ARM_EXYNOS_CPUFREQ) += exynos-cpufreq.o obj-$(CONFIG_ARM_EXYNOS4210_CPUFREQ) += exynos4210-cpufreq.o obj-$(CONFIG_ARCH_OMAP2PLUS) += omap-cpufreq.o diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c new file mode 100644 index 000000000000..24e4dd453fab --- /dev/null +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -0,0 +1,296 @@ +/* + * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * + * EXYNOS - CPU frequency scaling support for EXYNOS series + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +static struct exynos_dvfs_info *exynos_info; + +static struct regulator *arm_regulator; +static struct cpufreq_freqs freqs; + +static unsigned int locking_frequency; +static bool frequency_locked; +static DEFINE_MUTEX(cpufreq_lock); + +int exynos_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, + exynos_info->freq_table); +} + +unsigned int exynos_getspeed(unsigned int cpu) +{ + return clk_get_rate(exynos_info->cpu_clk) / 1000; +} + +static int exynos_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + unsigned int index, old_index; + unsigned int arm_volt, safe_arm_volt = 0; + int ret = 0; + struct cpufreq_frequency_table *freq_table = exynos_info->freq_table; + unsigned int *volt_table = exynos_info->volt_table; + unsigned int mpll_freq_khz = exynos_info->mpll_freq_khz; + + mutex_lock(&cpufreq_lock); + + freqs.old = policy->cur; + + if (frequency_locked && target_freq != locking_frequency) { + ret = -EAGAIN; + goto out; + } + + if (cpufreq_frequency_table_target(policy, freq_table, + freqs.old, relation, &old_index)) { + ret = -EINVAL; + goto out; + } + + if (cpufreq_frequency_table_target(policy, freq_table, + target_freq, relation, &index)) { + ret = -EINVAL; + goto out; + } + + freqs.new = freq_table[index].frequency; + freqs.cpu = policy->cpu; + + /* + * ARM clock source will be changed APLL to MPLL temporary + * To support this level, need to control regulator for + * required voltage level + */ + if (exynos_info->need_apll_change != NULL) { + if (exynos_info->need_apll_change(old_index, index) && + (freq_table[index].frequency < mpll_freq_khz) && + (freq_table[old_index].frequency < mpll_freq_khz)) + safe_arm_volt = volt_table[exynos_info->pll_safe_idx]; + } + arm_volt = volt_table[index]; + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* When the new frequency is higher than current frequency */ + if ((freqs.new > freqs.old) && !safe_arm_volt) { + /* Firstly, voltage up to increase frequency */ + regulator_set_voltage(arm_regulator, arm_volt, + arm_volt); + } + + if (safe_arm_volt) + regulator_set_voltage(arm_regulator, safe_arm_volt, + safe_arm_volt); + if (freqs.new != freqs.old) + exynos_info->set_freq(old_index, index); + + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + /* When the new frequency is lower than current frequency */ + if ((freqs.new < freqs.old) || + ((freqs.new > freqs.old) && safe_arm_volt)) { + /* down the voltage after frequency change */ + regulator_set_voltage(arm_regulator, arm_volt, + arm_volt); + } + +out: + mutex_unlock(&cpufreq_lock); + + return ret; +} + +#ifdef CONFIG_PM +static int exynos_cpufreq_suspend(struct cpufreq_policy *policy) +{ + return 0; +} + +static int exynos_cpufreq_resume(struct cpufreq_policy *policy) +{ + return 0; +} +#endif + +/** + * exynos_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume + * context + * @notifier + * @pm_event + * @v + * + * While frequency_locked == true, target() ignores every frequency but + * locking_frequency. The locking_frequency value is the initial frequency, + * which is set by the bootloader. In order to eliminate possible + * inconsistency in clock values, we save and restore frequencies during + * suspend and resume and block CPUFREQ activities. Note that the standard + * suspend/resume cannot be used as they are too deep (syscore_ops) for + * regulator actions. + */ +static int exynos_cpufreq_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *v) +{ + struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ + static unsigned int saved_frequency; + unsigned int temp; + + mutex_lock(&cpufreq_lock); + switch (pm_event) { + case PM_SUSPEND_PREPARE: + if (frequency_locked) + goto out; + + frequency_locked = true; + + if (locking_frequency) { + saved_frequency = exynos_getspeed(0); + + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, + CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + } + break; + + case PM_POST_SUSPEND: + if (saved_frequency) { + /* + * While frequency_locked, only locking_frequency + * is valid for target(). In order to use + * saved_frequency while keeping frequency_locked, + * we temporarly overwrite locking_frequency. + */ + temp = locking_frequency; + locking_frequency = saved_frequency; + + mutex_unlock(&cpufreq_lock); + exynos_target(policy, locking_frequency, + CPUFREQ_RELATION_H); + mutex_lock(&cpufreq_lock); + + locking_frequency = temp; + } + frequency_locked = false; + break; + } +out: + mutex_unlock(&cpufreq_lock); + + return NOTIFY_OK; +} + +static struct notifier_block exynos_cpufreq_nb = { + .notifier_call = exynos_cpufreq_pm_notifier, +}; + +static int exynos_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + policy->cur = policy->min = policy->max = exynos_getspeed(policy->cpu); + + cpufreq_frequency_table_get_attr(exynos_info->freq_table, policy->cpu); + + /* set the transition latency value */ + policy->cpuinfo.transition_latency = 100000; + + /* + * EXYNOS4 multi-core processors has 2 cores + * that the frequency cannot be set independently. + * Each cpu is bound to the same speed. + * So the affected cpu is all of the cpus. + */ + if (num_online_cpus() == 1) { + cpumask_copy(policy->related_cpus, cpu_possible_mask); + cpumask_copy(policy->cpus, cpu_online_mask); + } else { + cpumask_setall(policy->cpus); + } + + return cpufreq_frequency_table_cpuinfo(policy, exynos_info->freq_table); +} + +static struct cpufreq_driver exynos_driver = { + .flags = CPUFREQ_STICKY, + .verify = exynos_verify_speed, + .target = exynos_target, + .get = exynos_getspeed, + .init = exynos_cpufreq_cpu_init, + .name = "exynos_cpufreq", +#ifdef CONFIG_PM + .suspend = exynos_cpufreq_suspend, + .resume = exynos_cpufreq_resume, +#endif +}; + +static int __init exynos_cpufreq_init(void) +{ + int ret = -EINVAL; + + exynos_info = kzalloc(sizeof(struct exynos_dvfs_info), GFP_KERNEL); + if (!exynos_info) + return -ENOMEM; + + if (soc_is_exynos4210()) + ret = exynos4210_cpufreq_init(exynos_info); + else + pr_err("%s: CPU type not found\n", __func__); + + if (ret) + goto err_vdd_arm; + + if (exynos_info->set_freq == NULL) { + pr_err("%s: No set_freq function (ERR)\n", __func__); + goto err_vdd_arm; + } + + arm_regulator = regulator_get(NULL, "vdd_arm"); + if (IS_ERR(arm_regulator)) { + pr_err("%s: failed to get resource vdd_arm\n", __func__); + goto err_vdd_arm; + } + + register_pm_notifier(&exynos_cpufreq_nb); + + if (cpufreq_register_driver(&exynos_driver)) { + pr_err("%s: failed to register cpufreq driver\n", __func__); + goto err_cpufreq; + } + + return 0; +err_cpufreq: + unregister_pm_notifier(&exynos_cpufreq_nb); + + if (!IS_ERR(arm_regulator)) + regulator_put(arm_regulator); +err_vdd_arm: + kfree(exynos_info); + pr_debug("%s: failed initialization\n", __func__); + return -EINVAL; +} +late_initcall(exynos_cpufreq_init); diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index a0af2d4448a9..6bc4ada56df1 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -2,7 +2,7 @@ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * EXYNOS4 - CPU frequency scaling support + * EXYNOS4210 - CPU frequency scaling support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,10 +23,16 @@ #include #include #include +#include #include #include +#define CPUFREQ_LEVEL_END L5 + +static int max_support_idx = L0; +static int min_support_idx = (CPUFREQ_LEVEL_END - 1); + static struct clk *cpu_clk; static struct clk *moutcore; static struct clk *mout_mpll; @@ -37,20 +43,18 @@ static struct regulator *arm_regulator; static struct cpufreq_freqs freqs; struct cpufreq_clkdiv { + unsigned int index; unsigned int clkdiv; }; -static unsigned int locking_frequency; -static bool frequency_locked; -static DEFINE_MUTEX(cpufreq_lock); - -enum cpufreq_level_index { - L0, L1, L2, L3, L4, CPUFREQ_LEVEL_END, +static unsigned int exynos4210_volt_table[CPUFREQ_LEVEL_END] = { + 1250000, 1150000, 1050000, 975000, 950000, }; -static struct cpufreq_clkdiv exynos4_clkdiv_table[CPUFREQ_LEVEL_END]; -static struct cpufreq_frequency_table exynos4_freq_table[] = { +static struct cpufreq_clkdiv exynos4210_clkdiv_table[CPUFREQ_LEVEL_END]; + +static struct cpufreq_frequency_table exynos4210_freq_table[] = { {L0, 1200*1000}, {L1, 1000*1000}, {L2, 800*1000}, @@ -104,31 +108,7 @@ static unsigned int clkdiv_cpu1[CPUFREQ_LEVEL_END][2] = { { 3, 0 }, }; -struct cpufreq_voltage_table { - unsigned int index; /* any */ - unsigned int arm_volt; /* uV */ -}; - -static struct cpufreq_voltage_table exynos4_volt_table[CPUFREQ_LEVEL_END] = { - { - .index = L0, - .arm_volt = 1350000, - }, { - .index = L1, - .arm_volt = 1300000, - }, { - .index = L2, - .arm_volt = 1200000, - }, { - .index = L3, - .arm_volt = 1100000, - }, { - .index = L4, - .arm_volt = 1050000, - }, -}; - -static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = { +static unsigned int exynos4210_apll_pms_table[CPUFREQ_LEVEL_END] = { /* APLL FOUT L0: 1200MHz */ ((150 << 16) | (3 << 8) | 1), @@ -145,23 +125,13 @@ static unsigned int exynos4_apll_pms_table[CPUFREQ_LEVEL_END] = { ((200 << 16) | (6 << 8) | 3), }; -static int exynos4_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, exynos4_freq_table); -} - -static unsigned int exynos4_getspeed(unsigned int cpu) -{ - return clk_get_rate(cpu_clk) / 1000; -} - -static void exynos4_set_clkdiv(unsigned int div_index) +static void exynos4210_set_clkdiv(unsigned int div_index) { unsigned int tmp; /* Change Divider - CPU0 */ - tmp = exynos4_clkdiv_table[div_index].clkdiv; + tmp = exynos4210_clkdiv_table[div_index].clkdiv; __raw_writel(tmp, S5P_CLKDIV_CPU); @@ -185,7 +155,7 @@ static void exynos4_set_clkdiv(unsigned int div_index) } while (tmp & 0x11); } -static void exynos4_set_apll(unsigned int index) +static void exynos4210_set_apll(unsigned int index) { unsigned int tmp; @@ -204,7 +174,7 @@ static void exynos4_set_apll(unsigned int index) /* 3. Change PLL PMS values */ tmp = __raw_readl(S5P_APLL_CON0); tmp &= ~((0x3ff << 16) | (0x3f << 8) | (0x7 << 0)); - tmp |= exynos4_apll_pms_table[index]; + tmp |= exynos4210_apll_pms_table[index]; __raw_writel(tmp, S5P_APLL_CON0); /* 4. wait_lock_time */ @@ -221,305 +191,90 @@ static void exynos4_set_apll(unsigned int index) } while (tmp != (0x1 << S5P_CLKSRC_CPU_MUXCORE_SHIFT)); } -static void exynos4_set_frequency(unsigned int old_index, unsigned int new_index) +bool exynos4210_pms_change(unsigned int old_index, unsigned int new_index) +{ + unsigned int old_pm = (exynos4210_apll_pms_table[old_index] >> 8); + unsigned int new_pm = (exynos4210_apll_pms_table[new_index] >> 8); + + return (old_pm == new_pm) ? 0 : 1; +} + +static void exynos4210_set_frequency(unsigned int old_index, + unsigned int new_index) { unsigned int tmp; if (old_index > new_index) { - /* - * L1/L3, L2/L4 Level change require - * to only change s divider value - */ - if (((old_index == L3) && (new_index == L1)) || - ((old_index == L4) && (new_index == L2))) { + if (!exynos4210_pms_change(old_index, new_index)) { /* 1. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); + exynos4210_set_clkdiv(new_index); /* 2. Change just s value in apll m,p,s value */ tmp = __raw_readl(S5P_APLL_CON0); tmp &= ~(0x7 << 0); - tmp |= (exynos4_apll_pms_table[new_index] & 0x7); + tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); __raw_writel(tmp, S5P_APLL_CON0); } else { /* Clock Configuration Procedure */ /* 1. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); + exynos4210_set_clkdiv(new_index); /* 2. Change the apll m,p,s value */ - exynos4_set_apll(new_index); + exynos4210_set_apll(new_index); } } else if (old_index < new_index) { - /* - * L1/L3, L2/L4 Level change require - * to only change s divider value - */ - if (((old_index == L1) && (new_index == L3)) || - ((old_index == L2) && (new_index == L4))) { + if (!exynos4210_pms_change(old_index, new_index)) { /* 1. Change just s value in apll m,p,s value */ tmp = __raw_readl(S5P_APLL_CON0); tmp &= ~(0x7 << 0); - tmp |= (exynos4_apll_pms_table[new_index] & 0x7); + tmp |= (exynos4210_apll_pms_table[new_index] & 0x7); __raw_writel(tmp, S5P_APLL_CON0); /* 2. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); + exynos4210_set_clkdiv(new_index); } else { /* Clock Configuration Procedure */ /* 1. Change the apll m,p,s value */ - exynos4_set_apll(new_index); + exynos4210_set_apll(new_index); /* 2. Change the system clock divider values */ - exynos4_set_clkdiv(new_index); + exynos4210_set_clkdiv(new_index); } } } -static int exynos4_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - unsigned int index, old_index; - unsigned int arm_volt; - int err = -EINVAL; - - freqs.old = exynos4_getspeed(policy->cpu); - - mutex_lock(&cpufreq_lock); - - if (frequency_locked && target_freq != locking_frequency) { - err = -EAGAIN; - goto out; - } - - if (cpufreq_frequency_table_target(policy, exynos4_freq_table, - freqs.old, relation, &old_index)) - goto out; - - if (cpufreq_frequency_table_target(policy, exynos4_freq_table, - target_freq, relation, &index)) - goto out; - - err = 0; - - freqs.new = exynos4_freq_table[index].frequency; - freqs.cpu = policy->cpu; - - if (freqs.new == freqs.old) - goto out; - - /* get the voltage value */ - arm_volt = exynos4_volt_table[index].arm_volt; - - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - - /* control regulator */ - if (freqs.new > freqs.old) { - /* Voltage up */ - regulator_set_voltage(arm_regulator, arm_volt, arm_volt); - } - - /* Clock Configuration Procedure */ - exynos4_set_frequency(old_index, index); - - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - /* control regulator */ - if (freqs.new < freqs.old) { - /* Voltage down */ - regulator_set_voltage(arm_regulator, arm_volt, arm_volt); - } - -out: - mutex_unlock(&cpufreq_lock); - return err; -} - -#ifdef CONFIG_PM -/* - * These suspend/resume are used as syscore_ops, it is already too - * late to set regulator voltages at this stage. - */ -static int exynos4_cpufreq_suspend(struct cpufreq_policy *policy) -{ - return 0; -} - -static int exynos4_cpufreq_resume(struct cpufreq_policy *policy) -{ - return 0; -} -#endif - -/** - * exynos4_cpufreq_pm_notifier - block CPUFREQ's activities in suspend-resume - * context - * @notifier - * @pm_event - * @v - * - * While frequency_locked == true, target() ignores every frequency but - * locking_frequency. The locking_frequency value is the initial frequency, - * which is set by the bootloader. In order to eliminate possible - * inconsistency in clock values, we save and restore frequencies during - * suspend and resume and block CPUFREQ activities. Note that the standard - * suspend/resume cannot be used as they are too deep (syscore_ops) for - * regulator actions. - */ -static int exynos4_cpufreq_pm_notifier(struct notifier_block *notifier, - unsigned long pm_event, void *v) -{ - struct cpufreq_policy *policy = cpufreq_cpu_get(0); /* boot CPU */ - static unsigned int saved_frequency; - unsigned int temp; - - mutex_lock(&cpufreq_lock); - switch (pm_event) { - case PM_SUSPEND_PREPARE: - if (frequency_locked) - goto out; - frequency_locked = true; - - if (locking_frequency) { - saved_frequency = exynos4_getspeed(0); - - mutex_unlock(&cpufreq_lock); - exynos4_target(policy, locking_frequency, - CPUFREQ_RELATION_H); - mutex_lock(&cpufreq_lock); - } - - break; - case PM_POST_SUSPEND: - - if (saved_frequency) { - /* - * While frequency_locked, only locking_frequency - * is valid for target(). In order to use - * saved_frequency while keeping frequency_locked, - * we temporarly overwrite locking_frequency. - */ - temp = locking_frequency; - locking_frequency = saved_frequency; - - mutex_unlock(&cpufreq_lock); - exynos4_target(policy, locking_frequency, - CPUFREQ_RELATION_H); - mutex_lock(&cpufreq_lock); - - locking_frequency = temp; - } - - frequency_locked = false; - break; - } -out: - mutex_unlock(&cpufreq_lock); - - return NOTIFY_OK; -} - -static struct notifier_block exynos4_cpufreq_nb = { - .notifier_call = exynos4_cpufreq_pm_notifier, -}; - -static int exynos4_cpufreq_cpu_init(struct cpufreq_policy *policy) -{ - int ret; - - policy->cur = policy->min = policy->max = exynos4_getspeed(policy->cpu); - - cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); - - /* set the transition latency value */ - policy->cpuinfo.transition_latency = 100000; - - /* - * EXYNOS4 multi-core processors has 2 cores - * that the frequency cannot be set independently. - * Each cpu is bound to the same speed. - * So the affected cpu is all of the cpus. - */ - if (!cpu_online(1)) { - cpumask_copy(policy->related_cpus, cpu_possible_mask); - cpumask_copy(policy->cpus, cpu_online_mask); - } else { - cpumask_setall(policy->cpus); - } - - ret = cpufreq_frequency_table_cpuinfo(policy, exynos4_freq_table); - if (ret) - return ret; - - cpufreq_frequency_table_get_attr(exynos4_freq_table, policy->cpu); - - return 0; -} - -static int exynos4_cpufreq_cpu_exit(struct cpufreq_policy *policy) -{ - cpufreq_frequency_table_put_attr(policy->cpu); - return 0; -} - -static struct freq_attr *exynos4_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver exynos4_driver = { - .flags = CPUFREQ_STICKY, - .verify = exynos4_verify_speed, - .target = exynos4_target, - .get = exynos4_getspeed, - .init = exynos4_cpufreq_cpu_init, - .exit = exynos4_cpufreq_cpu_exit, - .name = "exynos4_cpufreq", - .attr = exynos4_cpufreq_attr, -#ifdef CONFIG_PM - .suspend = exynos4_cpufreq_suspend, - .resume = exynos4_cpufreq_resume, -#endif -}; - -static int __init exynos4_cpufreq_init(void) +int exynos4210_cpufreq_init(struct exynos_dvfs_info *info) { int i; unsigned int tmp; + unsigned long rate; cpu_clk = clk_get(NULL, "armclk"); if (IS_ERR(cpu_clk)) return PTR_ERR(cpu_clk); - locking_frequency = exynos4_getspeed(0); - moutcore = clk_get(NULL, "moutcore"); if (IS_ERR(moutcore)) - goto out; + goto err_moutcore; mout_mpll = clk_get(NULL, "mout_mpll"); if (IS_ERR(mout_mpll)) - goto out; + goto err_mout_mpll; + + rate = clk_get_rate(mout_mpll) / 1000; mout_apll = clk_get(NULL, "mout_apll"); if (IS_ERR(mout_apll)) - goto out; - - arm_regulator = regulator_get(NULL, "vdd_arm"); - if (IS_ERR(arm_regulator)) { - printk(KERN_ERR "failed to get resource %s\n", "vdd_arm"); - goto out; - } - - register_pm_notifier(&exynos4_cpufreq_nb); + goto err_mout_apll; tmp = __raw_readl(S5P_CLKDIV_CPU); for (i = L0; i < CPUFREQ_LEVEL_END; i++) { tmp &= ~(S5P_CLKDIV_CPU0_CORE_MASK | - S5P_CLKDIV_CPU0_COREM0_MASK | - S5P_CLKDIV_CPU0_COREM1_MASK | - S5P_CLKDIV_CPU0_PERIPH_MASK | - S5P_CLKDIV_CPU0_ATB_MASK | - S5P_CLKDIV_CPU0_PCLKDBG_MASK | - S5P_CLKDIV_CPU0_APLL_MASK); + S5P_CLKDIV_CPU0_COREM0_MASK | + S5P_CLKDIV_CPU0_COREM1_MASK | + S5P_CLKDIV_CPU0_PERIPH_MASK | + S5P_CLKDIV_CPU0_ATB_MASK | + S5P_CLKDIV_CPU0_PCLKDBG_MASK | + S5P_CLKDIV_CPU0_APLL_MASK); tmp |= ((clkdiv_cpu0[i][0] << S5P_CLKDIV_CPU0_CORE_SHIFT) | (clkdiv_cpu0[i][1] << S5P_CLKDIV_CPU0_COREM0_SHIFT) | @@ -529,29 +284,33 @@ static int __init exynos4_cpufreq_init(void) (clkdiv_cpu0[i][5] << S5P_CLKDIV_CPU0_PCLKDBG_SHIFT) | (clkdiv_cpu0[i][6] << S5P_CLKDIV_CPU0_APLL_SHIFT)); - exynos4_clkdiv_table[i].clkdiv = tmp; + exynos4210_clkdiv_table[i].clkdiv = tmp; } - return cpufreq_register_driver(&exynos4_driver); - -out: - if (!IS_ERR(cpu_clk)) - clk_put(cpu_clk); + info->mpll_freq_khz = rate; + info->pm_lock_idx = L2; + info->pll_safe_idx = L2; + info->max_support_idx = max_support_idx; + info->min_support_idx = min_support_idx; + info->cpu_clk = cpu_clk; + info->volt_table = exynos4210_volt_table; + info->freq_table = exynos4210_freq_table; + info->set_freq = exynos4210_set_frequency; + info->need_apll_change = exynos4210_pms_change; - if (!IS_ERR(moutcore)) - clk_put(moutcore); + return 0; +err_mout_apll: if (!IS_ERR(mout_mpll)) clk_put(mout_mpll); +err_mout_mpll: + if (!IS_ERR(moutcore)) + clk_put(moutcore); +err_moutcore: + if (!IS_ERR(cpu_clk)) + clk_put(cpu_clk); - if (!IS_ERR(mout_apll)) - clk_put(mout_apll); - - if (!IS_ERR(arm_regulator)) - regulator_put(arm_regulator); - - printk(KERN_ERR "%s: failed initialization\n", __func__); - + pr_debug("%s: failed initialization\n", __func__); return -EINVAL; } -late_initcall(exynos4_cpufreq_init); +EXPORT_SYMBOL(exynos4210_cpufreq_init); -- cgit v1.2.3-59-g8ed1b From 6c523c614c13b84a3dc64f7a56d6855b03e6b292 Mon Sep 17 00:00:00 2001 From: Jaecheol Lee Date: Sat, 7 Jan 2012 20:18:39 +0900 Subject: [CPUFREQ] EXYNOS: Removed useless headers and codes This patch removes no referencing header files and cleaned up useless code. Signed-off-by: Jaecheol Lee Signed-off-by: Kukjin Kim Signed-off-by: Dave Jones --- drivers/cpufreq/exynos-cpufreq.c | 8 +------- drivers/cpufreq/exynos4210-cpufreq.c | 14 +------------- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index 24e4dd453fab..5467879ea07d 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -9,7 +9,6 @@ * published by the Free Software Foundation. */ -#include #include #include #include @@ -18,15 +17,10 @@ #include #include #include -#include -#include -#include -#include #include -#include -#include +#include static struct exynos_dvfs_info *exynos_info; diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 6bc4ada56df1..065da5b702f1 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -9,25 +9,17 @@ * published by the Free Software Foundation. */ -#include +#include #include #include #include #include #include -#include #include -#include -#include -#include #include -#include #include -#include -#include - #define CPUFREQ_LEVEL_END L5 static int max_support_idx = L0; @@ -38,10 +30,6 @@ static struct clk *moutcore; static struct clk *mout_mpll; static struct clk *mout_apll; -static struct regulator *arm_regulator; - -static struct cpufreq_freqs freqs; - struct cpufreq_clkdiv { unsigned int index; unsigned int clkdiv; -- cgit v1.2.3-59-g8ed1b