diff options
-rw-r--r-- | arch/i386/kernel/apic.c | 78 | ||||
-rw-r--r-- | arch/i386/kernel/time.c | 6 | ||||
-rw-r--r-- | drivers/acpi/processor_idle.c | 15 | ||||
-rw-r--r-- | include/asm-i386/apic.h | 5 |
4 files changed, 102 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; |
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c index 41c5b2dc6200..a14d594bfbeb 100644 --- a/arch/i386/kernel/time.c +++ b/arch/i386/kernel/time.c | |||
@@ -302,6 +302,12 @@ irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
302 | do_timer_interrupt(irq, regs); | 302 | do_timer_interrupt(irq, regs); |
303 | 303 | ||
304 | write_sequnlock(&xtime_lock); | 304 | write_sequnlock(&xtime_lock); |
305 | |||
306 | #ifdef CONFIG_X86_LOCAL_APIC | ||
307 | if (using_apic_timer) | ||
308 | smp_send_timer_broadcast_ipi(regs); | ||
309 | #endif | ||
310 | |||
305 | return IRQ_HANDLED; | 311 | return IRQ_HANDLED; |
306 | } | 312 | } |
307 | 313 | ||
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 807b0df308f1..cc049338e418 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c | |||
@@ -843,6 +843,15 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) | |||
843 | unsigned int i; | 843 | unsigned int i; |
844 | unsigned int working = 0; | 844 | unsigned int working = 0; |
845 | 845 | ||
846 | #ifdef ARCH_APICTIMER_STOPS_ON_C3 | ||
847 | struct cpuinfo_x86 *c = cpu_data + pr->id; | ||
848 | cpumask_t mask = cpumask_of_cpu(pr->id); | ||
849 | |||
850 | if (c->x86_vendor == X86_VENDOR_INTEL) { | ||
851 | on_each_cpu(switch_ipi_to_APIC_timer, &mask, 1, 1); | ||
852 | } | ||
853 | #endif | ||
854 | |||
846 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { | 855 | for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { |
847 | struct acpi_processor_cx *cx = &pr->power.states[i]; | 856 | struct acpi_processor_cx *cx = &pr->power.states[i]; |
848 | 857 | ||
@@ -857,6 +866,12 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) | |||
857 | 866 | ||
858 | case ACPI_STATE_C3: | 867 | case ACPI_STATE_C3: |
859 | acpi_processor_power_verify_c3(pr, cx); | 868 | acpi_processor_power_verify_c3(pr, cx); |
869 | #ifdef ARCH_APICTIMER_STOPS_ON_C3 | ||
870 | if (c->x86_vendor == X86_VENDOR_INTEL) { | ||
871 | on_each_cpu(switch_APIC_timer_to_ipi, | ||
872 | &mask, 1, 1); | ||
873 | } | ||
874 | #endif | ||
860 | break; | 875 | break; |
861 | } | 876 | } |
862 | 877 | ||
diff --git a/include/asm-i386/apic.h b/include/asm-i386/apic.h index 8c454aa58ac6..d30b8571573f 100644 --- a/include/asm-i386/apic.h +++ b/include/asm-i386/apic.h | |||
@@ -132,6 +132,11 @@ extern unsigned int nmi_watchdog; | |||
132 | 132 | ||
133 | extern int disable_timer_pin_1; | 133 | extern int disable_timer_pin_1; |
134 | 134 | ||
135 | void smp_send_timer_broadcast_ipi(struct pt_regs *regs); | ||
136 | void switch_APIC_timer_to_ipi(void *cpumask); | ||
137 | void switch_ipi_to_APIC_timer(void *cpumask); | ||
138 | #define ARCH_APICTIMER_STOPS_ON_C3 1 | ||
139 | |||
135 | #else /* !CONFIG_X86_LOCAL_APIC */ | 140 | #else /* !CONFIG_X86_LOCAL_APIC */ |
136 | static inline void lapic_shutdown(void) { } | 141 | static inline void lapic_shutdown(void) { } |
137 | 142 | ||