Blackfin: SMP: implement cpu_freq support

Re-use some of the existing cpu hotplugging code in the process.

Signed-off-by: Graf Yang <graf.yang@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
Graf Yang 2010-01-28 10:46:55 +00:00 committed by Mike Frysinger
parent 820b127dae
commit 6f546bc3ac
6 changed files with 70 additions and 34 deletions

View File

@ -125,6 +125,9 @@ void unset_dram_srfs(void);
#define VRPAIR(vlev, freq) (((vlev) << 16) | ((freq) >> 16))
#ifdef CONFIG_CPU_FREQ
#define CPUFREQ_CPU 0
#endif
struct bfin_dpmc_platform_data {
const unsigned int *tuple_tab;
unsigned short tabsize;

View File

@ -34,7 +34,7 @@ extern unsigned long dcache_invld_count[NR_CPUS];
void smp_icache_flush_range_others(unsigned long start,
unsigned long end);
#ifdef CONFIG_HOTPLUG_CPU
void coreb_sleep(u32 sic_iwr0, u32 sic_iwr1, u32 sic_iwr2);
void coreb_die(void);
void cpu_die(void);
void platform_cpu_die(void);
int __cpu_disable(void);

View File

@ -5,30 +5,27 @@
* Licensed under the GPL-2 or later.
*/
#include <linux/smp.h>
#include <asm/blackfin.h>
#include <asm/irq.h>
#include <asm/smp.h>
#define SIC_SYSIRQ(irq) (irq - (IRQ_CORETMR + 1))
#include <mach/pll.h>
int hotplug_coreb;
void platform_cpu_die(void)
{
unsigned long iwr[2] = {0, 0};
unsigned long bank = SIC_SYSIRQ(IRQ_SUPPLE_0) / 32;
unsigned long bit = 1 << (SIC_SYSIRQ(IRQ_SUPPLE_0) % 32);
unsigned long iwr;
hotplug_coreb = 1;
iwr[bank] = bit;
/* disable core timer */
bfin_write_TCNTL(0);
/* clear ipi interrupt IRQ_SUPPLE_0 */
/* clear ipi interrupt IRQ_SUPPLE_0 of CoreB */
bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + 1)));
SSYNC();
coreb_sleep(iwr[0], iwr[1], 0);
/* set CoreB wakeup by ipi0, iwr will be discarded */
bfin_iwr_set_sup0(&iwr, &iwr, &iwr);
SSYNC();
coreb_die();
}

View File

@ -162,39 +162,31 @@ ENTRY(_coreb_trampoline_start)
ENDPROC(_coreb_trampoline_start)
ENTRY(_coreb_trampoline_end)
#ifdef CONFIG_HOTPLUG_CPU
.section ".text"
ENTRY(_set_sicb_iwr)
P0.H = hi(SICB_IWR0);
P0.L = lo(SICB_IWR0);
P1.H = hi(SICB_IWR1);
P1.L = lo(SICB_IWR1);
[P0] = R0;
[P1] = R1;
SSYNC;
RTS;
ENDPROC(_set_sicb_iwr)
ENTRY(_coreb_sleep)
ENTRY(_coreb_die)
sp.l = lo(INITIAL_STACK);
sp.h = hi(INITIAL_STACK);
fp = sp;
usp = sp;
call _set_sicb_iwr;
CLI R2;
SSYNC;
IDLE;
STI R2;
R0 = IWR_DISABLE_ALL;
R1 = IWR_DISABLE_ALL;
call _set_sicb_iwr;
P0.H = hi(SYSMMR_BASE);
P0.L = lo(SYSMMR_BASE);
[P0 + (SICB_IWR0 - SYSMMR_BASE)] = R0;
[P0 + (SICB_IWR1 - SYSMMR_BASE)] = R0;
SSYNC;
p0.h = hi(COREB_L1_CODE_START);
p0.l = lo(COREB_L1_CODE_START);
jump (p0);
ENDPROC(_coreb_sleep)
ENDPROC(_coreb_die)
#endif
__INIT
ENTRY(_coreb_start)

View File

@ -16,8 +16,6 @@
#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[] = {

View File

@ -61,17 +61,63 @@ err_out:
}
#ifdef CONFIG_CPU_FREQ
# ifdef CONFIG_SMP
static void bfin_idle_this_cpu(void *info)
{
unsigned long flags = 0;
unsigned long iwr0, iwr1, iwr2;
unsigned int cpu = smp_processor_id();
local_irq_save_hw(flags);
bfin_iwr_set_sup0(&iwr0, &iwr1, &iwr2);
platform_clear_ipi(cpu, IRQ_SUPPLE_0);
SSYNC();
asm("IDLE;");
bfin_iwr_restore(iwr0, iwr1, iwr2);
local_irq_restore_hw(flags);
}
static void bfin_idle_cpu(void)
{
smp_call_function(bfin_idle_this_cpu, NULL, 0);
}
static void bfin_wakeup_cpu(void)
{
unsigned int cpu;
unsigned int this_cpu = smp_processor_id();
cpumask_t mask = cpu_online_map;
cpu_clear(this_cpu, mask);
for_each_cpu_mask(cpu, mask)
platform_send_ipi_cpu(cpu, IRQ_SUPPLE_0);
}
# else
static void bfin_idle_cpu(void) {}
static void bfin_wakeup_cpu(void) {}
# endif
static int
vreg_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data)
{
struct cpufreq_freqs *freq = data;
if (freq->cpu != CPUFREQ_CPU)
return 0;
if (val == CPUFREQ_PRECHANGE && freq->old < freq->new) {
bfin_idle_cpu();
bfin_set_vlev(bfin_get_vlev(freq->new));
udelay(pdata->vr_settling_time); /* Wait until Volatge settled */
} else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)
bfin_wakeup_cpu();
} else if (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) {
bfin_idle_cpu();
bfin_set_vlev(bfin_get_vlev(freq->new));
bfin_wakeup_cpu();
}
return 0;
}