diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index e60ec54df334..ef4f860d5041 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -859,6 +859,7 @@ source "kernel/time/Kconfig" config SMP bool "Symmetric Multi-Processing (EXPERIMENTAL)" depends on EXPERIMENTAL && (REALVIEW_EB_ARM11MP || MACH_REALVIEW_PB11MP) + depends on GENERIC_CLOCKEVENTS select USE_GENERIC_SMP_HELPERS help This enables support for systems with more than one CPU. If you have diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h new file mode 100644 index 000000000000..3f8c9ebb646c --- /dev/null +++ b/arch/arm/include/asm/localtimer.h @@ -0,0 +1,51 @@ +/* + * arch/arm/include/asm/localtimer.h + * + * Copyright (C) 2004-2005 ARM Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef __ASM_ARM_LOCALTIMER_H +#define __ASM_ARM_LOCALTIMER_H + +struct clock_event_device; + +/* + * Setup a per-cpu timer, whether it be a local timer or dummy broadcast + */ +void percpu_timer_setup(void); + +/* + * Called from assembly, this is the local timer IRQ handler + */ +asmlinkage void do_local_timer(struct pt_regs *); + + +#ifdef CONFIG_LOCAL_TIMERS +/* + * Platform provides this to acknowledge a local timer IRQ. + * Returns true if the local timer IRQ is to be processed. + */ +int local_timer_ack(void); + +/* + * Stop a local timer interrupt. + */ +void local_timer_stop(void); + +/* + * Setup a local timer interrupt for a CPU. + */ +void local_timer_setup(struct clock_event_device *); + +#else + +static inline void local_timer_stop(void) +{ +} + +#endif + +#endif diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 5995935338e1..608f2d533ff2 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -55,11 +55,6 @@ extern void smp_store_cpu_info(unsigned int cpuid); */ extern void smp_cross_call(const struct cpumask *mask); -/* - * Broadcast a clock event to other CPUs. - */ -extern void smp_timer_broadcast(const struct cpumask *mask); - /* * Boot a secondary CPU, and assign it the specified idle task. * This also gives us the initial stack to use for this CPU. @@ -100,44 +95,9 @@ extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); #define arch_send_call_function_ipi_mask arch_send_call_function_ipi_mask -/* - * Local timer interrupt handling function (can be IPI'ed). - */ -extern void local_timer_interrupt(void); - -#ifdef CONFIG_LOCAL_TIMERS - -/* - * Stop a local timer interrupt. - */ -extern void local_timer_stop(void); - -/* - * Platform provides this to acknowledge a local timer IRQ - */ -extern int local_timer_ack(void); - -#else - -static inline void local_timer_stop(void) -{ -} - -#endif - -/* - * Setup a local timer interrupt for a CPU. - */ -extern void local_timer_setup(void); - /* * show local interrupt info */ extern void show_local_irqs(struct seq_file *); -/* - * Called from assembly, this is the local timer IRQ handler - */ -asmlinkage void do_local_timer(struct pt_regs *); - #endif /* ifndef __ASM_ARM_SMP_H */ diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 6014dfd22af4..91130e218aef 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -32,6 +34,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -274,9 +277,9 @@ asmlinkage void __cpuinit secondary_start_kernel(void) local_fiq_enable(); /* - * Setup local timer for this CPU. + * Setup the percpu timer for this CPU. */ - local_timer_setup(); + percpu_timer_setup(); calibrate_delay(); @@ -383,10 +386,16 @@ void show_local_irqs(struct seq_file *p) seq_putc(p, '\n'); } +/* + * Timer (local or broadcast) support + */ +static DEFINE_PER_CPU(struct clock_event_device, percpu_clockevent); + static void ipi_timer(void) { + struct clock_event_device *evt = &__get_cpu_var(percpu_clockevent); irq_enter(); - local_timer_interrupt(); + evt->event_handler(evt); irq_exit(); } @@ -405,6 +414,42 @@ asmlinkage void __exception do_local_timer(struct pt_regs *regs) } #endif +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST +static void smp_timer_broadcast(const struct cpumask *mask) +{ + send_ipi_message(mask, IPI_TIMER); +} + +static void broadcast_timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ +} + +static void local_timer_setup(struct clock_event_device *evt) +{ + evt->name = "dummy_timer"; + evt->features = CLOCK_EVT_FEAT_ONESHOT | + CLOCK_EVT_FEAT_PERIODIC | + CLOCK_EVT_FEAT_DUMMY; + evt->rating = 400; + evt->mult = 1; + evt->set_mode = broadcast_timer_set_mode; + evt->broadcast = smp_timer_broadcast; + + clockevents_register_device(evt); +} +#endif + +void __cpuinit percpu_timer_setup(void) +{ + unsigned int cpu = smp_processor_id(); + struct clock_event_device *evt = &per_cpu(percpu_clockevent, cpu); + + evt->cpumask = cpumask_of(cpu); + + local_timer_setup(evt); +} + static DEFINE_SPINLOCK(stop_lock); /* @@ -501,11 +546,6 @@ void smp_send_reschedule(int cpu) send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE); } -void smp_timer_broadcast(const struct cpumask *mask) -{ - send_ipi_message(mask, IPI_TIMER); -} - void smp_send_stop(void) { cpumask_t mask = cpu_online_map; diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c index 1c01d13460f0..cd98e7acd94d 100644 --- a/arch/arm/mach-realview/localtimer.c +++ b/arch/arm/mach-realview/localtimer.c @@ -11,10 +11,8 @@ #include #include #include -#include #include #include -#include #include #include #include @@ -24,18 +22,6 @@ #include #include -static DEFINE_PER_CPU(struct clock_event_device, local_clockevent); - -/* - * Used on SMP for either the local timer or IPI_TIMER - */ -void local_timer_interrupt(void) -{ - struct clock_event_device *clk = &__get_cpu_var(local_clockevent); - - clk->event_handler(clk); -} - #ifdef CONFIG_LOCAL_TIMERS /* set up by the platform code */ @@ -44,7 +30,7 @@ void __iomem *twd_base; static unsigned long mpcore_timer_rate; static void local_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *clk) + struct clock_event_device *evt) { unsigned long ctrl; @@ -140,32 +126,29 @@ static void __cpuinit twd_calibrate_rate(void) /* * Setup the local clock events for a CPU. */ -void __cpuinit local_timer_setup(void) +void __cpuinit local_timer_setup(struct clock_event_device *evt) { - unsigned int cpu = smp_processor_id(); - struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); unsigned long flags; twd_calibrate_rate(); - clk->name = "local_timer"; - clk->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; - clk->rating = 350; - clk->set_mode = local_timer_set_mode; - clk->set_next_event = local_timer_set_next_event; - clk->irq = IRQ_LOCALTIMER; - clk->cpumask = cpumask_of(cpu); - clk->shift = 20; - clk->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, clk->shift); - clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); - clk->min_delta_ns = clockevent_delta2ns(0xf, clk); + evt->name = "local_timer"; + evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT; + evt->rating = 350; + evt->set_mode = local_timer_set_mode; + evt->set_next_event = local_timer_set_next_event; + evt->irq = IRQ_LOCALTIMER; + evt->shift = 20; + evt->mult = div_sc(mpcore_timer_rate, NSEC_PER_SEC, evt->shift); + evt->max_delta_ns = clockevent_delta2ns(0xffffffff, evt); + evt->min_delta_ns = clockevent_delta2ns(0xf, evt); /* Make sure our local interrupt controller has this enabled */ local_irq_save(flags); get_irq_chip(IRQ_LOCALTIMER)->unmask(IRQ_LOCALTIMER); local_irq_restore(flags); - clockevents_register_device(clk); + clockevents_register_device(evt); } /* @@ -176,29 +159,4 @@ void __cpuexit local_timer_stop(void) __raw_writel(0, twd_base + TWD_TIMER_CONTROL); } -#else /* CONFIG_LOCAL_TIMERS */ - -static void dummy_timer_set_mode(enum clock_event_mode mode, - struct clock_event_device *clk) -{ -} - -void __cpuinit local_timer_setup(void) -{ - unsigned int cpu = smp_processor_id(); - struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); - - clk->name = "dummy_timer"; - clk->features = CLOCK_EVT_FEAT_ONESHOT | - CLOCK_EVT_FEAT_PERIODIC | - CLOCK_EVT_FEAT_DUMMY; - clk->rating = 400; - clk->mult = 1; - clk->set_mode = dummy_timer_set_mode; - clk->broadcast = smp_timer_broadcast; - clk->cpumask = cpumask_of(cpu); - - clockevents_register_device(clk); -} - -#endif /* !CONFIG_LOCAL_TIMERS */ +#endif /* CONFIG_LOCAL_TIMERS */ diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index 30a9c68591f6..b34d3a57ce93 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -217,13 +218,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (max_cpus > ncores) max_cpus = ncores; -#if defined(CONFIG_LOCAL_TIMERS) || defined(CONFIG_GENERIC_CLOCKEVENTS_BROADCAST) - /* - * Enable the local timer or broadcast device for the boot CPU. - */ - local_timer_setup(); -#endif - /* * Initialise the present map, which describes the set of CPUs * actually populated at the present time. @@ -239,6 +233,12 @@ void __init smp_prepare_cpus(unsigned int max_cpus) * WFI */ if (max_cpus > 1) { + /* + * Enable the local timer or broadcast device for the + * boot CPU, but only if we have more than one CPU. + */ + percpu_timer_setup(); + scu_enable(); poke_milo(); }