diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index b82828e768ad4570595a99d0f85e3f77c2e55582..a0aeecc33c739434113ee1cc25813d02063fdbba 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -33,6 +33,11 @@ config GENERIC_CLOCKEVENTS bool default n +config GENERIC_CLOCKEVENTS_BROADCAST + bool + depends on GENERIC_CLOCKEVENTS + default y if SMP && !LOCAL_TIMERS + config MMU bool default y diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index aef6f9ab900ebebe3bd73ee4cbb045b4b601767f..e9dfbab46cb65ef0a6e85d8aa90a9cc0cc0f1522 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -290,6 +290,11 @@ asmlinkage void __cpuinit secondary_start_kernel(void) local_irq_enable(); local_fiq_enable(); + /* + * Setup local timer for this CPU. + */ + local_timer_setup(cpu); + calibrate_delay(); smp_store_cpu_info(cpu); @@ -299,11 +304,6 @@ asmlinkage void __cpuinit secondary_start_kernel(void) */ cpu_set(cpu, cpu_online_map); - /* - * Setup local timer for this CPU. - */ - local_timer_setup(cpu); - /* * OK, it's off to the idle thread for us */ diff --git a/arch/arm/mach-realview/Makefile b/arch/arm/mach-realview/Makefile index 36e76ba937fc031286eeb4fa7acf46b13e37cd64..ca1e390c3c28795f3873bc8249e3eb3c74a8f078 100644 --- a/arch/arm/mach-realview/Makefile +++ b/arch/arm/mach-realview/Makefile @@ -4,6 +4,5 @@ obj-y := core.o clock.o obj-$(CONFIG_MACH_REALVIEW_EB) += realview_eb.o -obj-$(CONFIG_SMP) += platsmp.o headsmp.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o localtimer.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_LOCAL_TIMERS) += localtimer.o diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c index 6c68deed84dc1e5eae9d6670c7e5a707a6652b6c..8cabfec31da2769fdac4d17d336095afa55117ba 100644 --- a/arch/arm/mach-realview/core.c +++ b/arch/arm/mach-realview/core.c @@ -596,12 +596,20 @@ static void __init realview_clocksource_init(void) } /* - * Set up timer interrupt, and return the current time in seconds. + * Set up the clock source and clock events devices */ static void __init realview_timer_init(void) { u32 val; +#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST + /* + * The dummy clock device has to be registered before the main device + * so that the latter will broadcast the clock events + */ + local_timer_setup(smp_processor_id()); +#endif + /* * set clock frequency: * REALVIEW_REFCLK is 32KHz diff --git a/arch/arm/mach-realview/localtimer.c b/arch/arm/mach-realview/localtimer.c index c7bdf04ab094d1f82f2afbb52c204154cf0dfef4..529eb6979e614cab73b9cf61b2d6cc968cc69ea9 100644 --- a/arch/arm/mach-realview/localtimer.c +++ b/arch/arm/mach-realview/localtimer.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -25,6 +27,20 @@ #define TWD_BASE(cpu) (__io_address(REALVIEW_TWD_BASE) + \ ((cpu) * REALVIEW_TWD_SIZE)) +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 + static unsigned long mpcore_timer_rate; /* @@ -127,3 +143,26 @@ void __cpuexit local_timer_stop(unsigned int cpu) { __raw_writel(0, TWD_BASE(cpu) + 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(unsigned int cpu) +{ + struct clock_event_device *clk = &per_cpu(local_clockevent, cpu); + + clk->name = "dummy_timer"; + clk->features = CLOCK_EVT_FEAT_DUMMY; + clk->rating = 200; + clk->set_mode = dummy_timer_set_mode; + clk->broadcast = smp_timer_broadcast; + clk->cpumask = cpumask_of_cpu(cpu); + + clockevents_register_device(clk); +} + +#endif /* !CONFIG_LOCAL_TIMERS */ diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index fce3596f9950a6aea5d8489d9d674a99759ad7b0..bb5eaa48520d2d595a7529678e69ed1aff34c0ca 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -187,10 +187,14 @@ void __init smp_prepare_cpus(unsigned int max_cpus) if (max_cpus > ncores) max_cpus = ncores; +#ifdef CONFIG_LOCAL_TIMERS /* - * Enable the local timer for primary CPU + * Enable the local timer for primary CPU. If the device is + * dummy (!CONFIG_LOCAL_TIMERS), it was already registers in + * realview_timer_init */ local_timer_setup(cpu); +#endif /* * Initialise the present map, which describes the set of CPUs diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h index 1f7c51a1886d524e738613479ee2622d8df61be7..af99636db400e7e8cfbe81956faa1d84f11ebdf2 100644 --- a/include/asm-arm/smp.h +++ b/include/asm-arm/smp.h @@ -107,10 +107,6 @@ extern void platform_cpu_enable(unsigned int cpu); extern void local_timer_interrupt(void); #ifdef CONFIG_LOCAL_TIMERS -/* - * Setup a local timer interrupt for a CPU. - */ -extern void local_timer_setup(unsigned int cpu); /* * Stop a local timer interrupt. @@ -124,16 +120,17 @@ extern int local_timer_ack(void); #else -static inline void local_timer_setup(unsigned int cpu) -{ -} - static inline void local_timer_stop(unsigned int cpu) { } #endif +/* + * Setup a local timer interrupt for a CPU. + */ +extern void local_timer_setup(unsigned int cpu); + /* * show local interrupt info */