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
endmenu
menu "CPU Frequency scaling"
depends on !SMP
source "drivers/cpufreq/Kconfig"
......
......@@ -23,9 +23,7 @@
*/
#ifndef CONFIG_CPU_FREQ
#define TIME_SCALE 1
#define __bfin_cycles_off (0)
#define __bfin_cycles_mod (0)
# define TIME_SCALE 1
#else
/*
* Blackfin CPU frequency scaling supports max Core Clock 1, 1/2 and 1/4 .
......@@ -33,8 +31,11 @@
* adjust the Core Timer Presale Register. This way we don't lose time.
*/
#define TIME_SCALE 4
# ifdef CONFIG_CYCLES_CLOCKSOURCE
extern unsigned long long __bfin_cycles_off;
extern unsigned int __bfin_cycles_mod;
# endif
#endif
#if defined(CONFIG_TICKSOURCE_CORETMR)
......
......@@ -51,7 +51,11 @@
static notrace cycle_t bfin_read_cycles(struct clocksource *cs)
{
#ifdef CONFIG_CPU_FREQ
return __bfin_cycles_off + (get_cycles() << __bfin_cycles_mod);
#else
return get_cycles();
#endif
}
static struct clocksource bfin_cs_cycles = {
......
......@@ -15,6 +15,8 @@
#include <asm/time.h>
#include <asm/dpmc.h>
#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);
......
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