[ARM SMP] Add core ARM support for local timers

Add infrastructure for supporting per-cpu local timers to update
the profiling information and update system time accounting.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 91d5ef3..3bfef09 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -356,6 +356,16 @@
 	  Say Y here to experiment with turning CPUs off and on.  CPUs
 	  can be controlled through /sys/devices/system/cpu.
 
+config LOCAL_TIMERS
+	bool "Use local timer interrupts"
+	depends on SMP && n
+	default y
+	help
+	  Enable support for local timers on SMP platforms, rather then the
+	  legacy IPI broadcast method.  Local timers allows the system
+	  accounting to be spread across the timer interval, preventing a
+	  "thundering herd" at every timer tick.
+
 config PREEMPT
 	bool "Preemptible Kernel (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index a511ec5..d9fb819 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -47,6 +47,13 @@
 	movne	r0, sp
 	adrne	lr, 1b
 	bne	do_IPI
+
+#ifdef CONFIG_LOCAL_TIMERS
+	test_for_ltirq r0, r6, r5, lr
+	movne	r0, sp
+	adrne	lr, 1b
+	bne	do_local_timer
+#endif
 #endif
 
 	.endm
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index 6f86d0a..d7099db 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -264,6 +264,7 @@
 #endif
 #ifdef CONFIG_SMP
 		show_ipi_list(p);
+		show_local_irqs(p);
 #endif
 		seq_printf(p, "Err: %10lu\n", irq_err_count);
 	}
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index f5fc57e..77e2e9c 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -185,6 +185,11 @@
 	migrate_irqs();
 
 	/*
+	 * Stop the local timer for this CPU.
+	 */
+	local_timer_stop(cpu);
+
+	/*
 	 * Flush user cache and TLB mappings, and then remove this CPU
 	 * from the vm mask set of all processes.
 	 */
@@ -290,6 +295,11 @@
 	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
 	 */
 	cpu_idle();
@@ -454,6 +464,18 @@
 	seq_putc(p, '\n');
 }
 
+void show_local_irqs(struct seq_file *p)
+{
+	unsigned int cpu;
+
+	seq_printf(p, "LOC: ");
+
+	for_each_present_cpu(cpu)
+		seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs);
+
+	seq_putc(p, '\n');
+}
+
 static void ipi_timer(struct pt_regs *regs)
 {
 	int user = user_mode(regs);
@@ -464,6 +486,18 @@
 	irq_exit();
 }
 
+#ifdef CONFIG_LOCAL_TIMERS
+asmlinkage void do_local_timer(struct pt_regs *regs)
+{
+	int cpu = smp_processor_id();
+
+	if (local_timer_ack()) {
+		irq_stat[cpu].local_timer_irqs++;
+		ipi_timer(regs);
+	}
+}
+#endif
+
 /*
  * ipi_call_function - handle IPI from smp_call_function()
  *