aboutsummaryrefslogtreecommitdiffstats
path: root/arch/mips
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2007-10-11 18:46:09 -0400
committerRalf Baechle <ralf@linux-mips.org>2007-10-11 18:46:09 -0400
commitea5804015c0ce67741eb4b156a071fb4f415345f (patch)
treebc4da66c0d9a531a17159b1cb001ad8e96d24487 /arch/mips
parent7bcf7717b6a047c272410d0cd00213185fe6b99d (diff)
[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>
Diffstat (limited to 'arch/mips')
-rw-r--r--arch/mips/Kconfig4
-rw-r--r--arch/mips/kernel/smtc.c67
-rw-r--r--arch/mips/kernel/time.c111
-rw-r--r--arch/mips/mips-boards/generic/time.c1
4 files changed, 134 insertions, 49 deletions
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index ecce3aab1981..d8c905840af5 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -1368,6 +1368,7 @@ config MIPS_MT_SMTC
1368 depends on CPU_MIPS32_R2 1368 depends on CPU_MIPS32_R2
1369 #depends on CPU_MIPS64_R2 # once there is hardware ... 1369 #depends on CPU_MIPS64_R2 # once there is hardware ...
1370 depends on SYS_SUPPORTS_MULTITHREADING 1370 depends on SYS_SUPPORTS_MULTITHREADING
1371 select GENERIC_CLOCKEVENTS_BROADCAST
1371 select CPU_MIPSR2_IRQ_VI 1372 select CPU_MIPSR2_IRQ_VI
1372 select CPU_MIPSR2_IRQ_EI 1373 select CPU_MIPSR2_IRQ_EI
1373 select CPU_MIPSR2_SRS 1374 select CPU_MIPSR2_SRS
@@ -1537,6 +1538,9 @@ config CPU_HAS_SYNC
1537 depends on !CPU_R3000 1538 depends on !CPU_R3000
1538 default y 1539 default y
1539 1540
1541config GENERIC_CLOCKEVENTS_BROADCAST
1542 bool
1543
1540# 1544#
1541# Use the generic interrupt handling code in kernel/irq/: 1545# Use the generic interrupt handling code in kernel/irq/:
1542# 1546#
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c
index 137183bba54f..4d1ac9692dcd 100644
--- a/arch/mips/kernel/smtc.c
+++ b/arch/mips/kernel/smtc.c
@@ -1,5 +1,6 @@
1/* Copyright (C) 2004 Mips Technologies, Inc */ 1/* Copyright (C) 2004 Mips Technologies, Inc */
2 2
3#include <linux/clockchips.h>
3#include <linux/kernel.h> 4#include <linux/kernel.h>
4#include <linux/sched.h> 5#include <linux/sched.h>
5#include <linux/cpumask.h> 6#include <linux/cpumask.h>
@@ -62,7 +63,7 @@ asiduse smtc_live_asid[MAX_SMTC_TLBS][MAX_SMTC_ASIDS];
62 * Clock interrupt "latch" buffers, per "CPU" 63 * Clock interrupt "latch" buffers, per "CPU"
63 */ 64 */
64 65
65unsigned int ipi_timer_latch[NR_CPUS]; 66static atomic_t ipi_timer_latch[NR_CPUS];
66 67
67/* 68/*
68 * Number of InterProcessor Interupt (IPI) message buffers to allocate 69 * Number of InterProcessor Interupt (IPI) message buffers to allocate
@@ -296,8 +297,10 @@ int __init mipsmt_build_cpu_map(int start_cpu_slot)
296 __cpu_number_map[i] = i; 297 __cpu_number_map[i] = i;
297 __cpu_logical_map[i] = i; 298 __cpu_logical_map[i] = i;
298 } 299 }
300#ifdef CONFIG_MIPS_MT_FPAFF
299 /* Initialize map of CPUs with FPUs */ 301 /* Initialize map of CPUs with FPUs */
300 cpus_clear(mt_fpu_cpumask); 302 cpus_clear(mt_fpu_cpumask);
303#endif
301 304
302 /* One of those TC's is the one booting, and not a secondary... */ 305 /* One of those TC's is the one booting, and not a secondary... */
303 printk("%i available secondary CPU TC(s)\n", i - 1); 306 printk("%i available secondary CPU TC(s)\n", i - 1);
@@ -359,7 +362,7 @@ void mipsmt_prepare_cpus(void)
359 IPIQ[i].head = IPIQ[i].tail = NULL; 362 IPIQ[i].head = IPIQ[i].tail = NULL;
360 spin_lock_init(&IPIQ[i].lock); 363 spin_lock_init(&IPIQ[i].lock);
361 IPIQ[i].depth = 0; 364 IPIQ[i].depth = 0;
362 ipi_timer_latch[i] = 0; 365 atomic_set(&ipi_timer_latch[i], 0);
363 } 366 }
364 367
365 /* cpu_data index starts at zero */ 368 /* cpu_data index starts at zero */
@@ -482,10 +485,12 @@ void mipsmt_prepare_cpus(void)
482 485
483 /* Set up coprocessor affinity CPU mask(s) */ 486 /* Set up coprocessor affinity CPU mask(s) */
484 487
488#ifdef CONFIG_MIPS_MT_FPAFF
485 for (tc = 0; tc < ntc; tc++) { 489 for (tc = 0; tc < ntc; tc++) {
486 if (cpu_data[tc].options & MIPS_CPU_FPU) 490 if (cpu_data[tc].options & MIPS_CPU_FPU)
487 cpu_set(tc, mt_fpu_cpumask); 491 cpu_set(tc, mt_fpu_cpumask);
488 } 492 }
493#endif
489 494
490 /* set up ipi interrupts... */ 495 /* set up ipi interrupts... */
491 496
@@ -702,7 +707,7 @@ static void smtc_ipi_qdump(void)
702 * be done with the atomic.h primitives). And since this is 707 * be done with the atomic.h primitives). And since this is
703 * MIPS MT, we can assume that we have LL/SC. 708 * MIPS MT, we can assume that we have LL/SC.
704 */ 709 */
705static __inline__ int atomic_postincrement(unsigned int *pv) 710static inline int atomic_postincrement(atomic_t *v)
706{ 711{
707 unsigned long result; 712 unsigned long result;
708 713
@@ -714,8 +719,8 @@ static __inline__ int atomic_postincrement(unsigned int *pv)
714 " sc %1, %2 \n" 719 " sc %1, %2 \n"
715 " beqz %1, 1b \n" 720 " beqz %1, 1b \n"
716 __WEAK_LLSC_MB 721 __WEAK_LLSC_MB
717 : "=&r" (result), "=&r" (temp), "=m" (*pv) 722 : "=&r" (result), "=&r" (temp), "=m" (v->counter)
718 : "m" (*pv) 723 : "m" (v->counter)
719 : "memory"); 724 : "memory");
720 725
721 return result; 726 return result;
@@ -743,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
743 pipi->arg = (void *)action; 748 pipi->arg = (void *)action;
744 pipi->dest = cpu; 749 pipi->dest = cpu;
745 if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) { 750 if (cpu_data[cpu].vpe_id != cpu_data[smp_processor_id()].vpe_id) {
751 if (type == SMTC_CLOCK_TICK)
752 atomic_inc(&ipi_timer_latch[cpu]);
746 /* If not on same VPE, enqueue and send cross-VPE interupt */ 753 /* If not on same VPE, enqueue and send cross-VPE interupt */
747 smtc_ipi_nq(&IPIQ[cpu], pipi); 754 smtc_ipi_nq(&IPIQ[cpu], pipi);
748 LOCK_CORE_PRA(); 755 LOCK_CORE_PRA();
@@ -784,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action)
784 } 791 }
785 smtc_ipi_nq(&IPIQ[cpu], pipi); 792 smtc_ipi_nq(&IPIQ[cpu], pipi);
786 } else { 793 } else {
794 if (type == SMTC_CLOCK_TICK)
795 atomic_inc(&ipi_timer_latch[cpu]);
787 post_direct_ipi(cpu, pipi); 796 post_direct_ipi(cpu, pipi);
788 write_tc_c0_tchalt(0); 797 write_tc_c0_tchalt(0);
789 UNLOCK_CORE_PRA(); 798 UNLOCK_CORE_PRA();
@@ -801,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi)
801 unsigned long tcrestart; 810 unsigned long tcrestart;
802 extern u32 kernelsp[NR_CPUS]; 811 extern u32 kernelsp[NR_CPUS];
803 extern void __smtc_ipi_vector(void); 812 extern void __smtc_ipi_vector(void);
813//printk("%s: on %d for %d\n", __func__, smp_processor_id(), cpu);
804 814
805 /* Extract Status, EPC from halted TC */ 815 /* Extract Status, EPC from halted TC */
806 tcstatus = read_tc_c0_tcstatus(); 816 tcstatus = read_tc_c0_tcstatus();
@@ -851,25 +861,31 @@ static void ipi_call_interrupt(void)
851 smp_call_function_interrupt(); 861 smp_call_function_interrupt();
852} 862}
853 863
864DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
865
854void ipi_decode(struct smtc_ipi *pipi) 866void ipi_decode(struct smtc_ipi *pipi)
855{ 867{
868 unsigned int cpu = smp_processor_id();
869 struct clock_event_device *cd;
856 void *arg_copy = pipi->arg; 870 void *arg_copy = pipi->arg;
857 int type_copy = pipi->type; 871 int type_copy = pipi->type;
858 int dest_copy = pipi->dest; 872 int ticks;
859 873
860 smtc_ipi_nq(&freeIPIq, pipi); 874 smtc_ipi_nq(&freeIPIq, pipi);
861 switch (type_copy) { 875 switch (type_copy) {
862 case SMTC_CLOCK_TICK: 876 case SMTC_CLOCK_TICK:
863 irq_enter(); 877 irq_enter();
864 kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++; 878 kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++;
865 /* Invoke Clock "Interrupt" */ 879 cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
866 ipi_timer_latch[dest_copy] = 0; 880 ticks = atomic_read(&ipi_timer_latch[cpu]);
867#ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG 881 atomic_sub(ticks, &ipi_timer_latch[cpu]);
868 clock_hang_reported[dest_copy] = 0; 882 while (ticks) {
869#endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ 883 cd->event_handler(cd);
870 local_timer_interrupt(0, NULL); 884 ticks--;
885 }
871 irq_exit(); 886 irq_exit();
872 break; 887 break;
888
873 case LINUX_SMP_IPI: 889 case LINUX_SMP_IPI:
874 switch ((int)arg_copy) { 890 switch ((int)arg_copy) {
875 case SMP_RESCHEDULE_YOURSELF: 891 case SMP_RESCHEDULE_YOURSELF:
@@ -921,25 +937,6 @@ void deferred_smtc_ipi(void)
921} 937}
922 938
923/* 939/*
924 * Send clock tick to all TCs except the one executing the funtion
925 */
926
927void smtc_timer_broadcast(void)
928{
929 int cpu;
930 int myTC = cpu_data[smp_processor_id()].tc_id;
931 int myVPE = cpu_data[smp_processor_id()].vpe_id;
932
933 smtc_cpu_stats[smp_processor_id()].timerints++;
934
935 for_each_online_cpu(cpu) {
936 if (cpu_data[cpu].vpe_id == myVPE &&
937 cpu_data[cpu].tc_id != myTC)
938 smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
939 }
940}
941
942/*
943 * Cross-VPE interrupts in the SMTC prototype use "software interrupts" 940 * Cross-VPE interrupts in the SMTC prototype use "software interrupts"
944 * set via cross-VPE MTTR manipulation of the Cause register. It would be 941 * set via cross-VPE MTTR manipulation of the Cause register. It would be
945 * in some regards preferable to have external logic for "doorbell" hardware 942 * in some regards preferable to have external logic for "doorbell" hardware
@@ -1180,11 +1177,11 @@ void smtc_idle_loop_hook(void)
1180 for (tc = 0; tc < NR_CPUS; tc++) { 1177 for (tc = 0; tc < NR_CPUS; tc++) {
1181 /* Don't check ourself - we'll dequeue IPIs just below */ 1178 /* Don't check ourself - we'll dequeue IPIs just below */
1182 if ((tc != smp_processor_id()) && 1179 if ((tc != smp_processor_id()) &&
1183 ipi_timer_latch[tc] > timerq_limit) { 1180 atomic_read(&ipi_timer_latch[tc]) > timerq_limit) {
1184 if (clock_hang_reported[tc] == 0) { 1181 if (clock_hang_reported[tc] == 0) {
1185 pdb_msg += sprintf(pdb_msg, 1182 pdb_msg += sprintf(pdb_msg,
1186 "TC %d looks hung with timer latch at %d\n", 1183 "TC %d looks hung with timer latch at %d\n",
1187 tc, ipi_timer_latch[tc]); 1184 tc, atomic_read(&ipi_timer_latch[tc]));
1188 clock_hang_reported[tc]++; 1185 clock_hang_reported[tc]++;
1189 } 1186 }
1190 } 1187 }
@@ -1225,7 +1222,7 @@ void smtc_soft_dump(void)
1225 smtc_ipi_qdump(); 1222 smtc_ipi_qdump();
1226 printk("Timer IPI Backlogs:\n"); 1223 printk("Timer IPI Backlogs:\n");
1227 for (i=0; i < NR_CPUS; i++) { 1224 for (i=0; i < NR_CPUS; i++) {
1228 printk("%d: %d\n", i, ipi_timer_latch[i]); 1225 printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i]));
1229 } 1226 }
1230 printk("%d Recoveries of \"stolen\" FPU\n", 1227 printk("%d Recoveries of \"stolen\" FPU\n",
1231 atomic_read(&smtc_fpu_recoveries)); 1228 atomic_read(&smtc_fpu_recoveries));
diff --git a/arch/mips/kernel/time.c b/arch/mips/kernel/time.c
index 35988847c98a..369a5f9ad268 100644
--- a/arch/mips/kernel/time.c
+++ b/arch/mips/kernel/time.c
@@ -25,6 +25,7 @@
25#include <linux/spinlock.h> 25#include <linux/spinlock.h>
26#include <linux/interrupt.h> 26#include <linux/interrupt.h>
27#include <linux/module.h> 27#include <linux/module.h>
28#include <linux/kallsyms.h>
28 29
29#include <asm/bootinfo.h> 30#include <asm/bootinfo.h>
30#include <asm/cache.h> 31#include <asm/cache.h>
@@ -33,6 +34,7 @@
33#include <asm/cpu-features.h> 34#include <asm/cpu-features.h>
34#include <asm/div64.h> 35#include <asm/div64.h>
35#include <asm/sections.h> 36#include <asm/sections.h>
37#include <asm/smtc_ipi.h>
36#include <asm/time.h> 38#include <asm/time.h>
37 39
38#include <irq.h> 40#include <irq.h>
@@ -230,12 +232,24 @@ static int mips_next_event(unsigned long delta,
230 struct clock_event_device *evt) 232 struct clock_event_device *evt)
231{ 233{
232 unsigned int cnt; 234 unsigned int cnt;
235 int res;
233 236
237#ifdef CONFIG_MIPS_MT_SMTC
238 {
239 unsigned long flags, vpflags;
240 local_irq_save(flags);
241 vpflags = dvpe();
242#endif
234 cnt = read_c0_count(); 243 cnt = read_c0_count();
235 cnt += delta; 244 cnt += delta;
236 write_c0_compare(cnt); 245 write_c0_compare(cnt);
237 246 res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
238 return ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0; 247#ifdef CONFIG_MIPS_MT_SMTC
248 evpe(vpflags);
249 local_irq_restore(flags);
250 }
251#endif
252 return res;
239} 253}
240 254
241static void mips_set_mode(enum clock_event_mode mode, 255static void mips_set_mode(enum clock_event_mode mode,
@@ -244,9 +258,7 @@ static void mips_set_mode(enum clock_event_mode mode,
244 /* Nothing to do ... */ 258 /* Nothing to do ... */
245} 259}
246 260
247struct clock_event_device mips_clockevent; 261static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
248
249static struct clock_event_device *global_cd[NR_CPUS];
250static int cp0_timer_irq_installed; 262static int cp0_timer_irq_installed;
251 263
252static irqreturn_t timer_interrupt(int irq, void *dev_id) 264static irqreturn_t timer_interrupt(int irq, void *dev_id)
@@ -271,7 +283,12 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
271 */ 283 */
272 if (!r2 || (read_c0_cause() & (1 << 30))) { 284 if (!r2 || (read_c0_cause() & (1 << 30))) {
273 c0_timer_ack(); 285 c0_timer_ack();
274 cd = global_cd[cpu]; 286#ifdef CONFIG_MIPS_MT_SMTC
287 if (cpu_data[cpu].vpe_id)
288 goto out;
289 cpu = 0;
290#endif
291 cd = &per_cpu(mips_clockevent_device, cpu);
275 cd->event_handler(cd); 292 cd->event_handler(cd);
276 } 293 }
277 294
@@ -281,7 +298,11 @@ out:
281 298
282static struct irqaction timer_irqaction = { 299static struct irqaction timer_irqaction = {
283 .handler = timer_interrupt, 300 .handler = timer_interrupt,
301#ifdef CONFIG_MIPS_MT_SMTC
302 .flags = IRQF_DISABLED,
303#else
284 .flags = IRQF_DISABLED | IRQF_PERCPU, 304 .flags = IRQF_DISABLED | IRQF_PERCPU,
305#endif
285 .name = "timer", 306 .name = "timer",
286}; 307};
287 308
@@ -316,6 +337,60 @@ void __init __weak plat_timer_setup(struct irqaction *irq)
316{ 337{
317} 338}
318 339
340#ifdef CONFIG_MIPS_MT_SMTC
341DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
342
343static void smtc_set_mode(enum clock_event_mode mode,
344 struct clock_event_device *evt)
345{
346}
347
348int dummycnt[NR_CPUS];
349
350static void mips_broadcast(cpumask_t mask)
351{
352 unsigned int cpu;
353
354 for_each_cpu_mask(cpu, mask)
355 smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
356}
357
358static void setup_smtc_dummy_clockevent_device(void)
359{
360 //uint64_t mips_freq = mips_hpt_^frequency;
361 unsigned int cpu = smp_processor_id();
362 struct clock_event_device *cd;
363
364 cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
365
366 cd->name = "SMTC";
367 cd->features = CLOCK_EVT_FEAT_DUMMY;
368
369 /* Calculate the min / max delta */
370 cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
371 cd->shift = 0; //32;
372 cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
373 cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
374
375 cd->rating = 200;
376 cd->irq = 17; //-1;
377// if (cpu)
378// cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
379// else
380 cd->cpumask = cpumask_of_cpu(cpu);
381
382 cd->set_mode = smtc_set_mode;
383
384 cd->broadcast = mips_broadcast;
385
386 clockevents_register_device(cd);
387}
388#endif
389
390static void mips_event_handler(struct clock_event_device *dev)
391{
392}
393
319void __cpuinit mips_clockevent_init(void) 394void __cpuinit mips_clockevent_init(void)
320{ 395{
321 uint64_t mips_freq = mips_hpt_frequency; 396 uint64_t mips_freq = mips_hpt_frequency;
@@ -326,12 +401,18 @@ void __cpuinit mips_clockevent_init(void)
326 if (!cpu_has_counter) 401 if (!cpu_has_counter)
327 return; 402 return;
328 403
329 if (cpu == 0) 404#ifdef CONFIG_MIPS_MT_SMTC
330 cd = &mips_clockevent; 405 setup_smtc_dummy_clockevent_device();
331 else 406
332 cd = kzalloc(sizeof(*cd), GFP_ATOMIC); 407 /*
333 if (!cd) 408 * On SMTC we only register VPE0's compare interrupt as clockevent
334 return; /* We're probably roadkill ... */ 409 * device.
410 */
411 if (cpu)
412 return;
413#endif
414
415 cd = &per_cpu(mips_clockevent_device, cpu);
335 416
336 cd->name = "MIPS"; 417 cd->name = "MIPS";
337 cd->features = CLOCK_EVT_FEAT_ONESHOT; 418 cd->features = CLOCK_EVT_FEAT_ONESHOT;
@@ -344,11 +425,15 @@ void __cpuinit mips_clockevent_init(void)
344 425
345 cd->rating = 300; 426 cd->rating = 300;
346 cd->irq = irq; 427 cd->irq = irq;
428#ifdef CONFIG_MIPS_MT_SMTC
429 cd->cpumask = CPU_MASK_ALL;
430#else
347 cd->cpumask = cpumask_of_cpu(cpu); 431 cd->cpumask = cpumask_of_cpu(cpu);
432#endif
348 cd->set_next_event = mips_next_event; 433 cd->set_next_event = mips_next_event;
349 cd->set_mode = mips_set_mode; 434 cd->set_mode = mips_set_mode;
435 cd->event_handler = mips_event_handler;
350 436
351 global_cd[cpu] = cd;
352 clockevents_register_device(cd); 437 clockevents_register_device(cd);
353 438
354 if (!cp0_timer_irq_installed) { 439 if (!cp0_timer_irq_installed) {
diff --git a/arch/mips/mips-boards/generic/time.c b/arch/mips/mips-boards/generic/time.c
index 2c8db3e0f4b7..cf55ecd96bf4 100644
--- a/arch/mips/mips-boards/generic/time.c
+++ b/arch/mips/mips-boards/generic/time.c
@@ -55,7 +55,6 @@ unsigned long cpu_khz;
55 55
56static int mips_cpu_timer_irq; 56static int mips_cpu_timer_irq;
57extern int cp0_perfcount_irq; 57extern int cp0_perfcount_irq;
58extern void smtc_timer_broadcast(void);
59 58
60static void mips_timer_dispatch(void) 59static void mips_timer_dispatch(void)
61{ 60{