/* * The guts of the apic timer interrupt */ static void local_apic_timer_interrupt(void) { int cpu = smp_processor_id(); struct clock_event_device *evt = &per_cpu(lapic_events, cpu); /* * 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. * * 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); }
static void local_apic_timer_interrupt(void) { int cpu = smp_processor_id(); struct clock_event_device *evt = &per_cpu(lapic_events, cpu); if (!evt->event_handler) { pr_warning("Spurious LAPIC timer interrupt on cpu %d\n", cpu); lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, evt); return; } inc_irq_stat(apic_timer_irqs); evt->event_handler(evt); }
static int __init calibrate_APIC_clock(void) { struct clock_event_device *levt = &__get_cpu_var(lapic_events); void (*real_handler)(struct clock_event_device *dev); unsigned long deltaj; long delta, deltatsc; int pm_referenced = 0; local_irq_disable(); real_handler = global_clock_event->event_handler; global_clock_event->event_handler = lapic_cal_handler; __setup_APIC_LVTT(0xffffffff, 0, 0); local_irq_enable(); while (lapic_cal_loops <= LAPIC_CAL_LOOPS) cpu_relax(); local_irq_disable(); global_clock_event->event_handler = real_handler; delta = lapic_cal_t1 - lapic_cal_t2; apic_printk(APIC_VERBOSE, "... lapic delta = %ld\n", delta); deltatsc = (long)(lapic_cal_tsc2 - lapic_cal_tsc1); pm_referenced = !calibrate_by_pmtimer(lapic_cal_pm2 - lapic_cal_pm1, &delta, &deltatsc); lapic_clockevent.mult = div_sc(delta, TICK_NSEC * LAPIC_CAL_LOOPS, lapic_clockevent.shift); lapic_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); lapic_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &lapic_clockevent); calibration_result = (delta * APIC_DIVISOR) / LAPIC_CAL_LOOPS; apic_printk(APIC_VERBOSE, "..... delta %ld\n", delta); apic_printk(APIC_VERBOSE, "..... mult: %ld\n", lapic_clockevent.mult); apic_printk(APIC_VERBOSE, "..... calibration result: %u\n", calibration_result); if (cpu_has_tsc) { apic_printk(APIC_VERBOSE, "..... CPU clock speed is " "%ld.%04ld MHz.\n", (deltatsc / LAPIC_CAL_LOOPS) / (1000000 / HZ), (deltatsc / LAPIC_CAL_LOOPS) % (1000000 / HZ)); } apic_printk(APIC_VERBOSE, "..... host bus clock speed is " "%u.%04u MHz.\n", calibration_result / (1000000 / HZ), calibration_result % (1000000 / HZ)); if (calibration_result < (1000000 / HZ)) { local_irq_enable(); pr_warning("APIC frequency too slow, disabling apic timer\n"); return -1; } levt->features &= ~CLOCK_EVT_FEAT_DUMMY; if (!pm_referenced) { apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); levt->event_handler = lapic_cal_handler; lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); lapic_cal_loops = -1; local_irq_enable(); while (lapic_cal_loops <= LAPIC_CAL_LOOPS) cpu_relax(); lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); deltaj = lapic_cal_j2 - lapic_cal_j1; apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2) apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); else levt->features |= CLOCK_EVT_FEAT_DUMMY; } else local_irq_enable(); if (levt->features & CLOCK_EVT_FEAT_DUMMY) { pr_warning("APIC timer disabled due to verification failure\n"); return -1; } return 0; }
/* * Setup the boot APIC * * Calibrate and verify the result. */ void __init setup_boot_APIC_clock(void) { struct clock_event_device *levt = &__get_cpu_var(lapic_events); const long pm_100ms = PMTMR_TICKS_PER_SEC/10; const long pm_thresh = pm_100ms/100; void (*real_handler)(struct clock_event_device *dev); unsigned long deltaj; long delta, deltapm; int pm_referenced = 0; apic_printk(APIC_VERBOSE, "Using local APIC timer interrupts.\n" "calibrating APIC timer ...\n"); local_irq_disable(); /* Replace the global interrupt handler */ real_handler = global_clock_event->event_handler; global_clock_event->event_handler = lapic_cal_handler; /* * Setup the APIC counter to 1e9. There is no way the lapic * can underflow in the 100ms detection time frame */ __setup_APIC_LVTT(1000000000, 0, 0); /* Let the interrupts run */ local_irq_enable(); while (lapic_cal_loops <= LAPIC_CAL_LOOPS) cpu_relax(); local_irq_disable(); /* Restore the real event handler */ global_clock_event->event_handler = real_handler; /* Build delta t1-t2 as apic timer counts down */ delta = lapic_cal_t1 - lapic_cal_t2; apic_printk(APIC_VERBOSE, "... lapic delta = %ld\n", delta); /* Check, if the PM timer is available */ deltapm = lapic_cal_pm2 - lapic_cal_pm1; apic_printk(APIC_VERBOSE, "... PM timer delta = %ld\n", deltapm); if (deltapm) { unsigned long mult; u64 res; mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC, 22); if (deltapm > (pm_100ms - pm_thresh) && deltapm < (pm_100ms + pm_thresh)) { apic_printk(APIC_VERBOSE, "... PM timer result ok\n"); } else { res = (((u64) deltapm) * mult) >> 22; do_div(res, 1000000); printk(KERN_WARNING "APIC calibration not consistent " "with PM Timer: %ldms instead of 100ms\n", (long)res); /* Correct the lapic counter value */ res = (((u64) delta ) * pm_100ms); do_div(res, deltapm); printk(KERN_INFO "APIC delta adjusted to PM-Timer: " "%lu (%ld)\n", (unsigned long) res, delta); delta = (long) res; } pm_referenced = 1; } /* Calculate the scaled math multiplication factor */ lapic_clockevent.mult = div_sc(delta, TICK_NSEC * LAPIC_CAL_LOOPS, 32); lapic_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFF, &lapic_clockevent); lapic_clockevent.min_delta_ns = clockevent_delta2ns(0xF, &lapic_clockevent); calibration_result = (delta * APIC_DIVISOR) / LAPIC_CAL_LOOPS; apic_printk(APIC_VERBOSE, "..... delta %ld\n", delta); apic_printk(APIC_VERBOSE, "..... mult: %ld\n", lapic_clockevent.mult); apic_printk(APIC_VERBOSE, "..... calibration result: %u\n", calibration_result); if (cpu_has_tsc) { delta = (long)(lapic_cal_tsc2 - lapic_cal_tsc1); apic_printk(APIC_VERBOSE, "..... CPU clock speed is " "%ld.%04ld MHz.\n", (delta / LAPIC_CAL_LOOPS) / (1000000 / HZ), (delta / LAPIC_CAL_LOOPS) % (1000000 / HZ)); } apic_printk(APIC_VERBOSE, "..... host bus clock speed is " "%u.%04u MHz.\n", calibration_result / (1000000 / HZ), calibration_result % (1000000 / HZ)); local_apic_timer_verify_ok = 1; /* We trust the pm timer based calibration */ if (!pm_referenced) { apic_printk(APIC_VERBOSE, "... verify APIC timer\n"); /* * Setup the apic timer manually */ levt->event_handler = lapic_cal_handler; lapic_timer_setup(CLOCK_EVT_MODE_PERIODIC, levt); lapic_cal_loops = -1; /* Let the interrupts run */ local_irq_enable(); while(lapic_cal_loops <= LAPIC_CAL_LOOPS) cpu_relax(); local_irq_disable(); /* Stop the lapic timer */ lapic_timer_setup(CLOCK_EVT_MODE_SHUTDOWN, levt); local_irq_enable(); /* Jiffies delta */ deltaj = lapic_cal_j2 - lapic_cal_j1; apic_printk(APIC_VERBOSE, "... jiffies delta = %lu\n", deltaj); /* Check, if the jiffies result is consistent */ if (deltaj >= LAPIC_CAL_LOOPS-2 && deltaj <= LAPIC_CAL_LOOPS+2) apic_printk(APIC_VERBOSE, "... jiffies result ok\n"); else local_apic_timer_verify_ok = 0; } if (!local_apic_timer_verify_ok) { printk(KERN_WARNING "APIC timer disabled due to verification failure.\n"); /* No broadcast on UP ! */ if (num_possible_cpus() == 1) return; } else { /* * 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; } /* Setup the lapic or request the broadcast */ setup_APIC_timer(); }