diff options
Diffstat (limited to 'arch/i386/kernel/apic.c')
-rw-r--r-- | arch/i386/kernel/apic.c | 78 |
1 files changed, 76 insertions, 2 deletions
diff --git a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c index 2d8c6ce1ecda..acd3f1e34ca6 100644 --- a/arch/i386/kernel/apic.c +++ b/arch/i386/kernel/apic.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/kernel_stat.h> | 26 | #include <linux/kernel_stat.h> |
27 | #include <linux/sysdev.h> | 27 | #include <linux/sysdev.h> |
28 | #include <linux/cpu.h> | 28 | #include <linux/cpu.h> |
29 | #include <linux/module.h> | ||
29 | 30 | ||
30 | #include <asm/atomic.h> | 31 | #include <asm/atomic.h> |
31 | #include <asm/smp.h> | 32 | #include <asm/smp.h> |
@@ -37,10 +38,17 @@ | |||
37 | #include <asm/i8253.h> | 38 | #include <asm/i8253.h> |
38 | 39 | ||
39 | #include <mach_apic.h> | 40 | #include <mach_apic.h> |
41 | #include <mach_ipi.h> | ||
40 | 42 | ||
41 | #include "io_ports.h" | 43 | #include "io_ports.h" |
42 | 44 | ||
43 | /* | 45 | /* |
46 | * cpu_mask that denotes the CPUs that needs timer interrupt coming in as | ||
47 | * IPIs in place of local APIC timers | ||
48 | */ | ||
49 | static cpumask_t timer_bcast_ipi; | ||
50 | |||
51 | /* | ||
44 | * Knob to control our willingness to enable the local APIC. | 52 | * Knob to control our willingness to enable the local APIC. |
45 | */ | 53 | */ |
46 | int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */ | 54 | int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */ |
@@ -931,11 +939,16 @@ void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound; | |||
931 | static void __setup_APIC_LVTT(unsigned int clocks) | 939 | static void __setup_APIC_LVTT(unsigned int clocks) |
932 | { | 940 | { |
933 | unsigned int lvtt_value, tmp_value, ver; | 941 | unsigned int lvtt_value, tmp_value, ver; |
942 | int cpu = smp_processor_id(); | ||
934 | 943 | ||
935 | ver = GET_APIC_VERSION(apic_read(APIC_LVR)); | 944 | ver = GET_APIC_VERSION(apic_read(APIC_LVR)); |
936 | lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; | 945 | lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; |
937 | if (!APIC_INTEGRATED(ver)) | 946 | if (!APIC_INTEGRATED(ver)) |
938 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); | 947 | lvtt_value |= SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV); |
948 | |||
949 | if (cpu_isset(cpu, timer_bcast_ipi)) | ||
950 | lvtt_value |= APIC_LVT_MASKED; | ||
951 | |||
939 | apic_write_around(APIC_LVTT, lvtt_value); | 952 | apic_write_around(APIC_LVTT, lvtt_value); |
940 | 953 | ||
941 | /* | 954 | /* |
@@ -1068,7 +1081,7 @@ void __devinit setup_secondary_APIC_clock(void) | |||
1068 | setup_APIC_timer(calibration_result); | 1081 | setup_APIC_timer(calibration_result); |
1069 | } | 1082 | } |
1070 | 1083 | ||
1071 | void __devinit disable_APIC_timer(void) | 1084 | void disable_APIC_timer(void) |
1072 | { | 1085 | { |
1073 | if (using_apic_timer) { | 1086 | if (using_apic_timer) { |
1074 | unsigned long v; | 1087 | unsigned long v; |
@@ -1080,7 +1093,10 @@ void __devinit disable_APIC_timer(void) | |||
1080 | 1093 | ||
1081 | void enable_APIC_timer(void) | 1094 | void enable_APIC_timer(void) |
1082 | { | 1095 | { |
1083 | if (using_apic_timer) { | 1096 | int cpu = smp_processor_id(); |
1097 | |||
1098 | if (using_apic_timer && | ||
1099 | !cpu_isset(cpu, timer_bcast_ipi)) { | ||
1084 | unsigned long v; | 1100 | unsigned long v; |
1085 | 1101 | ||
1086 | v = apic_read(APIC_LVTT); | 1102 | v = apic_read(APIC_LVTT); |
@@ -1088,6 +1104,32 @@ void enable_APIC_timer(void) | |||
1088 | } | 1104 | } |
1089 | } | 1105 | } |
1090 | 1106 | ||
1107 | void switch_APIC_timer_to_ipi(void *cpumask) | ||
1108 | { | ||
1109 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
1110 | int cpu = smp_processor_id(); | ||
1111 | |||
1112 | if (cpu_isset(cpu, mask) && | ||
1113 | !cpu_isset(cpu, timer_bcast_ipi)) { | ||
1114 | disable_APIC_timer(); | ||
1115 | cpu_set(cpu, timer_bcast_ipi); | ||
1116 | } | ||
1117 | } | ||
1118 | EXPORT_SYMBOL(switch_APIC_timer_to_ipi); | ||
1119 | |||
1120 | void switch_ipi_to_APIC_timer(void *cpumask) | ||
1121 | { | ||
1122 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
1123 | int cpu = smp_processor_id(); | ||
1124 | |||
1125 | if (cpu_isset(cpu, mask) && | ||
1126 | cpu_isset(cpu, timer_bcast_ipi)) { | ||
1127 | cpu_clear(cpu, timer_bcast_ipi); | ||
1128 | enable_APIC_timer(); | ||
1129 | } | ||
1130 | } | ||
1131 | EXPORT_SYMBOL(switch_ipi_to_APIC_timer); | ||
1132 | |||
1091 | #undef APIC_DIVISOR | 1133 | #undef APIC_DIVISOR |
1092 | 1134 | ||
1093 | /* | 1135 | /* |
@@ -1152,6 +1194,38 @@ fastcall void smp_apic_timer_interrupt(struct pt_regs *regs) | |||
1152 | irq_exit(); | 1194 | irq_exit(); |
1153 | } | 1195 | } |
1154 | 1196 | ||
1197 | #ifndef CONFIG_SMP | ||
1198 | static void up_apic_timer_interrupt_call(struct pt_regs *regs) | ||
1199 | { | ||
1200 | int cpu = smp_processor_id(); | ||
1201 | |||
1202 | /* | ||
1203 | * the NMI deadlock-detector uses this. | ||
1204 | */ | ||
1205 | per_cpu(irq_stat, cpu).apic_timer_irqs++; | ||
1206 | |||
1207 | smp_local_timer_interrupt(regs); | ||
1208 | } | ||
1209 | #endif | ||
1210 | |||
1211 | void smp_send_timer_broadcast_ipi(struct pt_regs *regs) | ||
1212 | { | ||
1213 | cpumask_t mask; | ||
1214 | |||
1215 | cpus_and(mask, cpu_online_map, timer_bcast_ipi); | ||
1216 | if (!cpus_empty(mask)) { | ||
1217 | #ifdef CONFIG_SMP | ||
1218 | send_IPI_mask(mask, LOCAL_TIMER_VECTOR); | ||
1219 | #else | ||
1220 | /* | ||
1221 | * We can directly call the apic timer interrupt handler | ||
1222 | * in UP case. Minus all irq related functions | ||
1223 | */ | ||
1224 | up_apic_timer_interrupt_call(regs); | ||
1225 | #endif | ||
1226 | } | ||
1227 | } | ||
1228 | |||
1155 | int setup_profiling_timer(unsigned int multiplier) | 1229 | int setup_profiling_timer(unsigned int multiplier) |
1156 | { | 1230 | { |
1157 | return -EINVAL; | 1231 | return -EINVAL; |