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:
Родитель
682f5dc4ed
Коммит
0d152c27e3
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче