121bcbe6eSJason King /*
221bcbe6eSJason King * This file and its contents are supplied under the terms of the
321bcbe6eSJason King * Common Development and Distribution License ("CDDL"), version 1.0.
421bcbe6eSJason King * You may only use this file in accordance with the terms of version
521bcbe6eSJason King * 1.0 of the CDDL.
621bcbe6eSJason King *
721bcbe6eSJason King * A full copy of the text of the CDDL should have accompanied this
821bcbe6eSJason King * source. A copy of the CDDL is also available via the Internet at
921bcbe6eSJason King * http://www.illumos.org/license/CDDL.
1021bcbe6eSJason King */
1121bcbe6eSJason King
1221bcbe6eSJason King /*
1321bcbe6eSJason King * Copyright 2020 Joyent, Inc.
1421bcbe6eSJason King */
1521bcbe6eSJason King
1621bcbe6eSJason King #include <sys/tsc.h>
1721bcbe6eSJason King #include <sys/prom_debug.h>
1821bcbe6eSJason King #include <sys/hpet.h>
1921bcbe6eSJason King #include <sys/clock.h>
2021bcbe6eSJason King
2121bcbe6eSJason King /*
2221bcbe6eSJason King * The amount of time (in microseconds) between tsc samples. This is
2321bcbe6eSJason King * somewhat arbitrary, but seems reasonable. A frequency of 1GHz is
2421bcbe6eSJason King * 1,000,000,000 ticks / sec (10^9). 100us is 10^(-6) * 10^2 => 10^(-4), so
2521bcbe6eSJason King * 100us would represent 10^5 (100,000) ticks.
2621bcbe6eSJason King */
2721bcbe6eSJason King #define HPET_SAMPLE_INTERVAL_US (100)
2821bcbe6eSJason King
2921bcbe6eSJason King /*
3021bcbe6eSJason King * The same as above, but in nanoseconds (for ease in converting to HPET
3121bcbe6eSJason King * ticks)
3221bcbe6eSJason King */
3321bcbe6eSJason King #define HPET_SAMPLE_INTERVAL_NS (USEC2NSEC(HPET_SAMPLE_INTERVAL_US))
3421bcbe6eSJason King
3521bcbe6eSJason King /* The amount of HPET sample ticks to wait */
3621bcbe6eSJason King #define HPET_SAMPLE_TICKS (HRTIME_TO_HPET_TICKS(HPET_SAMPLE_INTERVAL_NS))
3721bcbe6eSJason King
3821bcbe6eSJason King #define TSC_NUM_SAMPLES 10
3921bcbe6eSJason King
4021bcbe6eSJason King static boolean_t
tsc_calibrate_hpet(uint64_t * freqp)4121bcbe6eSJason King tsc_calibrate_hpet(uint64_t *freqp)
4221bcbe6eSJason King {
4321bcbe6eSJason King uint64_t hpet_sum = 0;
4421bcbe6eSJason King uint64_t tsc_sum = 0;
4521bcbe6eSJason King uint_t i;
4621bcbe6eSJason King
4721bcbe6eSJason King PRM_POINT("Attempting to use HPET for TSC calibration...");
4821bcbe6eSJason King
4921bcbe6eSJason King if (hpet_early_init() != DDI_SUCCESS)
5021bcbe6eSJason King return (B_FALSE);
5121bcbe6eSJason King
5221bcbe6eSJason King /*
5321bcbe6eSJason King * The expansion of HPET_SAMPLE_TICKS (specifically
5421bcbe6eSJason King * HRTIME_TO_HPET_TICKS) uses the HPET period to calculate the number
5521bcbe6eSJason King * of HPET ticks for the given time period. Therefore, we cannot
5621bcbe6eSJason King * set hpet_num_ticks until after the early HPET initialization has
5721bcbe6eSJason King * been performed by hpet_early_init() (and the HPET period is known).
5821bcbe6eSJason King */
5921bcbe6eSJason King const uint64_t hpet_num_ticks = HPET_SAMPLE_TICKS;
6021bcbe6eSJason King
6121bcbe6eSJason King for (i = 0; i < TSC_NUM_SAMPLES; i++) {
6221bcbe6eSJason King uint64_t hpet_now, hpet_end;
6321bcbe6eSJason King uint64_t tsc_start, tsc_end;
6421bcbe6eSJason King
6521bcbe6eSJason King hpet_now = hpet_read_timer();
6621bcbe6eSJason King hpet_end = hpet_now + hpet_num_ticks;
6721bcbe6eSJason King
6821bcbe6eSJason King tsc_start = tsc_read();
6921bcbe6eSJason King while (hpet_now < hpet_end)
7021bcbe6eSJason King hpet_now = hpet_read_timer();
7121bcbe6eSJason King
7221bcbe6eSJason King tsc_end = tsc_read();
7321bcbe6eSJason King
7421bcbe6eSJason King /*
7521bcbe6eSJason King * If our TSC isn't advancing after 100us, we're pretty much
7621bcbe6eSJason King * hosed.
7721bcbe6eSJason King */
7821bcbe6eSJason King VERIFY3P(tsc_end, >, tsc_start);
7921bcbe6eSJason King
8021bcbe6eSJason King tsc_sum += tsc_end - tsc_start;
8121bcbe6eSJason King
8221bcbe6eSJason King /*
8321bcbe6eSJason King * We likely did not end exactly HPET_SAMPLE_TICKS after
8421bcbe6eSJason King * we started, so save the actual amount.
8521bcbe6eSJason King */
8621bcbe6eSJason King hpet_sum += hpet_num_ticks + hpet_now - hpet_end;
8721bcbe6eSJason King }
8821bcbe6eSJason King
8921bcbe6eSJason King uint64_t hpet_avg = hpet_sum / TSC_NUM_SAMPLES;
9021bcbe6eSJason King uint64_t tsc_avg = tsc_sum / TSC_NUM_SAMPLES;
9121bcbe6eSJason King uint64_t hpet_ns = hpet_avg * hpet_info.period / HPET_FEMTO_TO_NANO;
9221bcbe6eSJason King
9321bcbe6eSJason King PRM_POINT("HPET calibration complete");
9421bcbe6eSJason King
9521bcbe6eSJason King *freqp = tsc_avg * NANOSEC / hpet_ns;
9621bcbe6eSJason King PRM_DEBUG(*freqp);
9721bcbe6eSJason King
9821bcbe6eSJason King return (B_TRUE);
9921bcbe6eSJason King }
10021bcbe6eSJason King
101*236cb9a8SJoshua M. Clulow /*
102*236cb9a8SJoshua M. Clulow * Reports from the field suggest that HPET calibration is currently producing
103*236cb9a8SJoshua M. Clulow * a substantially greater error than PIT calibration on a wide variety of
104*236cb9a8SJoshua M. Clulow * systems. We are placing it last in the preference order until that can be
105*236cb9a8SJoshua M. Clulow * resolved. HPET calibration cannot be disabled completely, as some systems
106*236cb9a8SJoshua M. Clulow * no longer emulate the PIT at all.
107*236cb9a8SJoshua M. Clulow */
10821bcbe6eSJason King static tsc_calibrate_t tsc_calibration_hpet = {
10921bcbe6eSJason King .tscc_source = "HPET",
110*236cb9a8SJoshua M. Clulow .tscc_preference = 1,
11121bcbe6eSJason King .tscc_calibrate = tsc_calibrate_hpet,
11221bcbe6eSJason King };
11321bcbe6eSJason King TSC_CALIBRATION_SOURCE(tsc_calibration_hpet);
114