1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Joyent, Inc. 14 */ 15 16 #include <sys/tsc.h> 17 #include <sys/prom_debug.h> 18 #include <sys/hpet.h> 19 #include <sys/clock.h> 20 21 /* 22 * The amount of time (in microseconds) between tsc samples. This is 23 * somewhat arbitrary, but seems reasonable. A frequency of 1GHz is 24 * 1,000,000,000 ticks / sec (10^9). 100us is 10^(-6) * 10^2 => 10^(-4), so 25 * 100us would represent 10^5 (100,000) ticks. 26 */ 27 #define HPET_SAMPLE_INTERVAL_US (100) 28 29 /* 30 * The same as above, but in nanoseconds (for ease in converting to HPET 31 * ticks) 32 */ 33 #define HPET_SAMPLE_INTERVAL_NS (USEC2NSEC(HPET_SAMPLE_INTERVAL_US)) 34 35 /* The amount of HPET sample ticks to wait */ 36 #define HPET_SAMPLE_TICKS (HRTIME_TO_HPET_TICKS(HPET_SAMPLE_INTERVAL_NS)) 37 38 #define TSC_NUM_SAMPLES 10 39 40 static boolean_t 41 tsc_calibrate_hpet(uint64_t *freqp) 42 { 43 uint64_t hpet_sum = 0; 44 uint64_t tsc_sum = 0; 45 uint_t i; 46 47 PRM_POINT("Attempting to use HPET for TSC calibration..."); 48 49 if (hpet_early_init() != DDI_SUCCESS) 50 return (B_FALSE); 51 52 /* 53 * The expansion of HPET_SAMPLE_TICKS (specifically 54 * HRTIME_TO_HPET_TICKS) uses the HPET period to calculate the number 55 * of HPET ticks for the given time period. Therefore, we cannot 56 * set hpet_num_ticks until after the early HPET initialization has 57 * been performed by hpet_early_init() (and the HPET period is known). 58 */ 59 const uint64_t hpet_num_ticks = HPET_SAMPLE_TICKS; 60 61 for (i = 0; i < TSC_NUM_SAMPLES; i++) { 62 uint64_t hpet_now, hpet_end; 63 uint64_t tsc_start, tsc_end; 64 65 hpet_now = hpet_read_timer(); 66 hpet_end = hpet_now + hpet_num_ticks; 67 68 tsc_start = tsc_read(); 69 while (hpet_now < hpet_end) 70 hpet_now = hpet_read_timer(); 71 72 tsc_end = tsc_read(); 73 74 /* 75 * If our TSC isn't advancing after 100us, we're pretty much 76 * hosed. 77 */ 78 VERIFY3P(tsc_end, >, tsc_start); 79 80 tsc_sum += tsc_end - tsc_start; 81 82 /* 83 * We likely did not end exactly HPET_SAMPLE_TICKS after 84 * we started, so save the actual amount. 85 */ 86 hpet_sum += hpet_num_ticks + hpet_now - hpet_end; 87 } 88 89 uint64_t hpet_avg = hpet_sum / TSC_NUM_SAMPLES; 90 uint64_t tsc_avg = tsc_sum / TSC_NUM_SAMPLES; 91 uint64_t hpet_ns = hpet_avg * hpet_info.period / HPET_FEMTO_TO_NANO; 92 93 PRM_POINT("HPET calibration complete"); 94 95 *freqp = tsc_avg * NANOSEC / hpet_ns; 96 PRM_DEBUG(*freqp); 97 98 return (B_TRUE); 99 } 100 101 /* 102 * Reports from the field suggest that HPET calibration is currently producing 103 * a substantially greater error than PIT calibration on a wide variety of 104 * systems. We are placing it last in the preference order until that can be 105 * resolved. HPET calibration cannot be disabled completely, as some systems 106 * no longer emulate the PIT at all. 107 */ 108 static tsc_calibrate_t tsc_calibration_hpet = { 109 .tscc_source = "HPET", 110 .tscc_preference = 1, 111 .tscc_calibrate = tsc_calibrate_hpet, 112 }; 113 TSC_CALIBRATION_SOURCE(tsc_calibration_hpet); 114