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 --- drivers/cpufreq/omap-cpufreq.c | 188 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 drivers/cpufreq/omap-cpufreq.c (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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 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(-) (limited to 'drivers/cpufreq/omap-cpufreq.c') 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