[MIPS] Dyntick support for SMTC:

The kernel currently only supports broadcasting of the timer interrupt
from a single timer, not multicasting into two multicast groups of
processors.  So the implemented mechanism for SMTC works by broadcasting
the cp0 compare interrupt on VPE 0 and ignoring it on any additional VPEs.

Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c
index 137183b..4d1ac96 100644
--- a/arch/mips/kernel/smtc.c
+++ b/arch/mips/kernel/smtc.c
@@ -1,5 +1,6 @@
 /* Copyright (C) 2004 Mips Technologies, Inc */
 
+#include <linux/clockchips.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/cpumask.h>
@@ -62,7 +63,7 @@
  * Clock interrupt "latch" buffers, per "CPU"
  */
 
-unsigned int ipi_timer_latch[NR_CPUS];
+static atomic_t ipi_timer_latch[NR_CPUS];
 
 /*
  * Number of InterProcessor Interupt (IPI) message buffers to allocate
@@ -296,8 +297,10 @@
 		__cpu_number_map[i] = i;
 		__cpu_logical_map[i] = i;
 	}
+#ifdef CONFIG_MIPS_MT_FPAFF
 	/* Initialize map of CPUs with FPUs */
 	cpus_clear(mt_fpu_cpumask);
+#endif
 
 	/* One of those TC's is the one booting, and not a secondary... */
 	printk("%i available secondary CPU TC(s)\n", i - 1);
@@ -359,7 +362,7 @@
 		IPIQ[i].head = IPIQ[i].tail = NULL;
 		spin_lock_init(&IPIQ[i].lock);
 		IPIQ[i].depth = 0;
-		ipi_timer_latch[i] = 0;
+		atomic_set(&ipi_timer_latch[i], 0);
 	}
 
 	/* cpu_data index starts at zero */
@@ -482,10 +485,12 @@
 
 	/* Set up coprocessor affinity CPU mask(s) */
 
+#ifdef CONFIG_MIPS_MT_FPAFF
 	for (tc = 0; tc < ntc; tc++) {
 		if (cpu_data[tc].options & MIPS_CPU_FPU)
 			cpu_set(tc, mt_fpu_cpumask);
 	}
+#endif
 
 	/* set up ipi interrupts... */
 
@@ -702,7 +707,7 @@
  * be done with the atomic.h primitives). And since this is
  * MIPS MT, we can assume that we have LL/SC.
  */
-static __inline__ int atomic_postincrement(unsigned int *pv)
+static inline int atomic_postincrement(atomic_t *v)
 {
 	unsigned long result;
 
@@ -714,8 +719,8 @@
 	"	sc	%1, %2					\n"
 	"	beqz	%1, 1b					\n"
 	__WEAK_LLSC_MB
-	: "=&r" (result), "=&r" (temp), "=m" (*pv)
-	: "m" (*pv)
+	: "=&r" (result), "=&r" (temp), "=m" (v->counter)
+	: "m" (v->counter)
 	: "memory");
 
 	return result;
@@ -743,6 +748,8 @@
 	pipi->arg = (void *)action;
 	pipi->dest = cpu;
 	if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) {
+		if (type == SMTC_CLOCK_TICK)
+			atomic_inc(&ipi_timer_latch[cpu]);
 		/* If not on same VPE, enqueue and send cross-VPE interupt */
 		smtc_ipi_nq(&IPIQ[cpu], pipi);
 		LOCK_CORE_PRA();
@@ -784,6 +791,8 @@
 			}
 			smtc_ipi_nq(&IPIQ[cpu], pipi);
 		} else {
+			if (type == SMTC_CLOCK_TICK)
+				atomic_inc(&ipi_timer_latch[cpu]);
 			post_direct_ipi(cpu, pipi);
 			write_tc_c0_tchalt(0);
 			UNLOCK_CORE_PRA();
@@ -801,6 +810,7 @@
 	unsigned long tcrestart;
 	extern u32 kernelsp[NR_CPUS];
 	extern void __smtc_ipi_vector(void);
+//printk("%s: on %d for %d\n", __func__, smp_processor_id(), cpu);
 
 	/* Extract Status, EPC from halted TC */
 	tcstatus = read_tc_c0_tcstatus();
@@ -851,25 +861,31 @@
 	smp_call_function_interrupt();
 }
 
+DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
+
 void ipi_decode(struct smtc_ipi *pipi)
 {
+	unsigned int cpu = smp_processor_id();
+	struct clock_event_device *cd;
 	void *arg_copy = pipi->arg;
 	int type_copy = pipi->type;
-	int dest_copy = pipi->dest;
+	int ticks;
 
 	smtc_ipi_nq(&freeIPIq, pipi);
 	switch (type_copy) {
 	case SMTC_CLOCK_TICK:
 		irq_enter();
-		kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++;
-		/* Invoke Clock "Interrupt" */
-		ipi_timer_latch[dest_copy] = 0;
-#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG
-		clock_hang_reported[dest_copy] = 0;
-#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */
-		local_timer_interrupt(0, NULL);
+		kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++;
+		cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
+		ticks = atomic_read(&ipi_timer_latch[cpu]);
+		atomic_sub(ticks, &ipi_timer_latch[cpu]);
+		while (ticks) {
+			cd->event_handler(cd);
+			ticks--;
+		}
 		irq_exit();
 		break;
+
 	case LINUX_SMP_IPI:
 		switch ((int)arg_copy) {
 		case SMP_RESCHEDULE_YOURSELF:
@@ -921,25 +937,6 @@
 }
 
 /*
- * Send clock tick to all TCs except the one executing the funtion
- */
-
-void smtc_timer_broadcast(void)
-{
-	int cpu;
-	int myTC = cpu_data[smp_processor_id()].tc_id;
-	int myVPE = cpu_data[smp_processor_id()].vpe_id;
-
-	smtc_cpu_stats[smp_processor_id()].timerints++;
-
-	for_each_online_cpu(cpu) {
-		if (cpu_data[cpu].vpe_id == myVPE &&
-		    cpu_data[cpu].tc_id != myTC)
-			smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
-	}
-}
-
-/*
  * Cross-VPE interrupts in the SMTC prototype use "software interrupts"
  * set via cross-VPE MTTR manipulation of the Cause register. It would be
  * in some regards preferable to have external logic for "doorbell" hardware
@@ -1180,11 +1177,11 @@
 	for (tc = 0; tc < NR_CPUS; tc++) {
 		/* Don't check ourself - we'll dequeue IPIs just below */
 		if ((tc != smp_processor_id()) &&
-		    ipi_timer_latch[tc] > timerq_limit) {
+		    atomic_read(&ipi_timer_latch[tc]) > timerq_limit) {
 		    if (clock_hang_reported[tc] == 0) {
 			pdb_msg += sprintf(pdb_msg,
 				"TC %d looks hung with timer latch at %d\n",
-				tc, ipi_timer_latch[tc]);
+				tc, atomic_read(&ipi_timer_latch[tc]));
 			clock_hang_reported[tc]++;
 			}
 		}
@@ -1225,7 +1222,7 @@
 	smtc_ipi_qdump();
 	printk("Timer IPI Backlogs:\n");
 	for (i=0; i < NR_CPUS; i++) {
-		printk("%d: %d\n", i, ipi_timer_latch[i]);
+		printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i]));
 	}
 	printk("%d Recoveries of \"stolen\" FPU\n",
 	       atomic_read(&smtc_fpu_recoveries));