aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386/kernel/apic.c
diff options
context:
space:
mode:
authorVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>2006-01-11 16:44:21 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-11 22:04:54 -0500
commit6eb0a0fd059598ee0d49c6283ce25cccd743e9fc (patch)
treea608f92e3b8a94cba89e94786169897c64629580 /arch/i386/kernel/apic.c
parent5a07a30c3cc4dc438494d6416ffa74008a2194b3 (diff)
[PATCH] i386: Handle missing local APIC timer interrupts on C3 state
Whenever we see that a CPU is capable of C3 (during ACPI cstate init), we disable local APIC timer and switch to using a broadcast from external timer interrupt (IRQ 0). This is needed because Intel CPUs stop the local APIC timer in C3. This is currently only enabled for Intel CPUs. Patch below adds the code for i386 and also the ACPI hunk. Signed-off-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> Signed-off-by: Andi Kleen <ak@suse.de> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel/apic.c')
-rw-r--r--arch/i386/kernel/apic.c78
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 */
49static 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 */
46int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */ 54int enable_local_apic __initdata = 0; /* -1=force-disable, +1=force-enable */
@@ -931,11 +939,16 @@ void (*wait_timer_tick)(void) __devinitdata = wait_8254_wraparound;
931static void __setup_APIC_LVTT(unsigned int clocks) 939static 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
1071void __devinit disable_APIC_timer(void) 1084void 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
1081void enable_APIC_timer(void) 1094void 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
1107void 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}
1118EXPORT_SYMBOL(switch_APIC_timer_to_ipi);
1119
1120void 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}
1131EXPORT_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
1198static 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
1211void 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
1155int setup_profiling_timer(unsigned int multiplier) 1229int setup_profiling_timer(unsigned int multiplier)
1156{ 1230{
1157 return -EINVAL; 1231 return -EINVAL;