aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--arch/i386/kernel/apic.c78
-rw-r--r--arch/i386/kernel/time.c6
-rw-r--r--drivers/acpi/processor_idle.c15
-rw-r--r--include/asm-i386/apic.h5
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 */
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;
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
133extern int disable_timer_pin_1; 133extern int disable_timer_pin_1;
134 134
135void smp_send_timer_broadcast_ipi(struct pt_regs *regs);
136void switch_APIC_timer_to_ipi(void *cpumask);
137void 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 */
136static inline void lapic_shutdown(void) { } 141static inline void lapic_shutdown(void) { }
137 142