Commit 0d152c27 authored by Yi Li's avatar Yi Li Committed by Mike Frysinger

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: default avatarYi Li <yi.li@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
parent 682f5dc4
...@@ -236,7 +236,7 @@ endchoice ...@@ -236,7 +236,7 @@ endchoice
config SMP config SMP
depends on BF561 depends on BF561
select GENERIC_CLOCKEVENTS select TICKSOURCE_CORETMR
bool "Symmetric multi-processing support" bool "Symmetric multi-processing support"
---help--- ---help---
This enables support for systems with more than one CPU, This enables support for systems with more than one CPU,
...@@ -610,23 +610,23 @@ config GENERIC_CLOCKEVENTS ...@@ -610,23 +610,23 @@ config GENERIC_CLOCKEVENTS
bool "Generic clock events" bool "Generic clock events"
default y default y
choice menu "Clock event device"
prompt "Kernel Tick Source"
depends on GENERIC_CLOCKEVENTS depends on GENERIC_CLOCKEVENTS
default TICKSOURCE_CORETMR
config TICKSOURCE_GPTMR0 config TICKSOURCE_GPTMR0
bool "Gptimer0 (SCLK domain)" bool "GPTimer0"
depends on !SMP
select BFIN_GPTIMERS select BFIN_GPTIMERS
config TICKSOURCE_CORETMR config TICKSOURCE_CORETMR
bool "Core timer (CCLK domain)" bool "Core timer"
default y
endchoice endmenu
config CYCLES_CLOCKSOURCE menu "Clock souce"
bool "Use 'CYCLES' as a clocksource"
depends on GENERIC_CLOCKEVENTS depends on GENERIC_CLOCKEVENTS
config CYCLES_CLOCKSOURCE
bool "CYCLES"
default y
depends on !BFIN_SCRATCH_REG_CYCLES depends on !BFIN_SCRATCH_REG_CYCLES
depends on !SMP depends on !SMP
help help
...@@ -637,10 +637,10 @@ config CYCLES_CLOCKSOURCE ...@@ -637,10 +637,10 @@ config CYCLES_CLOCKSOURCE
writing the registers will most likely crash the kernel. writing the registers will most likely crash the kernel.
config GPTMR0_CLOCKSOURCE config GPTMR0_CLOCKSOURCE
bool "Use GPTimer0 as a clocksource" bool "GPTimer0"
select BFIN_GPTIMERS select BFIN_GPTIMERS
depends on GENERIC_CLOCKEVENTS
depends on !TICKSOURCE_GPTMR0 depends on !TICKSOURCE_GPTMR0
endmenu
config ARCH_USES_GETTIMEOFFSET config ARCH_USES_GETTIMEOFFSET
depends on !GENERIC_CLOCKEVENTS depends on !GENERIC_CLOCKEVENTS
......
...@@ -37,5 +37,9 @@ extern unsigned long long __bfin_cycles_off; ...@@ -37,5 +37,9 @@ extern unsigned long long __bfin_cycles_off;
extern unsigned int __bfin_cycles_mod; extern unsigned int __bfin_cycles_mod;
#endif #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 #endif
...@@ -132,7 +132,6 @@ static int __init bfin_cs_gptimer0_init(void) ...@@ -132,7 +132,6 @@ static int __init bfin_cs_gptimer0_init(void)
# define bfin_cs_gptimer0_init() # define bfin_cs_gptimer0_init()
#endif #endif
#if defined(CONFIG_GPTMR0_CLOCKSOURCE) || defined(CONFIG_CYCLES_CLOCKSOURCE) #if defined(CONFIG_GPTMR0_CLOCKSOURCE) || defined(CONFIG_CYCLES_CLOCKSOURCE)
/* prefer to use cycles since it has higher rating */ /* prefer to use cycles since it has higher rating */
notrace unsigned long long sched_clock(void) notrace unsigned long long sched_clock(void)
...@@ -145,47 +144,8 @@ notrace unsigned long long sched_clock(void) ...@@ -145,47 +144,8 @@ notrace unsigned long long sched_clock(void)
} }
#endif #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) #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) struct clock_event_device *evt)
{ {
disable_gptimers(TIMER0bit); disable_gptimers(TIMER0bit);
...@@ -196,7 +156,7 @@ static int bfin_timer_set_next_event(unsigned long cycles, ...@@ -196,7 +156,7 @@ static int bfin_timer_set_next_event(unsigned long cycles,
return 0; 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) struct clock_event_device *evt)
{ {
switch (mode) { switch (mode) {
...@@ -224,25 +184,65 @@ static void bfin_timer_set_mode(enum clock_event_mode 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); 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); 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); struct clock_event_device *evt = dev_id;
return get_sclk(); 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) struct clock_event_device *evt)
{ {
bfin_write_TCNTL(TMPWR); bfin_write_TCNTL(TMPWR);
...@@ -253,7 +253,7 @@ static int bfin_timer_set_next_event(unsigned long cycles, ...@@ -253,7 +253,7 @@ static int bfin_timer_set_next_event(unsigned long cycles,
return 0; 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) struct clock_event_device *evt)
{ {
switch (mode) { switch (mode) {
...@@ -285,19 +285,13 @@ static void bfin_timer_set_mode(enum clock_event_mode mode, ...@@ -285,19 +285,13 @@ static void bfin_timer_set_mode(enum clock_event_mode mode,
} }
} }
static void bfin_timer_ack(void) void bfin_coretmr_init(void)
{
}
static void __init bfin_timer_init(void)
{ {
/* power up the timer, but don't enable it just yet */ /* power up the timer, but don't enable it just yet */
bfin_write_TCNTL(TMPWR); bfin_write_TCNTL(TMPWR);
CSYNC(); CSYNC();
/* /* the TSCALE prescaler counter. */
* the TSCALE prescaler counter.
*/
bfin_write_TSCALE(TIME_SCALE - 1); bfin_write_TSCALE(TIME_SCALE - 1);
bfin_write_TPERIOD(0); bfin_write_TPERIOD(0);
bfin_write_TCOUNT(0); bfin_write_TCOUNT(0);
...@@ -305,48 +299,51 @@ static void __init bfin_timer_init(void) ...@@ -305,48 +299,51 @@ static void __init bfin_timer_init(void)
CSYNC(); CSYNC();
} }
static unsigned long __init bfin_clockevent_check(void) #ifdef CONFIG_CORE_TIMER_IRQ_L1
{ __attribute__((l1_text))
setup_irq(IRQ_CORETMR, &bfin_timer_irq); #endif
return get_cclk() / TIME_SCALE; irqreturn_t bfin_coretmr_interrupt(int irq, void *dev_id)
}
void __init setup_core_timer(void)
{ {
bfin_timer_init(); int cpu = smp_processor_id();
bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL); struct clock_event_device *evt = &per_cpu(coretmr_events, cpu);
}
#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(); smp_mb();
evt->event_handler(evt); evt->event_handler(evt);
bfin_timer_ack();
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static int __init bfin_clockevent_init(void) static struct irqaction coretmr_irq = {
{ .name = "Blackfin CoreTimer",
unsigned long timer_clk; .flags = IRQF_DISABLED | IRQF_TIMER | \
IRQF_IRQPOLL | IRQF_PERCPU,
timer_clk = bfin_clockevent_check(); .handler = bfin_coretmr_interrupt,
};
bfin_timer_init();
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);
return 0; void bfin_coretmr_clockevent_init(void)
{
unsigned long clock_tick;
unsigned int cpu = smp_processor_id();
struct clock_event_device *evt = &per_cpu(coretmr_events, cpu);
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;
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);
evt->cpumask = cpumask_of(cpu);
clockevents_register_device(evt);
} }
#endif /* CONFIG_TICKSOURCE_CORETMR */
void __init time_init(void) void __init time_init(void)
{ {
...@@ -370,5 +367,21 @@ void __init time_init(void) ...@@ -370,5 +367,21 @@ void __init time_init(void)
bfin_cs_cycles_init(); bfin_cs_cycles_init();
bfin_cs_gptimer0_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); ...@@ -25,4 +25,6 @@ void platform_send_ipi_cpu(unsigned int cpu);
void platform_clear_ipi(unsigned int cpu); void platform_clear_ipi(unsigned int cpu);
void bfin_local_timer_setup(void);
#endif /* !_MACH_BF561_SMP */ #endif /* !_MACH_BF561_SMP */
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <asm/smp.h> #include <asm/smp.h>
#include <asm/dma.h> #include <asm/dma.h>
#include <asm/time.h>
static DEFINE_SPINLOCK(boot_lock); static DEFINE_SPINLOCK(boot_lock);
...@@ -144,3 +145,20 @@ void platform_clear_ipi(unsigned int cpu) ...@@ -144,3 +145,20 @@ void platform_clear_ipi(unsigned int cpu)
bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + cpu))); bfin_write_SICB_SYSCR(bfin_read_SICB_SYSCR() | (1 << (10 + cpu)));
SSYNC(); 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) ...@@ -1073,9 +1073,6 @@ int __init init_arch_irq(void)
#endif #endif
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
#ifdef CONFIG_TICKSOURCE_GPTMR0
case IRQ_TIMER0:
#endif
#ifdef CONFIG_TICKSOURCE_CORETMR #ifdef CONFIG_TICKSOURCE_CORETMR
case IRQ_CORETMR: case IRQ_CORETMR:
#endif #endif
......
...@@ -365,9 +365,6 @@ int __cpuinit __cpu_up(unsigned int cpu) ...@@ -365,9 +365,6 @@ int __cpuinit __cpu_up(unsigned int cpu)
static void __cpuinit setup_secondary(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; unsigned long ilat;
bfin_write_IMASK(0); bfin_write_IMASK(0);
...@@ -382,17 +379,6 @@ static void __cpuinit setup_secondary(unsigned int cpu) ...@@ -382,17 +379,6 @@ static void __cpuinit setup_secondary(unsigned int cpu)
bfin_irq_flags |= IMASK_IVG15 | bfin_irq_flags |= IMASK_IVG15 |
IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 | IMASK_IVG14 | IMASK_IVG13 | IMASK_IVG12 | IMASK_IVG11 |
IMASK_IVG10 | IMASK_IVG9 | IMASK_IVG8 | IMASK_IVG7 | IMASK_IVGHW; 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) void __cpuinit secondary_start_kernel(void)
...@@ -435,6 +421,9 @@ void __cpuinit secondary_start_kernel(void) ...@@ -435,6 +421,9 @@ void __cpuinit secondary_start_kernel(void)
platform_secondary_init(cpu); platform_secondary_init(cpu);
/* setup local core timer */
bfin_local_timer_setup();
local_irq_enable(); local_irq_enable();
/* /*
......
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