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') diff --git a/drivers/cpufreq/omap-cpufreq.c b/drivers/cpufreq/omap-cpufreq.c index a6b2be7ea5a..1953f9d082a 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