xref: /illumos-gate/usr/src/uts/i86pc/os/tscc_hpet.c (revision 236cb9a8)
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