enable_mtrr_cleanup = 1;
return 0;
}
------------------ -early_param("enble_mtrr_cleanup", enable_mtrr_cleanup_setup);
++++++++++++++++++ +early_param("enable_mtrr_cleanup", enable_mtrr_cleanup_setup);
+++++++++++ ++++++ +
++++++++++++ ++++++++static int __init mtrr_cleanup_debug_setup(char *str)
++++++++++++ ++++++++{
++++++++++++ ++++++++ debug_print = 1;
++++++++++++ ++++++++ return 0;
++++++++++++ ++++++++}
++++++++++++ ++++++++early_param("mtrr_cleanup_debug", mtrr_cleanup_debug_setup);
+ +
struct var_mtrr_state {
unsigned long range_startk;
unsigned long range_sizek;
(range0_basek + range0_sizek)<<10);
state->reg = range_to_mtrr(state->reg, range0_basek,
range0_sizek, MTRR_TYPE_WRBACK);
- --- - -- - ----
- --- - -- - ---- }
- --- - -- - ----
- --- - -- - ---- range_basek = range0_basek + range0_sizek;
- --- - -- - ---- range_sizek = chunk_sizek;
------------ --------
- --- - -- - ---- if (range_basek + range_sizek > basek &&
- --- - -- - ---- range_basek + range_sizek <= (basek + sizek)) {
- --- - -- - ---- /* one hole */
- --- - -- - ---- second_basek = basek;
- --- - -- - ---- second_sizek = range_basek + range_sizek - basek;
}
--- - - -- - range_basek = range0_basek + range0_sizek;
--- - - -- - range_sizek = chunk_sizek;
--- - - -- -
--- - - -- - if (range_basek + range_sizek > basek &&
--- - - -- - range_basek + range_sizek <= (basek + sizek)) {
--- - - -- - /* one hole */
--- - - -- - second_basek = basek;
--- - - -- - second_sizek = range_basek + range_sizek - basek;
--- - - -- - }
--- - - -- -
------------ -------- /* if last piece, only could one hole near end */
------------ -------- if ((second_basek || !basek) &&
------------ -------- range_sizek - (state->range_sizek - range0_sizek) - second_sizek <
------------ -------- (chunk_sizek >> 1)) {
------------ -------- /*
------------ -------- * one hole in middle (second_sizek is 0) or at end
------------ -------- * (second_sizek is 0 )
------------ -------- */
------------ -------- hole_sizek = range_sizek - (state->range_sizek - range0_sizek)
------------ -------- - second_sizek;
------------ -------- hole_basek = range_basek + range_sizek - hole_sizek
------------ -------- - second_sizek;
------------ -------- } else {
------------ -------- /* fallback for big hole, or several holes */
++++++++++++ ++++++++ if (range0_sizek < state->range_sizek) {
++++++++++++ ++++++++ /* need to handle left over */
range_sizek = state->range_sizek - range0_sizek;
------------ -------- second_basek = 0;
------------ -------- second_sizek = 0;
++++++++++++ ++++++++
++++++++++++ ++++++++ if (debug_print)
++++++++++++ ++++++++ printk(KERN_DEBUG "range: %016lx - %016lx\n",
++++++++++++ ++++++++ range_basek<<10,
++++++++++++ ++++++++ (range_basek + range_sizek)<<10);
++++++++++++ ++++++++ state->reg = range_to_mtrr(state->reg, range_basek,
++++++++++++ ++++++++ range_sizek, MTRR_TYPE_WRBACK);
}
------------ -------- if (debug_print)
------------ -------- printk(KERN_DEBUG "range: %016lx - %016lx\n", range_basek<<10,
------------ -------- (range_basek + range_sizek)<<10);
------------ -------- state->reg = range_to_mtrr(state->reg, range_basek, range_sizek,
------------ -------- MTRR_TYPE_WRBACK);
if (hole_sizek) {
++++++++++++ ++++++++ hole_basek = range_basek - hole_sizek - second_sizek;
if (debug_print)
printk(KERN_DEBUG "hole: %016lx - %016lx\n",
------------ -------- hole_basek<<10, (hole_basek + hole_sizek)<<10);
------------ -------- state->reg = range_to_mtrr(state->reg, hole_basek, hole_sizek,
------------ -------- MTRR_TYPE_UNCACHABLE);
------------ --------
++++++++++++ ++++++++ hole_basek<<10,
++++++++++++ ++++++++ (hole_basek + hole_sizek)<<10);
++++++++++++ ++++++++ state->reg = range_to_mtrr(state->reg, hole_basek,
++++++++++++ ++++++++ hole_sizek, MTRR_TYPE_UNCACHABLE);
}
return second_sizek;
num_var_ranges - num[MTRR_NUM_TYPES])
return 0;
++++++++++++ ++++++++ /* print original var MTRRs at first, for debugging: */
++++++++++++ ++++++++ printk(KERN_DEBUG "original variable MTRRs\n");
++++++++++++ ++++++++ for (i = 0; i < num_var_ranges; i++) {
++++++++++++ ++++++++ char start_factor = 'K', size_factor = 'K';
++++++++++++ ++++++++ unsigned long start_base, size_base;
++++++++++++ ++++++++
++++++++++++ ++++++++ size_base = range_state[i].size_pfn << (PAGE_SHIFT - 10);
++++++++++++ ++++++++ if (!size_base)
++++++++++++ ++++++++ continue;
++++++++++++ ++++++++
++++++++++++ ++++++++ size_base = to_size_factor(size_base, &size_factor),
++++++++++++ ++++++++ start_base = range_state[i].base_pfn << (PAGE_SHIFT - 10);
++++++++++++ ++++++++ start_base = to_size_factor(start_base, &start_factor),
++++++++++++ ++++++++ type = range_state[i].type;
++++++++++++ ++++++++
++++++++++++ ++++++++ printk(KERN_DEBUG "reg %d, base: %ld%cB, range: %ld%cB, type %s\n",
++++++++++++ ++++++++ i, start_base, start_factor,
++++++++++++ ++++++++ size_base, size_factor,
++++++++++++ ++++++++ (type == MTRR_TYPE_UNCACHABLE) ? "UC" :
++++++++++++ ++++++++ ((type == MTRR_TYPE_WRPROT) ? "WP" :
++++++++++++ ++++++++ ((type == MTRR_TYPE_WRBACK) ? "WB" : "Other"))
++++++++++++ ++++++++ );
++++++++++++ ++++++++ }
++++++++++++ ++++++++
memset(range, 0, sizeof(range));
extra_remove_size = 0;
-- ------------------ if (mtrr_tom2) {
-- ------------------ extra_remove_base = 1 << (32 - PAGE_SHIFT);
++ ++++++++++++++++++ extra_remove_base = 1 << (32 - PAGE_SHIFT);
++ ++++++++++++++++++ if (mtrr_tom2)
extra_remove_size =
(mtrr_tom2 >> PAGE_SHIFT) - extra_remove_base;
-- ------------------ }
nr_range = x86_get_mtrr_mem_range(range, 0, extra_remove_base,
extra_remove_size);
++++++++++++ ++++++++ /*
++++++++++++ ++++++++ * [0, 1M) should always be coverred by var mtrr with WB
++++++++++++ ++++++++ * and fixed mtrrs should take effective before var mtrr for it
++++++++++++ ++++++++ */
++++++++++++ ++++++++ nr_range = add_range_with_merge(range, nr_range, 0,
++++++++++++ ++++++++ (1ULL<<(20 - PAGE_SHIFT)) - 1);
++++++++++++ ++++++++ /* sort the ranges */
++++++++++++ ++++++++ sort(range, nr_range, sizeof(struct res_range), cmp_range, NULL);
++++++++++++ ++++++++
range_sums = sum_ranges(range, nr_range);
printk(KERN_INFO "total RAM coverred: %ldM\n",
range_sums >> (20 - PAGE_SHIFT));
/*
* Read TSC and the reference counters. Take care of SMI disturbance
*/
- - ----- ----- -- --static u64 tsc_read_refs(u64 *pm, u64 *hpet)
- - - - static u64 __init tsc_read_refs(u64 *pm, u64 *hpet)
++++++++++++++++++ ++static u64 tsc_read_refs(u64 *p, int hpet)
{
u64 t1, t2;
int i;
return ULLONG_MAX;
}
- - - -- --- -/**
- - - -- --- - * native_calibrate_tsc - calibrate the tsc on boot
++++++++++++++++++ ++/*
++++++++++++++++++ ++ * Calculate the TSC frequency from HPET reference
+ + +++ + +++ + + */
- - - -- --- -unsigned long native_calibrate_tsc(void)
++++++++++++++++++ ++static unsigned long calc_hpet_ref(u64 deltatsc, u64 hpet1, u64 hpet2)
+ + +++ + +++ + + {
- - - -- --- - unsigned long flags;
- - - -- --- - u64 tsc1, tsc2, tr1, tr2, delta, pm1, pm2, hpet1, hpet2;
- - - -- --- - int hpet = is_hpet_enabled();
- - - -- --- - unsigned int tsc_khz_val = 0;
++++++++++++++++++ ++ u64 tmp;
+ + +++ + +++ + +
- - - -- --- - local_irq_save(flags);
++++++++++++++++++ ++ if (hpet2 < hpet1)
++++++++++++++++++ ++ hpet2 += 0x100000000ULL;
++++++++++++++++++ ++ hpet2 -= hpet1;
++++++++++++++++++ ++ tmp = ((u64)hpet2 * hpet_readl(HPET_PERIOD));
++++++++++++++++++ ++ do_div(tmp, 1000000);
++++++++++++++++++ ++ do_div(deltatsc, tmp);
++++++++++++++++++ ++
++++++++++++++++++ ++ return (unsigned long) deltatsc;
++++++++++++++++++ ++}
++++++++++++++++++ ++
++++++++++++++++++ ++/*
++++++++++++++++++ ++ * Calculate the TSC frequency from PMTimer reference
++++++++++++++++++ ++ */
++++++++++++++++++ ++static unsigned long calc_pmtimer_ref(u64 deltatsc, u64 pm1, u64 pm2)
++++++++++++++++++ ++{
++++++++++++++++++ ++ u64 tmp;
+ + +++ + ++++ + +
- - - - --- - tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL);
++++++++++++++++++ ++ if (!pm1 && !pm2)
++++++++++++++++++ ++ return ULONG_MAX;
++++++++++++++++++ ++
++++++++++++++++++ ++ if (pm2 < pm1)
++++++++++++++++++ ++ pm2 += (u64)ACPI_PM_OVRRUN;
++++++++++++++++++ ++ pm2 -= pm1;
++++++++++++++++++ ++ tmp = pm2 * 1000000000LL;
++++++++++++++++++ ++ do_div(tmp, PMTMR_TICKS_PER_SEC);
++++++++++++++++++ ++ do_div(deltatsc, tmp);
++++++++++++++++++ ++
++++++++++++++++++ ++ return (unsigned long) deltatsc;
++++++++++++++++++ ++}
++++++++++++++++++ ++
++++++++++++++++++ ++#define CAL_MS 10
++++++++++++++++++ ++#define CAL_LATCH (CLOCK_TICK_RATE / (1000 / CAL_MS))
++++++++++++++++++ ++#define CAL_PIT_LOOPS 1000
++++++++++++++++++ ++
++++++++++++++++++ ++#define CAL2_MS 50
++++++++++++++++++ ++#define CAL2_LATCH (CLOCK_TICK_RATE / (1000 / CAL2_MS))
++++++++++++++++++ ++#define CAL2_PIT_LOOPS 5000
++++++++++++++++++ ++
++++++++++++++++++ ++
+ + + ++ +++ +/*
+ + + ++ +++ + * Try to calibrate the TSC against the Programmable
+ + + ++ +++ + * Interrupt Timer and return the frequency of the TSC
+ + + ++ +++ + * in kHz.
+ + + ++ +++ + *
+ + + ++ +++ + * Return ULONG_MAX on failure to calibrate.
+ + + ++ +++ + */
- - --- - --- - - static unsigned long pit_calibrate_tsc(void)
++++++++++++++++++ ++static unsigned long pit_calibrate_tsc(u32 latch, unsigned long ms, int loopmin)
+ + + ++ +++ +{
+ + + ++ +++ + u64 tsc, t1, t2, delta;
+ + + ++ +++ + unsigned long tscmin, tscmax;
+ + + ++ +++ + int pitcnt;
+
+ + + ++ +++ + /* Set the Gate high, disable speaker */
+ outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+
+ + + ++ +++ + /*
+ + + ++ +++ + * Setup CTC channel 2* for mode 0, (interrupt on terminal
+ + + ++ +++ + * count mode), binary count. Set the latch register to 50ms
+ + + ++ +++ + * (LSB then MSB) to begin countdown.
+ + + ++ +++ + */
+ outb(0xb0, 0x43);
---------- ------- -- outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
---------- ------- -- outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42);
- - - - --- - tr1 = get_cycles();
- - - - --- - while ((inb(0x61) & 0x20) == 0);
- - - - --- - tr2 = get_cycles();
++++++++++++++++++ ++ outb(latch & 0xff, 0x42);
++++++++++++++++++ ++ outb(latch >> 8, 0x42);
+
- - - - --- - tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL);
+ + + ++ +++ + tsc = t1 = t2 = get_cycles();
+ + + ++ +++ +
+ + + ++ +++ + pitcnt = 0;
+ + + ++ +++ + tscmax = 0;
+ + + ++ +++ + tscmin = ULONG_MAX;
+ + + ++ +++ + while ((inb(0x61) & 0x20) == 0) {
+ + + ++ +++ + t2 = get_cycles();
+ + + ++ +++ + delta = t2 - tsc;
+ + + ++ +++ + tsc = t2;
+ + + ++ +++ + if ((unsigned long) delta < tscmin)
+ + + ++ +++ + tscmin = (unsigned int) delta;
+ + + ++ +++ + if ((unsigned long) delta > tscmax)
+ + + ++ +++ + tscmax = (unsigned int) delta;
+ + + ++ +++ + pitcnt++;
+ + + ++ +++ + }
+ + + ++ +++ +
+ + + ++ +++ + /*
+ + + ++ +++ + * Sanity checks:
+ + + ++ +++ + *
- - --- - --- - - * If we were not able to read the PIT more than 5000
++++++++++++++++++ ++ * If we were not able to read the PIT more than loopmin
+ + + ++ +++ + * times, then we have been hit by a massive SMI
+ + + ++ +++ + *
+ + + ++ +++ + * If the maximum is 10 times larger than the minimum,
+ + + ++ +++ + * then we got hit by an SMI as well.
+ + + ++ +++ + */
- - --- - --- - - if (pitcnt < 5000 || tscmax > 10 * tscmin)
++++++++++++++++++ ++ if (pitcnt < loopmin || tscmax > 10 * tscmin)
+ + + ++ +++ + return ULONG_MAX;
+ + + ++ +++ +
+ + + ++ +++ + /* Calculate the PIT value */
+ + + ++ +++ + delta = t2 - t1;
- - --- - --- - - do_div(delta, 50);
++++++++++++++++++ ++ do_div(delta, ms);
+ + + ++ +++ + return delta;
+ + + ++ +++ +}
+
++++++++++++++++++ ++/*
++++++++++++++++++ ++ * This reads the current MSB of the PIT counter, and
++++++++++++++++++ ++ * checks if we are running on sufficiently fast and
++++++++++++++++++ ++ * non-virtualized hardware.
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * Our expectations are:
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * - the PIT is running at roughly 1.19MHz
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * - each IO is going to take about 1us on real hardware,
++++++++++++++++++ ++ * but we allow it to be much faster (by a factor of 10) or
++++++++++++++++++ ++ * _slightly_ slower (ie we allow up to a 2us read+counter
++++++++++++++++++ ++ * update - anything else implies a unacceptably slow CPU
++++++++++++++++++ ++ * or PIT for the fast calibration to work.
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * - with 256 PIT ticks to read the value, we have 214us to
++++++++++++++++++ ++ * see the same MSB (and overhead like doing a single TSC
++++++++++++++++++ ++ * read per MSB value etc).
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * - We're doing 2 reads per loop (LSB, MSB), and we expect
++++++++++++++++++ ++ * them each to take about a microsecond on real hardware.
++++++++++++++++++ ++ * So we expect a count value of around 100. But we'll be
++++++++++++++++++ ++ * generous, and accept anything over 50.
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * - if the PIT is stuck, and we see *many* more reads, we
++++++++++++++++++ ++ * return early (and the next caller of pit_expect_msb()
++++++++++++++++++ ++ * then consider it a failure when they don't see the
++++++++++++++++++ ++ * next expected value).
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * These expectations mean that we know that we have seen the
++++++++++++++++++ ++ * transition from one expected value to another with a fairly
++++++++++++++++++ ++ * high accuracy, and we didn't miss any events. We can thus
++++++++++++++++++ ++ * use the TSC value at the transitions to calculate a pretty
++++++++++++++++++ ++ * good value for the TSC frequencty.
++++++++++++++++++ ++ */
++++++++++++++++++ ++static inline int pit_expect_msb(unsigned char val)
++++++++++++++++++ ++{
++++++++++++++++++ ++ int count = 0;
++++++++++ +++++++ ++
- tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL);
++++++++++++++++++ ++ for (count = 0; count < 50000; count++) {
++++++++++++++++++ ++ /* Ignore LSB */
++++++++++++++++++ ++ inb(0x42);
++++++++++++++++++ ++ if (inb(0x42) != val)
++++++++++++++++++ ++ break;
++++++++++++++++++ ++ }
++++++++++++++++++ ++ return count > 50;
++++++++++++++++++ ++}
++++++++++++++++++ ++
++++++++++++++++++ ++/*
++++++++++++++++++ ++ * How many MSB values do we want to see? We aim for a
++++++++++++++++++ ++ * 15ms calibration, which assuming a 2us counter read
++++++++++++++++++ ++ * error should give us roughly 150 ppm precision for
++++++++++++++++++ ++ * the calibration.
++++++++++++++++++ ++ */
++++++++++++++++++ ++#define QUICK_PIT_MS 15
++++++++++++++++++ ++#define QUICK_PIT_ITERATIONS (QUICK_PIT_MS * PIT_TICK_RATE / 1000 / 256)
++++++++++ +++++++ ++
++++++++++++++++++ ++static unsigned long quick_pit_calibrate(void)
++++++++++++++++++ ++{
++++++++++++++++++ ++ /* Set the Gate high, disable speaker */
++++++++++ +++++++ ++ outb((inb(0x61) & ~0x02) | 0x01, 0x61);
++++++++++ +++++++ ++
++++++++++++++++++ ++ /*
++++++++++++++++++ ++ * Counter 2, mode 0 (one-shot), binary count
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * NOTE! Mode 2 decrements by two (and then the
++++++++++++++++++ ++ * output is flipped each time, giving the same
++++++++++++++++++ ++ * final output frequency as a decrement-by-one),
++++++++++++++++++ ++ * so mode 0 is much better when looking at the
++++++++++++++++++ ++ * individual counts.
++++++++++++++++++ ++ */
++++++++++ +++++++ ++ outb(0xb0, 0x43);
- outb((CLOCK_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
- outb((CLOCK_TICK_RATE / (1000 / 50)) >> 8, 0x42);
- tr1 = get_cycles();
- while ((inb(0x61) & 0x20) == 0);
- tr2 = get_cycles();
++++++++++ +++++++ ++
- tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL);
++++++++++++++++++ ++ /* Start at 0xffff */
++++++++++++++++++ ++ outb(0xff, 0x42);
++++++++++++++++++ ++ outb(0xff, 0x42);
++++++++++++++++++ ++
++++++++++++++++++ ++ if (pit_expect_msb(0xff)) {
++++++++++++++++++ ++ int i;
++++++++++++++++++ ++ u64 t1, t2, delta;
++++++++++++++++++ ++ unsigned char expect = 0xfe;
++++++++++++++++++ ++
++++++++++++++++++ ++ t1 = get_cycles();
++++++++++++++++++ ++ for (i = 0; i < QUICK_PIT_ITERATIONS; i++, expect--) {
++++++++++++++++++ ++ if (!pit_expect_msb(expect))
++++++++++++++++++ ++ goto failed;
++++++++++++++++++ ++ }
++++++++++++++++++ ++ t2 = get_cycles();
++++++++++++++++++ ++
++++++++++++++++++ ++ /*
++++++++++++++++++ ++ * Make sure we can rely on the second TSC timestamp:
++++++++++++++++++ ++ */
++++++++++++++++++ ++ if (!pit_expect_msb(expect))
++++++++++++++++++ ++ goto failed;
++++++++++++++++++ ++
++++++++++++++++++ ++ /*
++++++++++++++++++ ++ * Ok, if we get here, then we've seen the
++++++++++++++++++ ++ * MSB of the PIT decrement QUICK_PIT_ITERATIONS
++++++++++++++++++ ++ * times, and each MSB had many hits, so we never
++++++++++++++++++ ++ * had any sudden jumps.
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * As a result, we can depend on there not being
++++++++++++++++++ ++ * any odd delays anywhere, and the TSC reads are
++++++++++++++++++ ++ * reliable.
++++++++++++++++++ ++ *
++++++++++++++++++ ++ * kHz = ticks / time-in-seconds / 1000;
++++++++++++++++++ ++ * kHz = (t2 - t1) / (QPI * 256 / PIT_TICK_RATE) / 1000
++++++++++++++++++ ++ * kHz = ((t2 - t1) * PIT_TICK_RATE) / (QPI * 256 * 1000)
++++++++++++++++++ ++ */
++++++++++++++++++ ++ delta = (t2 - t1)*PIT_TICK_RATE;
++++++++++++++++++ ++ do_div(delta, QUICK_PIT_ITERATIONS*256*1000);
++++++++++++++++++ ++ printk("Fast TSC calibration using PIT\n");
++++++++++++++++++ ++ return delta;
++++++++++++++++++ ++ }
++++++++++++++++++ ++failed:
++++++++++++++++++ ++ return 0;
++++++++++++++++++ ++}
+ + + + +++ +
+ + + ++ +++ +/**
+ + + ++ +++ + * native_calibrate_tsc - calibrate the tsc on boot
+ + + ++ +++ + */
+ + + ++ +++ +unsigned long native_calibrate_tsc(void)
+ + + ++ +++ +{
- - --- - --- - - u64 tsc1, tsc2, delta, pm1, pm2, hpet1, hpet2;
++++++++++++++++++ ++ u64 tsc1, tsc2, delta, ref1, ref2;
+ + + ++ +++ + unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
- - --- - --- - - unsigned long flags;
- - --- - --- - - int hpet = is_hpet_enabled(), i;
++++++++++++++++++ ++ unsigned long flags, latch, ms, fast_calibrate;
++++++++++++++++++ ++ int hpet = is_hpet_enabled(), i, loopmin;
++++++++++++++++++ ++
++++++++++++++++++ ++ local_irq_save(flags);
++++++++++++++++++ ++ fast_calibrate = quick_pit_calibrate();
+ + +++ + +++ + + local_irq_restore(flags);
++++++++++++++++++ ++ if (fast_calibrate)
++++++++++++++++++ ++ return fast_calibrate;
/*
- - - -- --- - * Preset the result with the raw and inaccurate PIT
- - - -- --- - * calibration value
+ + + ++ +++ + * Run 5 calibration loops to get the lowest frequency value
+ + + ++ +++ + * (the best estimate). We use two different calibration modes
+ + + ++ +++ + * here:
+ + + ++ +++ + *
+ + + ++ +++ + * 1) PIT loop. We set the PIT Channel 2 to oneshot mode and
+ + + ++ +++ + * load a timeout of 50ms. We read the time right after we
+ + + ++ +++ + * started the timer and wait until the PIT count down reaches
+ + + ++ +++ + * zero. In each wait loop iteration we read the TSC and check
+ + + ++ +++ + * the delta to the previous read. We keep track of the min
+ + + ++ +++ + * and max values of that delta. The delta is mostly defined
+ + + ++ +++ + * by the IO time of the PIT access, so we can detect when a
+ + + ++ +++ + * SMI/SMM disturbance happend between the two reads. If the
+ + + ++ +++ + * maximum time is significantly larger than the minimum time,
+ + + ++ +++ + * then we discard the result and have another try.
+ + + ++ +++ + *
+ + + ++ +++ + * 2) Reference counter. If available we use the HPET or the
+ + + ++ +++ + * PMTIMER as a reference to check the sanity of that value.
+ + + ++ +++ + * We use separate TSC readouts and check inside of the
+ + + ++ +++ + * reference read for a SMI/SMM disturbance. We dicard
+ + + ++ +++ + * disturbed values here as well. We do that around the PIT
+ + + ++ +++ + * calibration delay loop as we have to wait for a certain
+ + + ++ +++ + * amount of time anyway.
*/
- - --- - --- - - for (i = 0; i < 5; i++) {
- - - -- --- - delta = (tr2 - tr1);
- - - -- --- - do_div(delta, 50);
- - - -- --- - tsc_khz_val = delta;
- - - -- --- -
- - - -- --- - /* hpet or pmtimer available ? */
- - - -- --- - if (!hpet && !pm1 && !pm2) {
- - - -- --- - printk(KERN_INFO "TSC calibrated against PIT\n");
- - - -- --- - goto out;
++++++++++++++++++ ++
++++++++++++++++++ ++ /* Preset PIT loop values */
++++++++++++++++++ ++ latch = CAL_LATCH;
++++++++++++++++++ ++ ms = CAL_MS;
++++++++++++++++++ ++ loopmin = CAL_PIT_LOOPS;
++++++++++++++++++ ++
++++++++++++++++++ ++ for (i = 0; i < 3; i++) {
+ + + ++ +++ + unsigned long tsc_pit_khz;
+ + + ++ +++ +
+ + + ++ +++ + /*
+ + + ++ +++ + * Read the start value and the reference count of
+ + + ++ +++ + * hpet/pmtimer when available. Then do the PIT
+ + + ++ +++ + * calibration, which will take at least 50ms, and
+ + + ++ +++ + * read the end value.
+ + + ++ +++ + */
+ + + ++ +++ + local_irq_save(flags);
- - --- - --- - - tsc1 = tsc_read_refs(&pm1, hpet ? &hpet1 : NULL);
- - --- - --- - - tsc_pit_khz = pit_calibrate_tsc();
- - --- - --- - - tsc2 = tsc_read_refs(&pm2, hpet ? &hpet2 : NULL);
++++++++++++++++++ ++ tsc1 = tsc_read_refs(&ref1, hpet);
++++++++++++++++++ ++ tsc_pit_khz = pit_calibrate_tsc(latch, ms, loopmin);
++++++++++++++++++ ++ tsc2 = tsc_read_refs(&ref2, hpet);
+ + + ++ +++ + local_irq_restore(flags);
+ + + ++ +++ +
+ + + ++ +++ + /* Pick the lowest PIT TSC calibration so far */
+ + + ++ +++ + tsc_pit_min = min(tsc_pit_min, tsc_pit_khz);
+ + + ++ +++ +
+ + + ++ +++ + /* hpet or pmtimer available ? */
- - --- - --- - - if (!hpet && !pm1 && !pm2)
++++++++++++++++++ ++ if (!hpet && !ref1 && !ref2)
+ + + ++ +++ + continue;
+ + + ++ +++ +
+ + + ++ +++ + /* Check, whether the sampling was disturbed by an SMI */
+ + + ++ +++ + if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX)
+ + + ++ +++ + continue;
+ + + ++ +++ +
+ + + ++ +++ + tsc2 = (tsc2 - tsc1) * 1000000LL;
++++++++++++++++++ ++ if (hpet)
++++++++++++++++++ ++ tsc2 = calc_hpet_ref(tsc2, ref1, ref2);
++++++++++++++++++ ++ else
++++++++++++++++++ ++ tsc2 = calc_pmtimer_ref(tsc2, ref1, ref2);
+ + + ++ +++ +
- - --- - --- - - if (hpet) {
- - --- - --- - - if (hpet2 < hpet1)
- - --- - --- - - hpet2 += 0x100000000ULL;
- - --- - --- - - hpet2 -= hpet1;
- - --- - --- - - tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD));
- - --- - --- - - do_div(tsc1, 1000000);
- - --- - --- - - } else {
- - --- - --- - - if (pm2 < pm1)
- - --- - --- - - pm2 += (u64)ACPI_PM_OVRRUN;
- - --- - --- - - pm2 -= pm1;
- - --- - --- - - tsc1 = pm2 * 1000000000LL;
- - --- - --- - - do_div(tsc1, PMTMR_TICKS_PER_SEC);
++++++++++++++++++ ++ tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2);
++++++++++++++++++ ++
++++++++++++++++++ ++ /* Check the reference deviation */
++++++++++++++++++ ++ delta = ((u64) tsc_pit_min) * 100;
++++++++++++++++++ ++ do_div(delta, tsc_ref_min);
++++++++++++++++++ ++
++++++++++++++++++ ++ /*
++++++++++++++++++ ++ * If both calibration results are inside a 10% window
++++++++++++++++++ ++ * then we can be sure, that the calibration
++++++++++++++++++ ++ * succeeded. We break out of the loop right away. We
++++++++++++++++++ ++ * use the reference value, as it is more precise.
++++++++++++++++++ ++ */
++++++++++++++++++ ++ if (delta >= 90 && delta <= 110) {
++++++++++++++++++ ++ printk(KERN_INFO
++++++++++++++++++ ++ "TSC: PIT calibration matches %s. %d loops\n",
++++++++++++++++++ ++ hpet ? "HPET" : "PMTIMER", i + 1);
++++++++++++++++++ ++ return tsc_ref_min;
+ + + ++ +++ + }
+ + + ++ +++ +
- - --- - --- - - do_div(tsc2, tsc1);
- - --- - --- - - tsc_ref_min = min(tsc_ref_min, (unsigned long) tsc2);
++++++++++++++++++ ++ /*
++++++++++++++++++ ++ * Check whether PIT failed more than once. This
++++++++++++++++++ ++ * happens in virtualized environments. We need to
++++++++++++++++++ ++ * give the virtual PC a slightly longer timeframe for
++++++++++++++++++ ++ * the HPET/PMTIMER to make the result precise.
++++++++++++++++++ ++ */
++++++++++++++++++ ++ if (i == 1 && tsc_pit_min == ULONG_MAX) {
++++++++++++++++++ ++ latch = CAL2_LATCH;
++++++++++++++++++ ++ ms = CAL2_MS;
++++++++++++++++++ ++ loopmin = CAL2_PIT_LOOPS;
++++++++++++++++++ ++ }
}
- - - -- --- - /* Check, whether the sampling was disturbed by an SMI */
- - - -- --- - if (tsc1 == ULLONG_MAX || tsc2 == ULLONG_MAX) {
- - - -- --- - printk(KERN_WARNING "TSC calibration disturbed by SMI, "
- - - -- --- - "using PIT calibration result\n");
- - - -- --- - goto out;
+ + + ++ +++ + /*
+ + + ++ +++ + * Now check the results.
+ + + ++ +++ + */
+ + + ++ +++ + if (tsc_pit_min == ULONG_MAX) {
+ + + ++ +++ + /* PIT gave no useful value */
- printk(KERN_WARNING "TSC: PIT calibration failed due to "
- "SMI disturbance.\n");
+ + + ++ +++ + + printk(KERN_WARNING "TSC: Unable to calibrate against PIT\n");
+ + + ++ +++ +
+ + + ++ +++ + /* We don't have an alternative source, disable TSC */
- - --- - --- - - if (!hpet && !pm1 && !pm2) {
++++++++++++++++++ ++ if (!hpet && !ref1 && !ref2) {
+ + + ++ +++ + printk("TSC: No reference (HPET/PMTIMER) available\n");
+ + + ++ +++ + return 0;
+ + + ++ +++ + }
+ + + ++ +++ +
+ + + ++ +++ + /* The alternative source failed as well, disable TSC */
+ + + ++ +++ + if (tsc_ref_min == ULONG_MAX) {
+ + + ++ +++ + printk(KERN_WARNING "TSC: HPET/PMTIMER calibration "
- - --- - --- - - "failed due to SMI disturbance.\n");
++++++++++++++++++ ++ "failed.\n");
+ + + ++ +++ + return 0;
+ + + ++ +++ + }
+ + + ++ +++ +
+ + + ++ +++ + /* Use the alternative source */
+ + + ++ +++ + printk(KERN_INFO "TSC: using %s reference calibration\n",
+ + + ++ +++ + hpet ? "HPET" : "PMTIMER");
+ + + ++ +++ +
+ + + ++ +++ + return tsc_ref_min;
}
- - - -- --- - tsc2 = (tsc2 - tsc1) * 1000000LL;
- - - -- --- -
- - - -- --- - if (hpet) {
- - - -- --- - printk(KERN_INFO "TSC calibrated against HPET\n");
- - - -- --- - if (hpet2 < hpet1)
- - - -- --- - hpet2 += 0x100000000ULL;
- - - -- --- - hpet2 -= hpet1;
- - - -- --- - tsc1 = ((u64)hpet2 * hpet_readl(HPET_PERIOD));
- - - -- --- - do_div(tsc1, 1000000);
- - - -- --- - } else {
- - - -- --- - printk(KERN_INFO "TSC calibrated against PM_TIMER\n");
- - - -- --- - if (pm2 < pm1)
- - - -- --- - pm2 += (u64)ACPI_PM_OVRRUN;
- - - -- --- - pm2 -= pm1;
- - - -- --- - tsc1 = pm2 * 1000000000LL;
- - - -- --- - do_div(tsc1, PMTMR_TICKS_PER_SEC);
+ + + ++ +++ + /* We don't have an alternative source, use the PIT calibration value */
- - --- - --- - - if (!hpet && !pm1 && !pm2) {
++++++++++++++++++ ++ if (!hpet && !ref1 && !ref2) {
+ + + ++ +++ + printk(KERN_INFO "TSC: Using PIT calibration value\n");
+ + + ++ +++ + return tsc_pit_min;
}
- - - -- --- - do_div(tsc2, tsc1);
- - - -- --- - tsc_khz_val = tsc2;
+ + + ++ +++ + /* The alternative source failed, use the PIT calibration value */
+ + + ++ +++ + if (tsc_ref_min == ULONG_MAX) {
- - --- - --- - - printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed due "
- - --- - --- - - "to SMI disturbance. Using PIT calibration\n");
++++++++++++++++++ ++ printk(KERN_WARNING "TSC: HPET/PMTIMER calibration failed. "
++++++++++++++++++ ++ "Using PIT calibration\n");
+ + + ++ +++ + return tsc_pit_min;
+ + + ++ +++ + }
- - --- - --- - - /* Check the reference deviation */
- - --- - --- - - delta = ((u64) tsc_pit_min) * 100;
- - --- - --- - - do_div(delta, tsc_ref_min);
- - --- - --- - -
- - --- - --- - - /*
- - --- - --- - - * If both calibration results are inside a 5% window, the we
- - --- - --- - - * use the lower frequency of those as it is probably the
- - --- - --- - - * closest estimate.
- - --- - --- - - */
- - --- - --- - - if (delta >= 95 && delta <= 105) {
- - --- - --- - - printk(KERN_INFO "TSC: PIT calibration confirmed by %s.\n",
- - --- - --- - - hpet ? "HPET" : "PMTIMER");
- - --- - --- - - printk(KERN_INFO "TSC: using %s calibration value\n",
- - --- - --- - - tsc_pit_min <= tsc_ref_min ? "PIT" :
- - --- - --- - - hpet ? "HPET" : "PMTIMER");
- - --- - --- - - return tsc_pit_min <= tsc_ref_min ? tsc_pit_min : tsc_ref_min;
- - --- - --- - - }
- - --- - --- - -
- - --- - --- - - printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n",
- - --- - --- - - hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min);
- - --- - --- - -
- - - -- --- -out:
- - - -- --- - return tsc_khz_val;
+ + + ++ +++ + /*
+ + + ++ +++ + * The calibration values differ too much. In doubt, we use
+ + + ++ +++ + * the PIT value as we know that there are PMTIMERs around
- - --- - --- - - * running at double speed.
++++++++++++++++++ ++ * running at double speed. At least we let the user know:
+ + + ++ +++ + */
++++++++++++++++++ ++ printk(KERN_WARNING "TSC: PIT calibration deviates from %s: %lu %lu.\n",
++++++++++++++++++ ++ hpet ? "HPET" : "PMTIMER", tsc_pit_min, tsc_ref_min);
+ + + ++ +++ + printk(KERN_INFO "TSC: Using PIT calibration value\n");
+ + + ++ +++ + return tsc_pit_min;
}
- - - -- --- -
#ifdef CONFIG_X86_32
/* Only called from the Powernow K7 cpu freq driver */
int recalibrate_cpu_khz(void)