Blackfin: SMP: make core timers per-cpu clock events for HRT

SMP systems require per-cpu local clock event devices in order to enable
HRT support.  One a BF561, we can use local core timer for this purpose.
Originally, there was one global core-timer clock event device set up for
core A.

To accomplish this feat, we need to split the gptimer0/core timer logic
so that each is a standalone clock event.  There is no requirement that
we only have one clock event source anyways.  Once we have this, we just
define per-cpu clock event devices for each local core timer.

Signed-off-by: Yi Li <yi.li@analog.com>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
Yi Li 2009-12-28 10:21:49 +00:00 committed by Mike Frysinger
parent 682f5dc4ed
commit 0d152c27e3
7 changed files with 142 additions and 119 deletions

View File

@ -236,7 +236,7 @@ endchoice
config SMP
depends on BF561
select GENERIC_CLOCKEVENTS
select TICKSOURCE_CORETMR
bool "Symmetric multi-processing support"
---help---
This enables support for systems with more than one CPU,
@ -610,23 +610,23 @@ config GENERIC_CLOCKEVENTS
bool "Generic clock events"
default y
choice
prompt "Kernel Tick Source"
menu "Clock event device"
depends on GENERIC_CLOCKEVENTS
default TICKSOURCE_CORETMR
config TICKSOURCE_GPTMR0
bool "Gptimer0 (SCLK domain)"
bool "GPTimer0"
depends on !SMP
select BFIN_GPTIMERS
config TICKSOURCE_CORETMR
bool "Core timer (CCLK domain)"
bool "Core timer"
default y
endmenu
endchoice
config CYCLES_CLOCKSOURCE
bool "Use 'CYCLES' as a clocksource"
menu "Clock souce"
depends on GENERIC_CLOCKEVENTS
config CYCLES_CLOCKSOURCE
bool "CYCLES"
default y
depends on !BFIN_SCRATCH_REG_CYCLES
depends on !SMP
help
@ -637,10 +637,10 @@ config CYCLES_CLOCKSOURCE
writing the registers will most likely crash the kernel.
config GPTMR0_CLOCKSOURCE
bool "Use GPTimer0 as a clocksource"
bool "GPTimer0"
select BFIN_GPTIMERS
depends on GENERIC_CLOCKEVENTS
depends on !TICKSOURCE_GPTMR0
endmenu
config ARCH_USES_GETTIMEOFFSET
depends on !GENERIC_CLOCKEVENTS

View File

@ -37,5 +37,9 @@ extern unsigned long long __bfin_cycles_off;
extern unsigned int __bfin_cycles_mod;
#endif
extern void __init setup_core_timer(void);
#if defined(CONFIG_TICKSOURCE_CORETMR)
extern void bfin_coretmr_init(void);
extern void bfin_coretmr_clockevent_init(void);
#endif
#endif

View File

@ -132,7 +132,6 @@ static int __init bfin_cs_gptimer0_init(void)
# define bfin_cs_gptimer0_init()
#endif
#if defined(CONFIG_GPTMR0_CLOCKSOURCE) || defined(CONFIG_CYCLES_CLOCKSOURCE)
/* prefer to use cycles since it has higher rating */
notrace unsigned long long sched_clock(void)
@ -145,47 +144,8 @@ notrace unsigned long long sched_clock(void)
}
#endif
#ifdef CONFIG_CORE_TIMER_IRQ_L1
__attribute__((l1_text))
#endif
irqreturn_t timer_interrupt(int irq, void *dev_id);
static int bfin_timer_set_next_event(unsigned long, \
struct clock_event_device *);
static void bfin_timer_set_mode(enum clock_event_mode, \
struct clock_event_device *);
static struct clock_event_device clockevent_bfin = {
#if defined(CONFIG_TICKSOURCE_GPTMR0)
.name = "bfin_gptimer0",
.rating = 300,
.irq = IRQ_TIMER0,
#else
.name = "bfin_core_timer",
.rating = 350,
.irq = IRQ_CORETMR,
#endif
.shift = 32,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = bfin_timer_set_next_event,
.set_mode = bfin_timer_set_mode,
};
static struct irqaction bfin_timer_irq = {
#if defined(CONFIG_TICKSOURCE_GPTMR0)
.name = "Blackfin GPTimer0",
#else
.name = "Blackfin CoreTimer",
#endif
.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_PERCPU,
.handler = timer_interrupt,
.dev_id = &clockevent_bfin,
};
#if defined(CONFIG_TICKSOURCE_GPTMR0)
static int bfin_timer_set_next_event(unsigned long cycles,
static int bfin_gptmr0_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
disable_gptimers(TIMER0bit);
@ -196,7 +156,7 @@ static int bfin_timer_set_next_event(unsigned long cycles,
return 0;
}
static void bfin_timer_set_mode(enum clock_event_mode mode,
static void bfin_gptmr0_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
@ -224,25 +184,65 @@ static void bfin_timer_set_mode(enum clock_event_mode mode,
}
}
static void bfin_timer_ack(void)
static void bfin_gptmr0_ack(void)
{
set_gptimer_status(TIMER_GROUP1, TIMER_STATUS_TIMIL0);
}
static void __init bfin_timer_init(void)
static void __init bfin_gptmr0_init(void)
{
disable_gptimers(TIMER0bit);
}
static unsigned long __init bfin_clockevent_check(void)
#ifdef CONFIG_CORE_TIMER_IRQ_L1
__attribute__((l1_text))
#endif
irqreturn_t bfin_gptmr0_interrupt(int irq, void *dev_id)
{
setup_irq(IRQ_TIMER0, &bfin_timer_irq);
return get_sclk();
struct clock_event_device *evt = dev_id;
smp_mb();
evt->event_handler(evt);
bfin_gptmr0_ack();
return IRQ_HANDLED;
}
#else /* CONFIG_TICKSOURCE_CORETMR */
static struct irqaction gptmr0_irq = {
.name = "Blackfin GPTimer0",
.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_PERCPU,
.handler = bfin_gptmr0_interrupt,
};
static int bfin_timer_set_next_event(unsigned long cycles,
static struct clock_event_device clockevent_gptmr0 = {
.name = "bfin_gptimer0",
.rating = 300,
.irq = IRQ_TIMER0,
.shift = 32,
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
.set_next_event = bfin_gptmr0_set_next_event,
.set_mode = bfin_gptmr0_set_mode,
};
static void __init bfin_gptmr0_clockevent_init(struct clock_event_device *evt)
{
unsigned long clock_tick;
clock_tick = get_sclk();
evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(-1, evt);
evt->min_delta_ns = clockevent_delta2ns(100, evt);
evt->cpumask = cpumask_of(0);
clockevents_register_device(evt);
}
#endif /* CONFIG_TICKSOURCE_GPTMR0 */
#if defined(CONFIG_TICKSOURCE_CORETMR)
/* per-cpu local core timer */
static DEFINE_PER_CPU(struct clock_event_device, coretmr_events);
static int bfin_coretmr_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
bfin_write_TCNTL(TMPWR);
@ -253,7 +253,7 @@ static int bfin_timer_set_next_event(unsigned long cycles,
return 0;
}
static void bfin_timer_set_mode(enum clock_event_mode mode,
static void bfin_coretmr_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
switch (mode) {
@ -285,19 +285,13 @@ static void bfin_timer_set_mode(enum clock_event_mode mode,
}
}
static void bfin_timer_ack(void)
{
}
static void __init bfin_timer_init(void)
void bfin_coretmr_init(void)
{
/* power up the timer, but don't enable it just yet */
bfin_write_TCNTL(TMPWR);
CSYNC();
/*
* the TSCALE prescaler counter.
*/
/* the TSCALE prescaler counter. */
bfin_write_TSCALE(TIME_SCALE - 1);
bfin_write_TPERIOD(0);
bfin_write_TCOUNT(0);
@ -305,48 +299,51 @@ static void __init bfin_timer_init(void)
CSYNC();
}
static unsigned long __init bfin_clockevent_check(void)
#ifdef CONFIG_CORE_TIMER_IRQ_L1
__attribute__((l1_text))
#endif
irqreturn_t bfin_coretmr_interrupt(int irq, void *dev_id)
{
setup_irq(IRQ_CORETMR, &bfin_timer_irq);
return get_cclk() / TIME_SCALE;
}
int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(coretmr_events, cpu);
void __init setup_core_timer(void)
{
bfin_timer_init();
bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL);
}
#endif /* CONFIG_TICKSOURCE_GPTMR0 */
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "do_timer()" routine every clocktick
*/
irqreturn_t timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
smp_mb();
evt->event_handler(evt);
bfin_timer_ack();
return IRQ_HANDLED;
}
static int __init bfin_clockevent_init(void)
static struct irqaction coretmr_irq = {
.name = "Blackfin CoreTimer",
.flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_PERCPU,
.handler = bfin_coretmr_interrupt,
};
void bfin_coretmr_clockevent_init(void)
{
unsigned long timer_clk;
unsigned long clock_tick;
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(coretmr_events, cpu);
timer_clk = bfin_clockevent_check();
evt->name = "bfin_core_timer";
evt->rating = 350;
evt->irq = -1;
evt->shift = 32;
evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
evt->set_next_event = bfin_coretmr_set_next_event;
evt->set_mode = bfin_coretmr_set_mode;
bfin_timer_init();
clock_tick = get_cclk() / TIME_SCALE;
evt->mult = div_sc(clock_tick, NSEC_PER_SEC, evt->shift);
evt->max_delta_ns = clockevent_delta2ns(-1, evt);
evt->min_delta_ns = clockevent_delta2ns(100, evt);
clockevent_bfin.mult = div_sc(timer_clk, NSEC_PER_SEC, clockevent_bfin.shift);
clockevent_bfin.max_delta_ns = clockevent_delta2ns(-1, &clockevent_bfin);
clockevent_bfin.min_delta_ns = clockevent_delta2ns(100, &clockevent_bfin);
clockevent_bfin.cpumask = cpumask_of(0);
clockevents_register_device(&clockevent_bfin);
evt->cpumask = cpumask_of(cpu);
return 0;
clockevents_register_device(evt);
}
#endif /* CONFIG_TICKSOURCE_CORETMR */
void __init time_init(void)
{
@ -370,5 +367,21 @@ void __init time_init(void)
bfin_cs_cycles_init();
bfin_cs_gptimer0_init();
bfin_clockevent_init();
#if defined(CONFIG_TICKSOURCE_CORETMR)
bfin_coretmr_init();
setup_irq(IRQ_CORETMR, &coretmr_irq);
bfin_coretmr_clockevent_init();
#endif
#if defined(CONFIG_TICKSOURCE_GPTMR0)
bfin_gptmr0_init();
setup_irq(IRQ_TIMER0, &gptmr0_irq);
gptmr0_irq.dev_id = &clockevent_gptmr0;
bfin_gptmr0_clockevent_init(&clockevent_gptmr0);
#endif
#if !defined(CONFIG_TICKSOURCE_CORETMR) && !defined(CONFIG_TICKSOURCE_GPTMR0)
# error at least one clock event device is required
#endif
}

View File

@ -25,4 +25,6 @@ void platform_send_ipi_cpu(unsigned int cpu);
void platform_clear_ipi(unsigned int cpu);
void bfin_local_timer_setup(void);
#endif /* !_MACH_BF561_SMP */

View File

@ -11,6 +11,7 @@
#include <linux/delay.h>
#include <asm/smp.h>
#include <asm/dma.h>
#include <asm/time.h>
static DEFINE_SPINLOCK(boot_lock);
@ -144,3 +145,20 @@ void platform_clear_ipi(unsigned int cpu)
bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + cpu)));
SSYNC();
}
/*
* Setup core B's local core timer.
* In SMP, core timer is used for clock event device.
*/
void __cpuinit bfin_local_timer_setup(void)
{
#if defined(CONFIG_TICKSOURCE_CORETMR)
bfin_coretmr_init();
bfin_coretmr_clockevent_init();
get_irq_chip(IRQ_CORETMR)->unmask(IRQ_CORETMR);
#else
/* Power down the core timer, just to play safe. */
bfin_write_TCNTL(0);
#endif
}

View File

@ -1073,9 +1073,6 @@ int __init init_arch_irq(void)
#endif
#ifdef CONFIG_SMP
#ifdef CONFIG_TICKSOURCE_GPTMR0
case IRQ_TIMER0:
#endif
#ifdef CONFIG_TICKSOURCE_CORETMR
case IRQ_CORETMR:
#endif

View File

@ -365,9 +365,6 @@ int __cpuinit __cpu_up(unsigned int cpu)
static void __cpuinit setup_secondary(unsigned int cpu)
{
#if !defined(CONFIG_TICKSOURCE_GPTMR0)
struct irq_desc *timer_desc;
#endif
unsigned long ilat;
bfin_write_IMASK(0);
@ -382,17 +379,6 @@ static void __cpuinit setup_secondary(unsigned int cpu)
bfin_irq_flags |= IMASK_IVG15 |
IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW;
#if defined(CONFIG_TICKSOURCE_GPTMR0)
/* Power down the core timer, just to play safe. */
bfin_write_TCNTL(0);
/* system timer0 has been setup by CoreA. */
#else
timer_desc = irq_desc + IRQ_CORETMR;
setup_core_timer();
timer_desc->chip->enable(IRQ_CORETMR);
#endif
}
void __cpuinit secondary_start_kernel(void)
@ -435,6 +421,9 @@ void __cpuinit secondary_start_kernel(void)
platform_secondary_init(cpu);
/* setup local core timer */
bfin_local_timer_setup();
local_irq_enable();
/*