Blackfin: SMP: make core timers per-cpu clock events for HRT
authorYi Li <yi.li@analog.com>
Mon, 28 Dec 2009 10:21:49 +0000 (10:21 +0000)
committerMike Frysinger <vapier@gentoo.org>
Tue, 9 Mar 2010 05:30:48 +0000 (00:30 -0500)
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>
arch/blackfin/Kconfig
arch/blackfin/include/asm/time.h
arch/blackfin/kernel/time-ts.c
arch/blackfin/mach-bf561/include/mach/smp.h
arch/blackfin/mach-bf561/smp.c
arch/blackfin/mach-common/ints-priority.c
arch/blackfin/mach-common/smp.c

index b483639..0bd26db 100644 (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)"
-
-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
index 589e937..767b938 100644 (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
index 17c38c5..a351f97 100644 (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)
-{
-       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
 }
index 390c7f4..2c8c514 100644 (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 */
index ec93f3e..9036942 100644 (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
+
+}
index a5d2434..efbdb6a 100644 (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
index eddb720..b343ab3 100644 (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();
 
        /*