[MIPS] Dyntick support for SMTC:
authorRalf Baechle <ralf@linux-mips.org>
Thu, 11 Oct 2007 22:46:09 +0000 (23:46 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Thu, 11 Oct 2007 22:46:09 +0000 (23:46 +0100)
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>
arch/mips/Kconfig
arch/mips/kernel/smtc.c
arch/mips/kernel/time.c
arch/mips/mips-boards/generic/time.c

index ecce3aab19810999c03473aa1b33c21a25eaf7a3..d8c905840af5ec66aa7017d9fe91e830de9f6802 100644 (file)
@@ -1368,6 +1368,7 @@ config MIPS_MT_SMTC
        depends on CPU_MIPS32_R2
        #depends on CPU_MIPS64_R2               # once there is hardware ...
        depends on SYS_SUPPORTS_MULTITHREADING
+       select GENERIC_CLOCKEVENTS_BROADCAST
        select CPU_MIPSR2_IRQ_VI
        select CPU_MIPSR2_IRQ_EI
        select CPU_MIPSR2_SRS
@@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC
        depends on !CPU_R3000
        default y
 
+config GENERIC_CLOCKEVENTS_BROADCAST
+       bool
+
 #
 # Use the generic interrupt handling code in kernel/irq/:
 #
index 137183bba54f38bc1a12e8e56bdc0348c30ebd74..4d1ac9692dcd985e5d9ed30b8ab9766d935485bd 100644 (file)
@@ -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 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
  * 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 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot)
                __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 @@ void mipsmt_prepare_cpus(void)
                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 @@ void mipsmt_prepare_cpus(void)
 
        /* 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 @@ static void smtc_ipi_qdump(void)
  * 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 @@ static __inline__ int atomic_postincrement(unsigned int *pv)
        "       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 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
        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 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
                        }
                        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 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi)
        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 @@ static void ipi_call_interrupt(void)
        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:
@@ -920,25 +936,6 @@ void deferred_smtc_ipi(void)
        }
 }
 
-/*
- * 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
@@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void)
        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 @@ void smtc_soft_dump(void)
        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));
index 35988847c98a36777e2ef5d7793a2bc37ba3d2a9..369a5f9ad268d421dfebb294b883362099d25f35 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/kallsyms.h>
 
 #include <asm/bootinfo.h>
 #include <asm/cache.h>
@@ -33,6 +34,7 @@
 #include <asm/cpu-features.h>
 #include <asm/div64.h>
 #include <asm/sections.h>
+#include <asm/smtc_ipi.h>
 #include <asm/time.h>
 
 #include <irq.h>
@@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta,
                            struct clock_event_device *evt)
 {
        unsigned int cnt;
+       int res;
 
+#ifdef CONFIG_MIPS_MT_SMTC
+       {
+       unsigned long flags, vpflags;
+       local_irq_save(flags);
+       vpflags = dvpe();
+#endif
        cnt = read_c0_count();
        cnt += delta;
        write_c0_compare(cnt);
-
-       return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
+       res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
+#ifdef CONFIG_MIPS_MT_SMTC
+       evpe(vpflags);
+       local_irq_restore(flags);
+       }
+#endif
+       return res;
 }
 
 static void mips_set_mode(enum clock_event_mode mode,
@@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode,
        /* Nothing to do ...  */
 }
 
-struct clock_event_device mips_clockevent;
-
-static struct clock_event_device *global_cd[NR_CPUS];
+static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
 static int cp0_timer_irq_installed;
 
 static irqreturn_t timer_interrupt(int irq, void *dev_id)
@@ -271,7 +283,12 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
         */
        if (!r2 || (read_c0_cause() & (1 << 30))) {
                c0_timer_ack();
-               cd = global_cd[cpu];
+#ifdef CONFIG_MIPS_MT_SMTC
+               if (cpu_data[cpu].vpe_id)
+                       goto out;
+               cpu = 0;
+#endif
+               cd = &per_cpu(mips_clockevent_device, cpu);
                cd->event_handler(cd);
        }
 
@@ -281,7 +298,11 @@ out:
 
 static struct irqaction timer_irqaction = {
        .handler = timer_interrupt,
+#ifdef CONFIG_MIPS_MT_SMTC
+       .flags = IRQF_DISABLED,
+#else
        .flags = IRQF_DISABLED | IRQF_PERCPU,
+#endif
        .name = "timer",
 };
 
@@ -316,6 +337,60 @@ void __init __weak plat_timer_setup(struct irqaction *irq)
 {
 }
 
+#ifdef CONFIG_MIPS_MT_SMTC
+DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
+
+static void smtc_set_mode(enum clock_event_mode mode,
+                          struct clock_event_device *evt)
+{
+}
+
+int dummycnt[NR_CPUS];
+
+static void mips_broadcast(cpumask_t mask)
+{
+       unsigned int cpu;
+
+       for_each_cpu_mask(cpu, mask)
+               smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
+}
+
+static void setup_smtc_dummy_clockevent_device(void)
+{
+       //uint64_t mips_freq = mips_hpt_^frequency;
+       unsigned int cpu = smp_processor_id();
+       struct clock_event_device *cd;
+
+       cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
+
+       cd->name                = "SMTC";
+       cd->features            = CLOCK_EVT_FEAT_DUMMY;
+
+       /* Calculate the min / max delta */
+       cd->mult        = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
+       cd->shift               = 0; //32;
+       cd->max_delta_ns        = 0; //clockevent_delta2ns(0x7fffffff, cd);
+       cd->min_delta_ns        = 0; //clockevent_delta2ns(0x30, cd);
+
+       cd->rating              = 200;
+       cd->irq                 = 17; //-1;
+//     if (cpu)
+//             cd->cpumask     = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
+//     else
+               cd->cpumask     = cpumask_of_cpu(cpu);
+
+       cd->set_mode            = smtc_set_mode;
+
+       cd->broadcast           = mips_broadcast;
+
+       clockevents_register_device(cd);
+}
+#endif
+
+static void mips_event_handler(struct clock_event_device *dev)
+{
+}
+
 void __cpuinit mips_clockevent_init(void)
 {
        uint64_t mips_freq = mips_hpt_frequency;
@@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void)
        if (!cpu_has_counter)
                return;
 
-       if (cpu == 0)
-               cd = &mips_clockevent;
-       else
-               cd = kzalloc(sizeof(*cd), GFP_ATOMIC);
-       if (!cd)
-               return;         /* We're probably roadkill ...  */
+#ifdef CONFIG_MIPS_MT_SMTC
+       setup_smtc_dummy_clockevent_device();
+
+       /*
+        * On SMTC we only register VPE0's compare interrupt as clockevent
+        * device.
+        */
+       if (cpu)
+               return;
+#endif
+
+       cd = &per_cpu(mips_clockevent_device, cpu);
 
        cd->name                = "MIPS";
        cd->features            = CLOCK_EVT_FEAT_ONESHOT;
@@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void)
 
        cd->rating              = 300;
        cd->irq                 = irq;
+#ifdef CONFIG_MIPS_MT_SMTC
+       cd->cpumask             = CPU_MASK_ALL;
+#else
        cd->cpumask             = cpumask_of_cpu(cpu);
+#endif
        cd->set_next_event      = mips_next_event;
        cd->set_mode            = mips_set_mode;
+       cd->event_handler       = mips_event_handler;
 
-       global_cd[cpu] = cd;
        clockevents_register_device(cd);
 
        if (!cp0_timer_irq_installed) {
index 2c8db3e0f4b7d8e8cd644d70da04e8f302e4bc46..cf55ecd96bf43ff0c521e2175f5f24877350d275 100644 (file)
@@ -55,7 +55,6 @@ unsigned long cpu_khz;
 
 static int mips_cpu_timer_irq;
 extern int cp0_perfcount_irq;
-extern void smtc_timer_broadcast(void);
 
 static void mips_timer_dispatch(void)
 {