aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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