diff options
Diffstat (limited to 'arch/mips/kernel/smtc.c')
-rw-r--r-- | arch/mips/kernel/smtc.c | 146 |
1 files changed, 103 insertions, 43 deletions
diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c index f09404377ef1..a8c1a698d588 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 | ||
65 | unsigned int ipi_timer_latch[NR_CPUS]; | 66 | static 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 |
@@ -179,7 +180,7 @@ void __init sanitize_tlb_entries(void) | |||
179 | 180 | ||
180 | static void smtc_configure_tlb(void) | 181 | static void smtc_configure_tlb(void) |
181 | { | 182 | { |
182 | int i,tlbsiz,vpes; | 183 | int i, tlbsiz, vpes; |
183 | unsigned long mvpconf0; | 184 | unsigned long mvpconf0; |
184 | unsigned long config1val; | 185 | unsigned long config1val; |
185 | 186 | ||
@@ -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 */ |
@@ -369,7 +372,7 @@ void mipsmt_prepare_cpus(void) | |||
369 | cpu++; | 372 | cpu++; |
370 | 373 | ||
371 | /* Report on boot-time options */ | 374 | /* Report on boot-time options */ |
372 | mips_mt_set_cpuoptions (); | 375 | mips_mt_set_cpuoptions(); |
373 | if (vpelimit > 0) | 376 | if (vpelimit > 0) |
374 | printk("Limit of %d VPEs set\n", vpelimit); | 377 | printk("Limit of %d VPEs set\n", vpelimit); |
375 | if (tclimit > 0) | 378 | if (tclimit > 0) |
@@ -420,7 +423,7 @@ void mipsmt_prepare_cpus(void) | |||
420 | * code. Leave it alone! | 423 | * code. Leave it alone! |
421 | */ | 424 | */ |
422 | if (tc != 0) { | 425 | if (tc != 0) { |
423 | smtc_tc_setup(vpe,tc, cpu); | 426 | smtc_tc_setup(vpe, tc, cpu); |
424 | cpu++; | 427 | cpu++; |
425 | } | 428 | } |
426 | printk(" %d", tc); | 429 | printk(" %d", tc); |
@@ -428,7 +431,7 @@ void mipsmt_prepare_cpus(void) | |||
428 | } | 431 | } |
429 | if (slop) { | 432 | if (slop) { |
430 | if (tc != 0) { | 433 | if (tc != 0) { |
431 | smtc_tc_setup(vpe,tc, cpu); | 434 | smtc_tc_setup(vpe, tc, cpu); |
432 | cpu++; | 435 | cpu++; |
433 | } | 436 | } |
434 | printk(" %d", tc); | 437 | printk(" %d", tc); |
@@ -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 | ||
@@ -567,7 +572,7 @@ void smtc_init_secondary(void) | |||
567 | if (((read_c0_tcbind() & TCBIND_CURTC) != 0) && | 572 | if (((read_c0_tcbind() & TCBIND_CURTC) != 0) && |
568 | ((read_c0_tcbind() & TCBIND_CURVPE) | 573 | ((read_c0_tcbind() & TCBIND_CURVPE) |
569 | != cpu_data[smp_processor_id() - 1].vpe_id)){ | 574 | != cpu_data[smp_processor_id() - 1].vpe_id)){ |
570 | write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ); | 575 | write_c0_compare(read_c0_count() + mips_hpt_frequency/HZ); |
571 | } | 576 | } |
572 | 577 | ||
573 | local_irq_enable(); | 578 | local_irq_enable(); |
@@ -606,6 +611,60 @@ int setup_irq_smtc(unsigned int irq, struct irqaction * new, | |||
606 | return setup_irq(irq, new); | 611 | return setup_irq(irq, new); |
607 | } | 612 | } |
608 | 613 | ||
614 | #ifdef CONFIG_MIPS_MT_SMTC_IRQAFF | ||
615 | /* | ||
616 | * Support for IRQ affinity to TCs | ||
617 | */ | ||
618 | |||
619 | void smtc_set_irq_affinity(unsigned int irq, cpumask_t affinity) | ||
620 | { | ||
621 | /* | ||
622 | * If a "fast path" cache of quickly decodable affinity state | ||
623 | * is maintained, this is where it gets done, on a call up | ||
624 | * from the platform affinity code. | ||
625 | */ | ||
626 | } | ||
627 | |||
628 | void smtc_forward_irq(unsigned int irq) | ||
629 | { | ||
630 | int target; | ||
631 | |||
632 | /* | ||
633 | * OK wise guy, now figure out how to get the IRQ | ||
634 | * to be serviced on an authorized "CPU". | ||
635 | * | ||
636 | * Ideally, to handle the situation where an IRQ has multiple | ||
637 | * eligible CPUS, we would maintain state per IRQ that would | ||
638 | * allow a fair distribution of service requests. Since the | ||
639 | * expected use model is any-or-only-one, for simplicity | ||
640 | * and efficiency, we just pick the easiest one to find. | ||
641 | */ | ||
642 | |||
643 | target = first_cpu(irq_desc[irq].affinity); | ||
644 | |||
645 | /* | ||
646 | * We depend on the platform code to have correctly processed | ||
647 | * IRQ affinity change requests to ensure that the IRQ affinity | ||
648 | * mask has been purged of bits corresponding to nonexistent and | ||
649 | * offline "CPUs", and to TCs bound to VPEs other than the VPE | ||
650 | * connected to the physical interrupt input for the interrupt | ||
651 | * in question. Otherwise we have a nasty problem with interrupt | ||
652 | * mask management. This is best handled in non-performance-critical | ||
653 | * platform IRQ affinity setting code, to minimize interrupt-time | ||
654 | * checks. | ||
655 | */ | ||
656 | |||
657 | /* If no one is eligible, service locally */ | ||
658 | if (target >= NR_CPUS) { | ||
659 | do_IRQ_no_affinity(irq); | ||
660 | return; | ||
661 | } | ||
662 | |||
663 | smtc_send_ipi(target, IRQ_AFFINITY_IPI, irq); | ||
664 | } | ||
665 | |||
666 | #endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */ | ||
667 | |||
609 | /* | 668 | /* |
610 | * IPI model for SMTC is tricky, because interrupts aren't TC-specific. | 669 | * IPI model for SMTC is tricky, because interrupts aren't TC-specific. |
611 | * Within a VPE one TC can interrupt another by different approaches. | 670 | * Within a VPE one TC can interrupt another by different approaches. |
@@ -648,7 +707,7 @@ static void smtc_ipi_qdump(void) | |||
648 | * be done with the atomic.h primitives). And since this is | 707 | * be done with the atomic.h primitives). And since this is |
649 | * MIPS MT, we can assume that we have LL/SC. | 708 | * MIPS MT, we can assume that we have LL/SC. |
650 | */ | 709 | */ |
651 | static __inline__ int atomic_postincrement(unsigned int *pv) | 710 | static inline int atomic_postincrement(atomic_t *v) |
652 | { | 711 | { |
653 | unsigned long result; | 712 | unsigned long result; |
654 | 713 | ||
@@ -659,9 +718,9 @@ static __inline__ int atomic_postincrement(unsigned int *pv) | |||
659 | " addu %1, %0, 1 \n" | 718 | " addu %1, %0, 1 \n" |
660 | " sc %1, %2 \n" | 719 | " sc %1, %2 \n" |
661 | " beqz %1, 1b \n" | 720 | " beqz %1, 1b \n" |
662 | " sync \n" | 721 | __WEAK_LLSC_MB |
663 | : "=&r" (result), "=&r" (temp), "=m" (*pv) | 722 | : "=&r" (result), "=&r" (temp), "=m" (v->counter) |
664 | : "m" (*pv) | 723 | : "m" (v->counter) |
665 | : "memory"); | 724 | : "memory"); |
666 | 725 | ||
667 | return result; | 726 | return result; |
@@ -689,6 +748,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) | |||
689 | pipi->arg = (void *)action; | 748 | pipi->arg = (void *)action; |
690 | pipi->dest = cpu; | 749 | pipi->dest = cpu; |
691 | 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]); | ||
692 | /* If not on same VPE, enqueue and send cross-VPE interupt */ | 753 | /* If not on same VPE, enqueue and send cross-VPE interupt */ |
693 | smtc_ipi_nq(&IPIQ[cpu], pipi); | 754 | smtc_ipi_nq(&IPIQ[cpu], pipi); |
694 | LOCK_CORE_PRA(); | 755 | LOCK_CORE_PRA(); |
@@ -730,6 +791,8 @@ void smtc_send_ipi(int cpu, int type, unsigned int action) | |||
730 | } | 791 | } |
731 | smtc_ipi_nq(&IPIQ[cpu], pipi); | 792 | smtc_ipi_nq(&IPIQ[cpu], pipi); |
732 | } else { | 793 | } else { |
794 | if (type == SMTC_CLOCK_TICK) | ||
795 | atomic_inc(&ipi_timer_latch[cpu]); | ||
733 | post_direct_ipi(cpu, pipi); | 796 | post_direct_ipi(cpu, pipi); |
734 | write_tc_c0_tchalt(0); | 797 | write_tc_c0_tchalt(0); |
735 | UNLOCK_CORE_PRA(); | 798 | UNLOCK_CORE_PRA(); |
@@ -747,6 +810,7 @@ static void post_direct_ipi(int cpu, struct smtc_ipi *pipi) | |||
747 | unsigned long tcrestart; | 810 | unsigned long tcrestart; |
748 | extern u32 kernelsp[NR_CPUS]; | 811 | extern u32 kernelsp[NR_CPUS]; |
749 | 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); | ||
750 | 814 | ||
751 | /* Extract Status, EPC from halted TC */ | 815 | /* Extract Status, EPC from halted TC */ |
752 | tcstatus = read_tc_c0_tcstatus(); | 816 | tcstatus = read_tc_c0_tcstatus(); |
@@ -797,25 +861,31 @@ static void ipi_call_interrupt(void) | |||
797 | smp_call_function_interrupt(); | 861 | smp_call_function_interrupt(); |
798 | } | 862 | } |
799 | 863 | ||
864 | DECLARE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device); | ||
865 | |||
800 | void ipi_decode(struct smtc_ipi *pipi) | 866 | void ipi_decode(struct smtc_ipi *pipi) |
801 | { | 867 | { |
868 | unsigned int cpu = smp_processor_id(); | ||
869 | struct clock_event_device *cd; | ||
802 | void *arg_copy = pipi->arg; | 870 | void *arg_copy = pipi->arg; |
803 | int type_copy = pipi->type; | 871 | int type_copy = pipi->type; |
804 | int dest_copy = pipi->dest; | 872 | int ticks; |
805 | 873 | ||
806 | smtc_ipi_nq(&freeIPIq, pipi); | 874 | smtc_ipi_nq(&freeIPIq, pipi); |
807 | switch (type_copy) { | 875 | switch (type_copy) { |
808 | case SMTC_CLOCK_TICK: | 876 | case SMTC_CLOCK_TICK: |
809 | irq_enter(); | 877 | irq_enter(); |
810 | kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + cp0_compare_irq]++; | 878 | kstat_this_cpu.irqs[MIPS_CPU_IRQ_BASE + 1]++; |
811 | /* Invoke Clock "Interrupt" */ | 879 | cd = &per_cpu(smtc_dummy_clockevent_device, cpu); |
812 | ipi_timer_latch[dest_copy] = 0; | 880 | ticks = atomic_read(&ipi_timer_latch[cpu]); |
813 | #ifdef CONFIG_SMTC_IDLE_HOOK_DEBUG | 881 | atomic_sub(ticks, &ipi_timer_latch[cpu]); |
814 | clock_hang_reported[dest_copy] = 0; | 882 | while (ticks) { |
815 | #endif /* CONFIG_SMTC_IDLE_HOOK_DEBUG */ | 883 | cd->event_handler(cd); |
816 | local_timer_interrupt(0, NULL); | 884 | ticks--; |
885 | } | ||
817 | irq_exit(); | 886 | irq_exit(); |
818 | break; | 887 | break; |
888 | |||
819 | case LINUX_SMP_IPI: | 889 | case LINUX_SMP_IPI: |
820 | switch ((int)arg_copy) { | 890 | switch ((int)arg_copy) { |
821 | case SMP_RESCHEDULE_YOURSELF: | 891 | case SMP_RESCHEDULE_YOURSELF: |
@@ -830,6 +900,15 @@ void ipi_decode(struct smtc_ipi *pipi) | |||
830 | break; | 900 | break; |
831 | } | 901 | } |
832 | break; | 902 | break; |
903 | #ifdef CONFIG_MIPS_MT_SMTC_IRQAFF | ||
904 | case IRQ_AFFINITY_IPI: | ||
905 | /* | ||
906 | * Accept a "forwarded" interrupt that was initially | ||
907 | * taken by a TC who doesn't have affinity for the IRQ. | ||
908 | */ | ||
909 | do_IRQ_no_affinity((int)arg_copy); | ||
910 | break; | ||
911 | #endif /* CONFIG_MIPS_MT_SMTC_IRQAFF */ | ||
833 | default: | 912 | default: |
834 | printk("Impossible SMTC IPI Type 0x%x\n", type_copy); | 913 | printk("Impossible SMTC IPI Type 0x%x\n", type_copy); |
835 | break; | 914 | break; |
@@ -858,25 +937,6 @@ void deferred_smtc_ipi(void) | |||
858 | } | 937 | } |
859 | 938 | ||
860 | /* | 939 | /* |
861 | * Send clock tick to all TCs except the one executing the funtion | ||
862 | */ | ||
863 | |||
864 | void smtc_timer_broadcast(void) | ||
865 | { | ||
866 | int cpu; | ||
867 | int myTC = cpu_data[smp_processor_id()].tc_id; | ||
868 | int myVPE = cpu_data[smp_processor_id()].vpe_id; | ||
869 | |||
870 | smtc_cpu_stats[smp_processor_id()].timerints++; | ||
871 | |||
872 | for_each_online_cpu(cpu) { | ||
873 | if (cpu_data[cpu].vpe_id == myVPE && | ||
874 | cpu_data[cpu].tc_id != myTC) | ||
875 | smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0); | ||
876 | } | ||
877 | } | ||
878 | |||
879 | /* | ||
880 | * Cross-VPE interrupts in the SMTC prototype use "software interrupts" | 940 | * Cross-VPE interrupts in the SMTC prototype use "software interrupts" |
881 | * 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 |
882 | * in some regards preferable to have external logic for "doorbell" hardware | 942 | * in some regards preferable to have external logic for "doorbell" hardware |
@@ -1117,11 +1177,11 @@ void smtc_idle_loop_hook(void) | |||
1117 | for (tc = 0; tc < NR_CPUS; tc++) { | 1177 | for (tc = 0; tc < NR_CPUS; tc++) { |
1118 | /* Don't check ourself - we'll dequeue IPIs just below */ | 1178 | /* Don't check ourself - we'll dequeue IPIs just below */ |
1119 | if ((tc != smp_processor_id()) && | 1179 | if ((tc != smp_processor_id()) && |
1120 | ipi_timer_latch[tc] > timerq_limit) { | 1180 | atomic_read(&ipi_timer_latch[tc]) > timerq_limit) { |
1121 | if (clock_hang_reported[tc] == 0) { | 1181 | if (clock_hang_reported[tc] == 0) { |
1122 | pdb_msg += sprintf(pdb_msg, | 1182 | pdb_msg += sprintf(pdb_msg, |
1123 | "TC %d looks hung with timer latch at %d\n", | 1183 | "TC %d looks hung with timer latch at %d\n", |
1124 | tc, ipi_timer_latch[tc]); | 1184 | tc, atomic_read(&ipi_timer_latch[tc])); |
1125 | clock_hang_reported[tc]++; | 1185 | clock_hang_reported[tc]++; |
1126 | } | 1186 | } |
1127 | } | 1187 | } |
@@ -1162,7 +1222,7 @@ void smtc_soft_dump(void) | |||
1162 | smtc_ipi_qdump(); | 1222 | smtc_ipi_qdump(); |
1163 | printk("Timer IPI Backlogs:\n"); | 1223 | printk("Timer IPI Backlogs:\n"); |
1164 | for (i=0; i < NR_CPUS; i++) { | 1224 | for (i=0; i < NR_CPUS; i++) { |
1165 | printk("%d: %d\n", i, ipi_timer_latch[i]); | 1225 | printk("%d: %d\n", i, atomic_read(&ipi_timer_latch[i])); |
1166 | } | 1226 | } |
1167 | printk("%d Recoveries of \"stolen\" FPU\n", | 1227 | printk("%d Recoveries of \"stolen\" FPU\n", |
1168 | atomic_read(&smtc_fpu_recoveries)); | 1228 | atomic_read(&smtc_fpu_recoveries)); |
@@ -1204,7 +1264,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) | |||
1204 | if (cpu_has_vtag_icache) | 1264 | if (cpu_has_vtag_icache) |
1205 | flush_icache_all(); | 1265 | flush_icache_all(); |
1206 | /* Traverse all online CPUs (hack requires contigous range) */ | 1266 | /* Traverse all online CPUs (hack requires contigous range) */ |
1207 | for (i = 0; i < num_online_cpus(); i++) { | 1267 | for_each_online_cpu(i) { |
1208 | /* | 1268 | /* |
1209 | * We don't need to worry about our own CPU, nor those of | 1269 | * We don't need to worry about our own CPU, nor those of |
1210 | * CPUs who don't share our TLB. | 1270 | * CPUs who don't share our TLB. |
@@ -1233,7 +1293,7 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) | |||
1233 | /* | 1293 | /* |
1234 | * SMTC shares the TLB within VPEs and possibly across all VPEs. | 1294 | * SMTC shares the TLB within VPEs and possibly across all VPEs. |
1235 | */ | 1295 | */ |
1236 | for (i = 0; i < num_online_cpus(); i++) { | 1296 | for_each_online_cpu(i) { |
1237 | if ((smtc_status & SMTC_TLB_SHARED) || | 1297 | if ((smtc_status & SMTC_TLB_SHARED) || |
1238 | (cpu_data[i].vpe_id == cpu_data[cpu].vpe_id)) | 1298 | (cpu_data[i].vpe_id == cpu_data[cpu].vpe_id)) |
1239 | cpu_context(i, mm) = asid_cache(i) = asid; | 1299 | cpu_context(i, mm) = asid_cache(i) = asid; |