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

Commit b8ce3359 authored by Thomas Gleixner's avatar Thomas Gleixner Committed by Thomas Gleixner
Browse files

x86_64: convert to clock events



Finally switch to the clockevents code. Share code with i386 for
hpet and PIT.

Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarChris Wright <chrisw@sous-sol.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarArjan van de Ven <arjan@linux.intel.com>
parent ba7eda4c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@ obj-y := process_64.o signal_64.o entry_64.o traps_64.o irq_64.o \
		ptrace_64.o time_64.o ioport_64.o ldt_64.o setup_64.o i8259_64.o sys_x86_64.o \
		x8664_ksyms_64.o i387_64.o syscall_64.o vsyscall_64.o \
		setup64.o bootflag.o e820_64.o reboot_64.o quirks.o i8237.o \
		pci-dma_64.o pci-nommu_64.o alternative.o hpet_64.o tsc_64.o bugs_64.o \
		perfctr-watchdog.o
		pci-dma_64.o pci-nommu_64.o alternative.o hpet_32.o tsc_64.o bugs_64.o \
		perfctr-watchdog.o i8253_32.o

obj-$(CONFIG_STACKTRACE)	+= stacktrace.o
obj-$(CONFIG_X86_MCE)		+= mce_64.o therm_throt.o
+50 −40
Original line number Diff line number Diff line
@@ -857,25 +857,12 @@ static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)

static void setup_APIC_timer(void)
{
	unsigned long flags;
	int irqen;
	struct clock_event_device *levt = &__get_cpu_var(lapic_events);

	local_irq_save(flags);
	memcpy(levt, &lapic_clockevent, sizeof(*levt));
	levt->cpumask = cpumask_of_cpu(smp_processor_id());

	irqen = ! cpu_isset(smp_processor_id(),
			    timer_interrupt_broadcast_ipi_mask);
	__setup_APIC_LVTT(calibration_result, 0, irqen);
	/* Turn off PIT interrupt if we use APIC timer as main timer.
	   Only works with the PM timer right now
	   TBD fix it for HPET too. */
	if ((pmtmr_ioport != 0) &&
		smp_processor_id() == boot_cpu_id &&
		apic_runs_main_timer == 1 &&
		!cpu_isset(boot_cpu_id, timer_interrupt_broadcast_ipi_mask)) {
		stop_timer_interrupt();
		apic_runs_main_timer++;
	}
	local_irq_restore(flags);
	clockevents_register_device(levt);
}

/*
@@ -950,18 +937,34 @@ static void __init calibrate_APIC_clock(void)

void __init setup_boot_APIC_clock (void)
{
	/*
	 * The local apic timer can be disabled via the kernel commandline.
	 * Register the lapic timer as a dummy clock event source on SMP
	 * systems, so the broadcast mechanism is used. On UP systems simply
	 * ignore it.
	 */
	if (disable_apic_timer) {
		printk(KERN_INFO "Disabling APIC timer\n");
		/* No broadcast on UP ! */
		if (num_possible_cpus() > 1)
			setup_APIC_timer();
		return;
	}

	printk(KERN_INFO "Using local APIC timer interrupts.\n");
	using_apic_timer = 1;

	calibrate_APIC_clock();

	/*
	 * Now set up the timer for real.
	 * If nmi_watchdog is set to IO_APIC, we need the
	 * PIT/HPET going.  Otherwise register lapic as a dummy
	 * device.
	 */
	if (nmi_watchdog != NMI_IO_APIC)
		lapic_clockevent.features &= ~CLOCK_EVT_FEAT_DUMMY;
	else
		printk(KERN_WARNING "APIC timer registered as dummy,"
		       " due to nmi_watchdog=1!\n");

	setup_APIC_timer();
}

@@ -1073,22 +1076,34 @@ void setup_APIC_extended_lvt(unsigned char lvt_off, unsigned char vector,

void smp_local_timer_interrupt(void)
{
	profile_tick(CPU_PROFILING);
#ifdef CONFIG_SMP
	update_process_times(user_mode(get_irq_regs()));
#endif
	if (apic_runs_main_timer > 1 && smp_processor_id() == boot_cpu_id)
		main_timer_handler();
	int cpu = smp_processor_id();
	struct clock_event_device *evt = &per_cpu(lapic_events, cpu);

	/*
	 * We take the 'long' return path, and there every subsystem
	 * grabs the appropriate locks (kernel lock/ irq lock).
	 *
	 * We might want to decouple profiling from the 'long path',
	 * and do the profiling totally in assembly.
	 * Normally we should not be here till LAPIC has been initialized but
	 * in some cases like kdump, its possible that there is a pending LAPIC
	 * timer interrupt from previous kernel's context and is delivered in
	 * new kernel the moment interrupts are enabled.
	 *
	 * Currently this isn't too much of an issue (performance wise),
	 * we can take more than 100K local irqs per second on a 100 MHz P5.
	 * Interrupts are enabled early and LAPIC is setup much later, hence
	 * its possible that when we get here evt->event_handler is NULL.
	 * Check for event_handler being NULL and discard the interrupt as
	 * spurious.
	 */
	if (!evt->event_handler) {
		printk(KERN_WARNING
		       "Spurious LAPIC timer interrupt on cpu %d\n", cpu);
		/* Switch it off */
		lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt);
		return;
	}

	/*
	 * the NMI deadlock-detector uses this.
	 */
	add_pda(apic_timer_irqs, 1);

	evt->event_handler(evt);
}

/*
@@ -1103,11 +1118,6 @@ void smp_apic_timer_interrupt(struct pt_regs *regs)
{
	struct pt_regs *old_regs = set_irq_regs(regs);

	/*
	 * the NMI deadlock-detector uses this.
	 */
	add_pda(apic_timer_irqs, 1);

	/*
	 * NOTE! We'd better ACK the irq immediately,
	 * because timer handling can be slow.
@@ -1291,7 +1301,7 @@ static __init int setup_noapictimer(char *str)
static __init int setup_apicmaintimer(char *str)
{
	apic_runs_main_timer = 1;
	nohpet = 1;

	return 1;
}
__setup("apicmaintimer", setup_apicmaintimer);
@@ -1307,7 +1317,7 @@ static __init int setup_apicpmtimer(char *s)
{
	apic_calibrate_pmtmr = 1;
	notsc_setup(NULL);
	return setup_apicmaintimer(NULL);
	return 0;
}
__setup("apicpmtimer", setup_apicpmtimer);

+0 −46
Original line number Diff line number Diff line
@@ -444,46 +444,6 @@ void __init init_ISA_irqs (void)
	}
}

static void setup_timer_hardware(void)
{
	outb_p(0x34,0x43);		/* binary, mode 2, LSB/MSB, ch 0 */
	udelay(10);
	outb_p(LATCH & 0xff , 0x40);	/* LSB */
	udelay(10);
	outb(LATCH >> 8 , 0x40);	/* MSB */
}

static int timer_resume(struct sys_device *dev)
{
	setup_timer_hardware();
	return 0;
}

void i8254_timer_resume(void)
{
	setup_timer_hardware();
}

static struct sysdev_class timer_sysclass = {
	set_kset_name("timer_pit"),
	.resume		= timer_resume,
};

static struct sys_device device_timer = {
	.id		= 0,
	.cls		= &timer_sysclass,
};

static int __init init_timer_sysfs(void)
{
	int error = sysdev_class_register(&timer_sysclass);
	if (!error)
		error = sysdev_register(&device_timer);
	return error;
}

device_initcall(init_timer_sysfs);

void __init init_IRQ(void)
{
	int i;
@@ -533,12 +493,6 @@ void __init init_IRQ(void)
	set_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
	set_intr_gate(ERROR_APIC_VECTOR, error_interrupt);

	/*
	 * Set the clock to HZ Hz, we already have a valid
	 * vector now:
	 */
	setup_timer_hardware();

	if (!acpi_ioapic)
		setup_irq(2, &irq2);
}
+0 −4
Original line number Diff line number Diff line
@@ -223,8 +223,6 @@ void __cpuinit smp_callin(void)
	local_irq_disable();
	Dprintk("Stack at about %p\n",&cpuid);

	disable_APIC_timer();

	/*
	 * Save our processor parameters
	 */
@@ -348,8 +346,6 @@ void __cpuinit start_secondary(void)
		enable_8259A_irq(0);
	}

	enable_APIC_timer();

	/*
	 * The sibling maps must be set before turing the online map on for
	 * this cpu
+13 −94
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <linux/cpu.h>
#include <linux/kallsyms.h>
#include <linux/acpi.h>
#include <linux/clockchips.h>

#ifdef CONFIG_ACPI
#include <acpi/achware.h>	/* for PM timer frequency */
#include <acpi/acpi_bus.h>
@@ -46,12 +48,8 @@
#include <asm/nmi.h>
#include <asm/vgtod.h>

static char *timename = NULL;

DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
DEFINE_SPINLOCK(i8253_lock);
EXPORT_SYMBOL(i8253_lock);

volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;

@@ -194,6 +192,13 @@ static irqreturn_t timer_interrupt(int irq, void *dev_id)
	return IRQ_HANDLED;
}

static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
{
	global_clock_event->event_handler(global_clock_event);

	return IRQ_HANDLED;
}

unsigned long read_persistent_clock(void)
{
	unsigned int year, mon, day, hour, min, sec;
@@ -291,42 +296,8 @@ static unsigned int __init tsc_calibrate_cpu_khz(void)
	return pmc_now * tsc_khz / (tsc_now - tsc_start);
}

static void __pit_init(int val, u8 mode)
{
	unsigned long flags;

	spin_lock_irqsave(&i8253_lock, flags);
	outb_p(mode, PIT_MODE);
	outb_p(val & 0xff, PIT_CH0);	/* LSB */
	outb_p(val >> 8, PIT_CH0);	/* MSB */
	spin_unlock_irqrestore(&i8253_lock, flags);
}

void __init pit_init(void)
{
	__pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
}

void pit_stop_interrupt(void)
{
	__pit_init(0, 0x30); /* mode 0 */
}

void stop_timer_interrupt(void)
{
	char *name;
	if (hpet_address) {
		name = "HPET";
		hpet_timer_stop_set_go(0);
	} else {
		name = "PIT";
		pit_stop_interrupt();
	}
	printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
}

static struct irqaction irq0 = {
	.handler	= timer_interrupt,
	.handler	= timer_event_interrupt,
	.flags		= IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
	.mask		= CPU_MASK_NONE,
	.name		= "timer"
@@ -334,20 +305,10 @@ static struct irqaction irq0 = {

void __init time_init(void)
{
	if (nohpet)
		hpet_address = 0;

	if (hpet_arch_init())
		hpet_address = 0;
	if (!hpet_enable())
		setup_pit_timer();

	if (hpet_use_timer) {
		/* set tick_nsec to use the proper rate for HPET */
		tick_nsec = TICK_NSEC_HPET;
		timename = "HPET";
	} else {
		pit_init();
		timename = "PIT";
	}
	setup_irq(0, &irq0);

	tsc_calibrate();

@@ -369,46 +330,4 @@ void __init time_init(void)
	printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
		cpu_khz / 1000, cpu_khz % 1000);
	init_tsc_clocksource();

	setup_irq(0, &irq0);
}

/*
 * sysfs support for the timer.
 */

static int timer_suspend(struct sys_device *dev, pm_message_t state)
{
	return 0;
}

static int timer_resume(struct sys_device *dev)
{
	if (hpet_address)
		hpet_reenable();
	else
		i8254_timer_resume();
	return 0;
}

static struct sysdev_class timer_sysclass = {
	.resume = timer_resume,
	.suspend = timer_suspend,
	set_kset_name("timer"),
};

/* XXX this sysfs stuff should probably go elsewhere later -john */
static struct sys_device device_timer = {
	.id	= 0,
	.cls	= &timer_sysclass,
};

static int time_init_device(void)
{
	int error = sysdev_class_register(&timer_sysclass);
	if (!error)
		error = sysdev_register(&device_timer);
	return error;
}

device_initcall(time_init_device);
Loading