Esempio n. 1
0
/*
 * 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);
}
Esempio n. 2
0
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);
}
Esempio n. 3
0
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;
}
Esempio n. 4
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();
}