diff options
author | Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> | 2006-01-11 16:44:24 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-11 22:04:54 -0500 |
commit | d25bf7e5fe73b5b6d2246ab0be08ae35d718456b (patch) | |
tree | 472749fef9b017e9a4d94f9e07a29dc44c3294cb /arch | |
parent | 6eb0a0fd059598ee0d49c6283ce25cccd743e9fc (diff) |
[PATCH] x86_64: 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).
Patch below adds the code for x86_64.
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')
-rw-r--r-- | arch/x86_64/kernel/apic.c | 55 | ||||
-rw-r--r-- | arch/x86_64/kernel/time.c | 5 |
2 files changed, 58 insertions, 2 deletions
diff --git a/arch/x86_64/kernel/apic.c b/arch/x86_64/kernel/apic.c index fa47ae731576..40cbd60e2d0b 100644 --- a/arch/x86_64/kernel/apic.c +++ b/arch/x86_64/kernel/apic.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/mc146818rtc.h> | 25 | #include <linux/mc146818rtc.h> |
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/module.h> | ||
28 | 29 | ||
29 | #include <asm/atomic.h> | 30 | #include <asm/atomic.h> |
30 | #include <asm/smp.h> | 31 | #include <asm/smp.h> |
@@ -38,6 +39,12 @@ int apic_verbosity; | |||
38 | 39 | ||
39 | int disable_apic_timer __initdata; | 40 | int disable_apic_timer __initdata; |
40 | 41 | ||
42 | /* | ||
43 | * cpu_mask that denotes the CPUs that needs timer interrupt coming in as | ||
44 | * IPIs in place of local APIC timers | ||
45 | */ | ||
46 | static cpumask_t timer_interrupt_broadcast_ipi_mask; | ||
47 | |||
41 | /* Using APIC to generate smp_local_timer_interrupt? */ | 48 | /* Using APIC to generate smp_local_timer_interrupt? */ |
42 | int using_apic_timer = 0; | 49 | int using_apic_timer = 0; |
43 | 50 | ||
@@ -656,9 +663,14 @@ void __init init_apic_mappings(void) | |||
656 | static void __setup_APIC_LVTT(unsigned int clocks) | 663 | static void __setup_APIC_LVTT(unsigned int clocks) |
657 | { | 664 | { |
658 | unsigned int lvtt_value, tmp_value, ver; | 665 | unsigned int lvtt_value, tmp_value, ver; |
666 | int cpu = smp_processor_id(); | ||
659 | 667 | ||
660 | ver = GET_APIC_VERSION(apic_read(APIC_LVR)); | 668 | ver = GET_APIC_VERSION(apic_read(APIC_LVR)); |
661 | lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; | 669 | lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR; |
670 | |||
671 | if (cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) | ||
672 | lvtt_value |= APIC_LVT_MASKED; | ||
673 | |||
662 | apic_write_around(APIC_LVTT, lvtt_value); | 674 | apic_write_around(APIC_LVTT, lvtt_value); |
663 | 675 | ||
664 | /* | 676 | /* |
@@ -781,7 +793,7 @@ void __cpuinit setup_secondary_APIC_clock(void) | |||
781 | local_irq_enable(); | 793 | local_irq_enable(); |
782 | } | 794 | } |
783 | 795 | ||
784 | void __cpuinit disable_APIC_timer(void) | 796 | void disable_APIC_timer(void) |
785 | { | 797 | { |
786 | if (using_apic_timer) { | 798 | if (using_apic_timer) { |
787 | unsigned long v; | 799 | unsigned long v; |
@@ -793,7 +805,10 @@ void __cpuinit disable_APIC_timer(void) | |||
793 | 805 | ||
794 | void enable_APIC_timer(void) | 806 | void enable_APIC_timer(void) |
795 | { | 807 | { |
796 | if (using_apic_timer) { | 808 | int cpu = smp_processor_id(); |
809 | |||
810 | if (using_apic_timer && | ||
811 | !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { | ||
797 | unsigned long v; | 812 | unsigned long v; |
798 | 813 | ||
799 | v = apic_read(APIC_LVTT); | 814 | v = apic_read(APIC_LVTT); |
@@ -801,6 +816,42 @@ void enable_APIC_timer(void) | |||
801 | } | 816 | } |
802 | } | 817 | } |
803 | 818 | ||
819 | void switch_APIC_timer_to_ipi(void *cpumask) | ||
820 | { | ||
821 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
822 | int cpu = smp_processor_id(); | ||
823 | |||
824 | if (cpu_isset(cpu, mask) && | ||
825 | !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { | ||
826 | disable_APIC_timer(); | ||
827 | cpu_set(cpu, timer_interrupt_broadcast_ipi_mask); | ||
828 | } | ||
829 | } | ||
830 | EXPORT_SYMBOL(switch_APIC_timer_to_ipi); | ||
831 | |||
832 | void smp_send_timer_broadcast_ipi(void) | ||
833 | { | ||
834 | cpumask_t mask; | ||
835 | |||
836 | cpus_and(mask, cpu_online_map, timer_interrupt_broadcast_ipi_mask); | ||
837 | if (!cpus_empty(mask)) { | ||
838 | send_IPI_mask(mask, LOCAL_TIMER_VECTOR); | ||
839 | } | ||
840 | } | ||
841 | |||
842 | void switch_ipi_to_APIC_timer(void *cpumask) | ||
843 | { | ||
844 | cpumask_t mask = *(cpumask_t *)cpumask; | ||
845 | int cpu = smp_processor_id(); | ||
846 | |||
847 | if (cpu_isset(cpu, mask) && | ||
848 | cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) { | ||
849 | cpu_clear(cpu, timer_interrupt_broadcast_ipi_mask); | ||
850 | enable_APIC_timer(); | ||
851 | } | ||
852 | } | ||
853 | EXPORT_SYMBOL(switch_ipi_to_APIC_timer); | ||
854 | |||
804 | int setup_profiling_timer(unsigned int multiplier) | 855 | int setup_profiling_timer(unsigned int multiplier) |
805 | { | 856 | { |
806 | return -EINVAL; | 857 | return -EINVAL; |
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c index 43c9fa0f8d5f..211bf0e51d9d 100644 --- a/arch/x86_64/kernel/time.c +++ b/arch/x86_64/kernel/time.c | |||
@@ -471,6 +471,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
471 | 471 | ||
472 | write_sequnlock(&xtime_lock); | 472 | write_sequnlock(&xtime_lock); |
473 | 473 | ||
474 | #ifdef CONFIG_X86_LOCAL_APIC | ||
475 | if (using_apic_timer) | ||
476 | smp_send_timer_broadcast_ipi(); | ||
477 | #endif | ||
478 | |||
474 | return IRQ_HANDLED; | 479 | return IRQ_HANDLED; |
475 | } | 480 | } |
476 | 481 | ||