Commit 6c2b7072 authored by Graf Yang's avatar Graf Yang Committed by Mike Frysinger

Blackfin: add support for cpufreq on SMP systems

Signed-off-by: default avatarGraf Yang <graf.yang@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
parent 3b82790c
...@@ -1237,7 +1237,6 @@ config PM_BFIN_WAKE_GP ...@@ -1237,7 +1237,6 @@ config PM_BFIN_WAKE_GP
endmenu endmenu
menu "CPU Frequency scaling" menu "CPU Frequency scaling"
depends on !SMP
source "drivers/cpufreq/Kconfig" source "drivers/cpufreq/Kconfig"
......
...@@ -23,9 +23,7 @@ ...@@ -23,9 +23,7 @@
*/ */
#ifndef CONFIG_CPU_FREQ #ifndef CONFIG_CPU_FREQ
#define TIME_SCALE 1 # define TIME_SCALE 1
#define __bfin_cycles_off (0)
#define __bfin_cycles_mod (0)
#else #else
/* /*
* Blackfin CPU frequency scaling supports max Core Clock 1, 1/2 and 1/4 . * Blackfin CPU frequency scaling supports max Core Clock 1, 1/2 and 1/4 .
...@@ -33,8 +31,11 @@ ...@@ -33,8 +31,11 @@
* adjust the Core Timer Presale Register. This way we don't lose time. * adjust the Core Timer Presale Register. This way we don't lose time.
*/ */
#define TIME_SCALE 4 #define TIME_SCALE 4
# ifdef CONFIG_CYCLES_CLOCKSOURCE
extern unsigned long long __bfin_cycles_off; extern unsigned long long __bfin_cycles_off;
extern unsigned int __bfin_cycles_mod; extern unsigned int __bfin_cycles_mod;
# endif
#endif #endif
#if defined(CONFIG_TICKSOURCE_CORETMR) #if defined(CONFIG_TICKSOURCE_CORETMR)
......
...@@ -51,7 +51,11 @@ ...@@ -51,7 +51,11 @@
static notrace cycle_t bfin_read_cycles(struct clocksource *cs) static notrace cycle_t bfin_read_cycles(struct clocksource *cs)
{ {
#ifdef CONFIG_CPU_FREQ
return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod); return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod);
#else
return get_cycles();
#endif
} }
static struct clocksource bfin_cs_cycles = { static struct clocksource bfin_cs_cycles = {
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <asm/time.h> #include <asm/time.h>
#include <asm/dpmc.h> #include <asm/dpmc.h>
#define CPUFREQ_CPU 0
/* this is the table of CCLK frequencies, in Hz */ /* this is the table of CCLK frequencies, in Hz */
/* .index is the entry in the auxillary dpm_state_table[] */ /* .index is the entry in the auxillary dpm_state_table[] */
static struct cpufreq_frequency_table bfin_freq_table[] = { static struct cpufreq_frequency_table bfin_freq_table[] = {
...@@ -41,64 +43,114 @@ static struct bfin_dpm_state { ...@@ -41,64 +43,114 @@ static struct bfin_dpm_state {
unsigned int tscale; /* change the divider on the core timer interrupt */ unsigned int tscale; /* change the divider on the core timer interrupt */
} dpm_state_table[3]; } dpm_state_table[3];
#if defined(CONFIG_CYCLES_CLOCKSOURCE)
/* /*
normalized to maximum frequncy offset for CYCLES, * normalized to maximum frequncy offset for CYCLES,
used in time-ts cycles clock source, but could be used * used in time-ts cycles clock source, but could be used
somewhere also. * somewhere also.
*/ */
unsigned long long __bfin_cycles_off; unsigned long long __bfin_cycles_off;
unsigned int __bfin_cycles_mod; 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 */ unsigned int tscale;
if (cpu != 0) unsigned int index = *(unsigned int *)info;
return -1;
/* 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; 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 target_freq, unsigned int relation)
{ {
unsigned int index, plldiv, tscale; unsigned int index, plldiv, cpu;
unsigned long flags, cclk_hz; unsigned long flags, cclk_hz;
struct cpufreq_freqs freqs; struct cpufreq_freqs freqs;
#if defined(CONFIG_CYCLES_CLOCKSOURCE)
cycles_t cycles; cycles_t cycles;
#endif
if (cpufreq_frequency_table_target(policy, bfin_freq_table, for_each_online_cpu(cpu) {
target_freq, relation, &index)) struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
return -EINVAL;
if (!policy)
cclk_hz = bfin_freq_table[index].frequency; continue;
freqs.old = bfin_getfreq_khz(0); if (cpufreq_frequency_table_target(policy, bfin_freq_table,
freqs.new = cclk_hz; target_freq, relation, &index))
freqs.cpu = 0; return -EINVAL;
pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n", cclk_hz = bfin_freq_table[index].frequency;
cclk_hz, target_freq, freqs.old);
freqs.old = bfin_getfreq_khz(0);
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); freqs.new = cclk_hz;
local_irq_save_hw(flags); freqs.cpu = cpu;
plldiv = (bfin_read_PLL_DIV() & SSEL) | dpm_state_table[index].csel;
tscale = dpm_state_table[index].tscale; pr_debug("cpufreq: changing cclk to %lu; target = %u, oldfreq = %u\n",
bfin_write_PLL_DIV(plldiv); cclk_hz, target_freq, freqs.old);
/* we have to adjust the core timer, because it is using cclk */
bfin_write_TSCALE(tscale); cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
cycles = get_cycles(); if (cpu == CPUFREQ_CPU) {
SSYNC(); local_irq_save_hw(flags);
cycles += 10; /* ~10 cycles we lose after get_cycles() */ plldiv = (bfin_read_PLL_DIV() & SSEL) |
__bfin_cycles_off += (cycles << __bfin_cycles_mod) - (cycles << index); dpm_state_table[index].csel;
__bfin_cycles_mod = index; bfin_write_PLL_DIV(plldiv);
local_irq_restore_hw(flags); on_each_cpu(bfin_adjust_core_timer, &index, 1);
/* TODO: just test case for cycles clock source, remove later */ #if defined(CONFIG_CYCLES_CLOCKSOURCE)
pr_debug("cpufreq: done\n"); cycles = get_cycles();
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); 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; return 0;
} }
...@@ -110,37 +162,16 @@ static int bfin_verify_speed(struct cpufreq_policy *policy) ...@@ -110,37 +162,16 @@ static int bfin_verify_speed(struct cpufreq_policy *policy)
static int __init __bfin_cpu_init(struct cpufreq_policy *policy) static int __init __bfin_cpu_init(struct cpufreq_policy *policy)
{ {
unsigned long cclk, sclk, csel, min_cclk; unsigned long cclk, sclk;
int index;
if (policy->cpu != 0)
return -EINVAL;
cclk = get_cclk() / 1000; cclk = get_cclk() / 1000;
sclk = get_sclk() / 1000; sclk = get_sclk() / 1000;
#if ANOMALY_05000273 || ANOMALY_05000274 || \ if (policy->cpu == CPUFREQ_CPU)
(!defined(CONFIG_BF54x) && defined(CONFIG_BFIN_EXTMEM_DCACHEABLE)) bfin_init_tables(cclk, sclk);
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);
}
policy->cpuinfo.transition_latency = 50000; /* 50us assumed */ policy->cpuinfo.transition_latency = 50000; /* 50us assumed */
/*Now ,only support one cpu */
policy->cur = cclk; policy->cur = cclk;
cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu); cpufreq_frequency_table_get_attr(bfin_freq_table, policy->cpu);
return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table); return cpufreq_frequency_table_cpuinfo(policy, bfin_freq_table);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment