Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+13 −13
Original line number Diff line number Diff line
@@ -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)"

endchoice
	bool "Core timer"
	default y
endmenu

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
+5 −1
Original line number Diff line number Diff line
@@ -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
+105 −92
Original line number Diff line number Diff line
@@ -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)
{
	struct clock_event_device *evt = dev_id;
	smp_mb();
	evt->event_handler(evt);
	bfin_gptmr0_ack();
	return IRQ_HANDLED;
}

static struct irqaction gptmr0_irq = {
	.name		= "Blackfin GPTimer0",
	.flags		= IRQF_DISABLED | IRQF_TIMER | \
			  IRQF_IRQPOLL | IRQF_PERCPU,
	.handler	= bfin_gptmr0_interrupt,
};

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)
{
	setup_irq(IRQ_TIMER0, &bfin_timer_irq);
	return get_sclk();
	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 */

#else /* CONFIG_TICKSOURCE_CORETMR */
#if defined(CONFIG_TICKSOURCE_CORETMR)
/* per-cpu local core timer */
static DEFINE_PER_CPU(struct clock_event_device, coretmr_events);

static int bfin_timer_set_next_event(unsigned long cycles,
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)
{
	setup_irq(IRQ_CORETMR, &bfin_timer_irq);
	return get_cclk() / TIME_SCALE;
}

void __init setup_core_timer(void)
#ifdef CONFIG_CORE_TIMER_IRQ_L1
__attribute__((l1_text))
#endif
irqreturn_t bfin_coretmr_interrupt(int irq, void *dev_id)
{
	bfin_timer_init();
	bfin_timer_set_mode(CLOCK_EVT_MODE_PERIODIC, NULL);
}
#endif /* CONFIG_TICKSOURCE_GPTMR0 */
	int cpu = smp_processor_id();
	struct clock_event_device *evt = &per_cpu(coretmr_events, cpu);

/*
 * 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)
{
	unsigned long timer_clk;

	timer_clk = bfin_clockevent_check();

	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);
static struct irqaction coretmr_irq = {
	.name		= "Blackfin CoreTimer",
	.flags		= IRQF_DISABLED | IRQF_TIMER | \
			  IRQF_IRQPOLL | IRQF_PERCPU,
	.handler	= bfin_coretmr_interrupt,
};

	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)
{
@@ -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
}
+2 −0
Original line number Diff line number Diff line
@@ -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 */
+18 −0
Original line number Diff line number Diff line
@@ -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

}
Loading