Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d25bf7e5 authored by Venkatesh Pallipadi's avatar Venkatesh Pallipadi Committed by Linus Torvalds
Browse files

[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: default avatarVenkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: default avatarAndi Kleen <ak@suse.de>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 6eb0a0fd
Loading
Loading
Loading
Loading
+53 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@
#include <linux/mc146818rtc.h>
#include <linux/kernel_stat.h>
#include <linux/sysdev.h>
#include <linux/module.h>

#include <asm/atomic.h>
#include <asm/smp.h>
@@ -38,6 +39,12 @@ int apic_verbosity;

int disable_apic_timer __initdata;

/*
 * cpu_mask that denotes the CPUs that needs timer interrupt coming in as
 * IPIs in place of local APIC timers
 */
static cpumask_t timer_interrupt_broadcast_ipi_mask;

/* Using APIC to generate smp_local_timer_interrupt? */
int using_apic_timer = 0;

@@ -656,9 +663,14 @@ void __init init_apic_mappings(void)
static void __setup_APIC_LVTT(unsigned int clocks)
{
	unsigned int lvtt_value, tmp_value, ver;
	int cpu = smp_processor_id();

	ver = GET_APIC_VERSION(apic_read(APIC_LVR));
	lvtt_value = APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;

	if (cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask))
		lvtt_value |= APIC_LVT_MASKED;

	apic_write_around(APIC_LVTT, lvtt_value);

	/*
@@ -781,7 +793,7 @@ void __cpuinit setup_secondary_APIC_clock(void)
	local_irq_enable();
}

void __cpuinit disable_APIC_timer(void)
void disable_APIC_timer(void)
{
	if (using_apic_timer) {
		unsigned long v;
@@ -793,7 +805,10 @@ void __cpuinit disable_APIC_timer(void)

void enable_APIC_timer(void)
{
	if (using_apic_timer) {
	int cpu = smp_processor_id();

	if (using_apic_timer &&
	    !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
		unsigned long v;

		v = apic_read(APIC_LVTT);
@@ -801,6 +816,42 @@ void enable_APIC_timer(void)
	}
}

void switch_APIC_timer_to_ipi(void *cpumask)
{
	cpumask_t mask = *(cpumask_t *)cpumask;
	int cpu = smp_processor_id();

	if (cpu_isset(cpu, mask) &&
	    !cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
		disable_APIC_timer();
		cpu_set(cpu, timer_interrupt_broadcast_ipi_mask);
	}
}
EXPORT_SYMBOL(switch_APIC_timer_to_ipi);

void smp_send_timer_broadcast_ipi(void)
{
	cpumask_t mask;

	cpus_and(mask, cpu_online_map, timer_interrupt_broadcast_ipi_mask);
	if (!cpus_empty(mask)) {
		send_IPI_mask(mask, LOCAL_TIMER_VECTOR);
	}
}

void switch_ipi_to_APIC_timer(void *cpumask)
{
	cpumask_t mask = *(cpumask_t *)cpumask;
	int cpu = smp_processor_id();

	if (cpu_isset(cpu, mask) &&
	    cpu_isset(cpu, timer_interrupt_broadcast_ipi_mask)) {
		cpu_clear(cpu, timer_interrupt_broadcast_ipi_mask);
		enable_APIC_timer();
	}
}
EXPORT_SYMBOL(switch_ipi_to_APIC_timer);

int setup_profiling_timer(unsigned int multiplier)
{
	return -EINVAL;
+5 −0
Original line number Diff line number Diff line
@@ -471,6 +471,11 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 
	write_sequnlock(&xtime_lock);

#ifdef CONFIG_X86_LOCAL_APIC
	if (using_apic_timer)
		smp_send_timer_broadcast_ipi();
#endif

	return IRQ_HANDLED;
}

+6 −0
Original line number Diff line number Diff line
@@ -113,6 +113,12 @@ extern int disable_timer_pin_1;

extern void setup_threshold_lvt(unsigned long lvt_off);

void smp_send_timer_broadcast_ipi(void);
void switch_APIC_timer_to_ipi(void *cpumask);
void switch_ipi_to_APIC_timer(void *cpumask);

#define ARCH_APICTIMER_STOPS_ON_C3	1

#endif /* CONFIG_X86_LOCAL_APIC */

extern unsigned boot_cpu_id;