From 6c2b7072a7035837998da38809f98e4182e4c41c Mon Sep 17 00:00:00 2001 From: Graf Yang Date: Wed, 27 Jan 2010 11:16:32 +0000 Subject: Blackfin: add support for cpufreq on SMP systems Signed-off-by: Graf Yang Signed-off-by: Mike Frysinger --- arch/blackfin/mach-common/cpufreq.c | 155 +++++++++++++++++++++--------------- 1 file changed, 93 insertions(+), 62 deletions(-) (limited to 'arch/blackfin/mach-common/cpufreq.c') diff --git a/arch/blackfin/mach-common/cpufreq.c b/arch/blackfin/mach-common/cpufreq.c index 7775828..5d7f8ab 100644 --- a/arch/blackfin/mach-common/cpufreq.c +++ b/arch/blackfin/mach-common/cpufreq.c @@ -15,6 +15,8 @@ #include #include +#define CPUFREQ_CPU 0 + /* this is the table of CCLK frequencies, in Hz */ /* .index is the entry in the auxillary dpm_state_table[] */ static struct cpufreq_frequency_table bfin_freq_table[] = { @@ -41,64 +43,114 @@ static struct bfin_dpm_state { unsigned int tscale; /* change the divider on the core timer interrupt */ } dpm_state_table[3]; +#if defined(CONFIG_CYCLES_CLOCKSOURCE) /* - normalized to maximum frequncy offset for CYCLES, - used in time-ts cycles clock source, but could be used - somewhere also. + * normalized to maximum frequncy offset for CYCLES, + * used in time-ts cycles clock source, but could be used + * somewhere also. */ unsigned long long __bfin_cycles_off; unsigned int __bfin_cycles_mod; +#endif /**************************************************************************/ +static void __init bfin_init_tables(unsigned long cclk, unsigned long sclk) +{ -static unsigned int bfin_getfreq_khz(unsigned int cpu) + unsigned long csel, min_cclk; + int index; + + /* Anomaly 273 seems to still exist on non-BF54x w/dcache turned on */ +#if ANOMALY_05000273 || ANOMALY_05000274 || \ + (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE)) + min_cclk = sclk * 2; +#else + min_cclk = sclk; +#endif + csel = ((bfin_read_PLL_DIV() & CSEL) >> 4); + + for (index = 0; (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) { + bfin_freq_table[index].frequency = cclk >> index; + dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */ + dpm_state_table[index].tscale = (TIME_SCALE / (1 << csel)) - 1; + + pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n", + bfin_freq_table[index].frequency, + dpm_state_table[index].csel, + dpm_state_table[index].tscale); + } + return; +} + +static void bfin_adjust_core_timer(void *info) { - /* The driver only support single cpu */ - if (cpu != 0) - return -1; + unsigned int tscale; + unsigned int index = *(unsigned int *)info; + + /* we have to adjust the core timer, because it is using cclk */ + tscale = dpm_state_table[index].tscale; + bfin_write_TSCALE(tscale); + return; +} +static unsigned int bfin_getfreq_khz(unsigned int cpu) +{ + /* Both CoreA/B have the same core clock */ return get_cclk() / 1000; } -static int bfin_target(struct cpufreq_policy *policy, +static int bfin_target(struct cpufreq_policy *poli, unsigned int target_freq, unsigned int relation) { - unsigned int index, plldiv, tscale; + unsigned int index, plldiv, cpu; unsigned long flags, cclk_hz; struct cpufreq_freqs freqs; +#if defined(CONFIG_CYCLES_CLOCKSOURCE) cycles_t cycles; +#endif - if (cpufreq_frequency_table_target(policy, bfin_freq_table, - target_freq, relation, &index)) - return -EINVAL; - - cclk_hz = bfin_freq_table[index].frequency; - - freqs.old = bfin_getfreq_khz(0); - freqs.new = cclk_hz; - freqs.cpu = 0; - - pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n", - cclk_hz, target_freq, freqs.old); - - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - local_irq_save_hw(flags); - plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel; - tscale = dpm_state_table[index].tscale; - bfin_write_PLL_DIV(plldiv); - /* we have to adjust the core timer, because it is using cclk */ - bfin_write_TSCALE(tscale); - cycles = get_cycles(); - SSYNC(); - cycles += 10; /* ~10 cycles we lose after get_cycles() */ - __bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index); - __bfin_cycles_mod = index; - local_irq_restore_hw(flags); - /* TODO: just test case for cycles clock source, remove later */ - pr_debug("cpufreq: done\n"); - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + for_each_online_cpu(cpu) { + struct cpufreq_policy *policy = cpufreq_cpu_get(cpu); + + if (!policy) + continue; + + if (cpufreq_frequency_table_target(policy, bfin_freq_table, + target_freq, relation, &index)) + return -EINVAL; + + cclk_hz = bfin_freq_table[index].frequency; + + freqs.old = bfin_getfreq_khz(0); + freqs.new = cclk_hz; + freqs.cpu = cpu; + + pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n", + cclk_hz, target_freq, freqs.old); + + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + if (cpu == CPUFREQ_CPU) { + local_irq_save_hw(flags); + plldiv = (bfin_read_PLL_DIV() & SSEL) | + dpm_state_table[index].csel; + bfin_write_PLL_DIV(plldiv); + on_each_cpu(bfin_adjust_core_timer, &index, 1); +#if defined(CONFIG_CYCLES_CLOCKSOURCE) + cycles = get_cycles(); + SSYNC(); + cycles += 10; /* ~10 cycles we lose after get_cycles() */ + __bfin_cycles_off += + (cycles << __bfin_cycles_mod) - (cycles << index); + __bfin_cycles_mod = index; +#endif + local_irq_restore_hw(flags); + } + /* TODO: just test case for cycles clock source, remove later */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + } + pr_debug("cpufreq: done\n"); return 0; } @@ -110,37 +162,16 @@ static int bfin_verify_speed(struct cpufreq_policy *policy) static int __init __bfin_cpu_init(struct cpufreq_policy *policy) { - unsigned long cclk, sclk, csel, min_cclk; - int index; - - if (policy->cpu != 0) - return -EINVAL; + unsigned long cclk, sclk; cclk = get_cclk() / 1000; sclk = get_sclk() / 1000; -#if ANOMALY_05000273 || ANOMALY_05000274 || \ - (!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE)) - min_cclk = sclk * 2; -#else - min_cclk = sclk; -#endif - csel = ((bfin_read_PLL_DIV() & CSEL) >> 4); - - for (index = 0; (cclk >> index) >= min_cclk && csel <= 3; index++, csel++) { - bfin_freq_table[index].frequency = cclk >> index; - dpm_state_table[index].csel = csel << 4; /* Shift now into PLL_DIV bitpos */ - dpm_state_table[index].tscale = (TIME_SCALE / (1 << csel)) - 1; - - pr_debug("cpufreq: freq:%d csel:0x%x tscale:%d\n", - bfin_freq_table[index].frequency, - dpm_state_table[index].csel, - dpm_state_table[index].tscale); - } + if (policy->cpu == CPUFREQ_CPU) + bfin_init_tables(cclk, sclk); policy->cpuinfo.transition_latency = 50000; /* 50us assumed */ - /*Now ,only support one cpu */ policy->cur = cclk; cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu); return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table); -- cgit v1.1