Loading arch/x86/kernel/apic_64.c +79 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/sysdev.h> #include <linux/module.h> #include <linux/ioport.h> #include <linux/clockchips.h> #include <asm/atomic.h> #include <asm/smp.h> Loading Loading @@ -57,6 +58,77 @@ static struct resource lapic_resource = { static unsigned int calibration_result; static int lapic_next_event(unsigned long delta, struct clock_event_device *evt); static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt); static void lapic_timer_broadcast(cpumask_t mask); static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen); static struct clock_event_device lapic_clockevent = { .name = "lapic", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, .shift = 32, .set_mode = lapic_timer_setup, .set_next_event = lapic_next_event, .broadcast = lapic_timer_broadcast, .rating = 100, .irq = -1, }; static DEFINE_PER_CPU(struct clock_event_device, lapic_events); static int lapic_next_event(unsigned long delta, struct clock_event_device *evt) { apic_write(APIC_TMICT, delta); return 0; } static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt) { unsigned long flags; unsigned int v; /* Lapic used as dummy for broadcast ? */ if (evt->features & CLOCK_EVT_FEAT_DUMMY) return; local_irq_save(flags); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_ONESHOT: __setup_APIC_LVTT(calibration_result, mode != CLOCK_EVT_MODE_PERIODIC, 1); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: v = apic_read(APIC_LVTT); v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); apic_write(APIC_LVTT, v); break; case CLOCK_EVT_MODE_RESUME: /* Nothing to do here */ break; } local_irq_restore(flags); } /* * Local APIC timer broadcast function */ static void lapic_timer_broadcast(cpumask_t mask) { #ifdef CONFIG_SMP send_IPI_mask(mask, LOCAL_TIMER_VECTOR); #endif } /* * cpu_mask that denotes the CPUs that needs timer interrupt coming in as * IPIs in place of local APIC timers Loading Loading @@ -866,6 +938,13 @@ static void __init calibrate_APIC_clock(void) printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", result / 1000 / 1000, result / 1000 % 1000); /* Calculate the scaled math multiplication factor */ lapic_clockevent.mult = div_sc(result, NSEC_PER_SEC, 32); lapic_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); lapic_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &lapic_clockevent); calibration_result = result / HZ; } Loading arch/x86_64/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,10 @@ config CLOCKSOURCE_WATCHDOG bool default y config GENERIC_CLOCKEVENTS_MIGR bool default y config ZONE_DMA32 bool default y Loading Loading @@ -134,6 +138,8 @@ source "init/Kconfig" menu "Processor type and features" source "kernel/time/Kconfig" choice prompt "Subarchitecture Type" default X86_PC Loading Loading
arch/x86/kernel/apic_64.c +79 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #include <linux/sysdev.h> #include <linux/module.h> #include <linux/ioport.h> #include <linux/clockchips.h> #include <asm/atomic.h> #include <asm/smp.h> Loading Loading @@ -57,6 +58,77 @@ static struct resource lapic_resource = { static unsigned int calibration_result; static int lapic_next_event(unsigned long delta, struct clock_event_device *evt); static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt); static void lapic_timer_broadcast(cpumask_t mask); static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen); static struct clock_event_device lapic_clockevent = { .name = "lapic", .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP | CLOCK_EVT_FEAT_DUMMY, .shift = 32, .set_mode = lapic_timer_setup, .set_next_event = lapic_next_event, .broadcast = lapic_timer_broadcast, .rating = 100, .irq = -1, }; static DEFINE_PER_CPU(struct clock_event_device, lapic_events); static int lapic_next_event(unsigned long delta, struct clock_event_device *evt) { apic_write(APIC_TMICT, delta); return 0; } static void lapic_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt) { unsigned long flags; unsigned int v; /* Lapic used as dummy for broadcast ? */ if (evt->features & CLOCK_EVT_FEAT_DUMMY) return; local_irq_save(flags); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: case CLOCK_EVT_MODE_ONESHOT: __setup_APIC_LVTT(calibration_result, mode != CLOCK_EVT_MODE_PERIODIC, 1); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: v = apic_read(APIC_LVTT); v |= (APIC_LVT_MASKED | LOCAL_TIMER_VECTOR); apic_write(APIC_LVTT, v); break; case CLOCK_EVT_MODE_RESUME: /* Nothing to do here */ break; } local_irq_restore(flags); } /* * Local APIC timer broadcast function */ static void lapic_timer_broadcast(cpumask_t mask) { #ifdef CONFIG_SMP send_IPI_mask(mask, LOCAL_TIMER_VECTOR); #endif } /* * cpu_mask that denotes the CPUs that needs timer interrupt coming in as * IPIs in place of local APIC timers Loading Loading @@ -866,6 +938,13 @@ static void __init calibrate_APIC_clock(void) printk(KERN_INFO "Detected %d.%03d MHz APIC timer.\n", result / 1000 / 1000, result / 1000 % 1000); /* Calculate the scaled math multiplication factor */ lapic_clockevent.mult = div_sc(result, NSEC_PER_SEC, 32); lapic_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); lapic_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &lapic_clockevent); calibration_result = result / HZ; } Loading
arch/x86_64/Kconfig +6 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,10 @@ config CLOCKSOURCE_WATCHDOG bool default y config GENERIC_CLOCKEVENTS_MIGR bool default y config ZONE_DMA32 bool default y Loading Loading @@ -134,6 +138,8 @@ source "init/Kconfig" menu "Processor type and features" source "kernel/time/Kconfig" choice prompt "Subarchitecture Type" default X86_PC Loading