1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation. 3 * Copyright(c) 2012-2013 6WIND S.A. 4 */ 5 6 #include <stdio.h> 7 #include <stdint.h> 8 9 #include <rte_common.h> 10 #include <rte_cycles.h> 11 12 #include "eal_private.h" 13 14 enum timer_source eal_timer_source = EAL_TIMER_HPET; 15 16 #ifdef RTE_LIBEAL_USE_HPET 17 18 #define DEV_HPET "/dev/hpet" 19 20 /* Maximum number of counters. */ 21 #define HPET_TIMER_NUM 3 22 23 /* General capabilities register */ 24 #define CLK_PERIOD_SHIFT 32 /* Clock period shift. */ 25 #define CLK_PERIOD_MASK 0xffffffff00000000ULL /* Clock period mask. */ 26 27 /** 28 * HPET timer registers. From the Intel IA-PC HPET (High Precision Event 29 * Timers) Specification. 30 */ 31 struct eal_hpet_regs { 32 /* Memory-mapped, software visible registers */ 33 uint64_t capabilities; /**< RO General Capabilities Register. */ 34 uint64_t reserved0; /**< Reserved for future use. */ 35 uint64_t config; /**< RW General Configuration Register. */ 36 uint64_t reserved1; /**< Reserved for future use. */ 37 uint64_t isr; /**< RW Clear General Interrupt Status. */ 38 uint64_t reserved2[25]; /**< Reserved for future use. */ 39 union { 40 uint64_t counter; /**< RW Main Counter Value Register. */ 41 struct { 42 uint32_t counter_l; /**< RW Main Counter Low. */ 43 uint32_t counter_h; /**< RW Main Counter High. */ 44 }; 45 }; 46 uint64_t reserved3; /**< Reserved for future use. */ 47 struct { 48 uint64_t config; /**< RW Timer Config and Capability Reg. */ 49 uint64_t comp; /**< RW Timer Comparator Value Register. */ 50 uint64_t fsb; /**< RW FSB Interrupt Route Register. */ 51 uint64_t reserved4; /**< Reserved for future use. */ 52 } timers[HPET_TIMER_NUM]; /**< Set of HPET timers. */ 53 }; 54 55 /* Mmap'd hpet registers */ 56 static volatile struct eal_hpet_regs *eal_hpet = NULL; 57 58 /* Period at which the HPET counter increments in 59 * femtoseconds (10^-15 seconds). */ 60 static uint32_t eal_hpet_resolution_fs = 0; 61 62 /* Frequency of the HPET counter in Hz */ 63 static uint64_t eal_hpet_resolution_hz = 0; 64 65 /* Incremented 4 times during one 32bits hpet full count */ 66 static uint32_t eal_hpet_msb; 67 68 static pthread_t msb_inc_thread_id; 69 70 /* 71 * This function runs on a specific thread to update a global variable 72 * containing used to process MSB of the HPET (unfortunately, we need 73 * this because hpet is 32 bits by default under linux). 74 */ 75 static void * 76 hpet_msb_inc(__rte_unused void *arg) 77 { 78 uint32_t t; 79 80 while (1) { 81 t = (eal_hpet->counter_l >> 30); 82 if (t != (eal_hpet_msb & 3)) 83 eal_hpet_msb ++; 84 sleep(10); 85 } 86 return NULL; 87 } 88 89 uint64_t 90 rte_get_hpet_hz(void) 91 { 92 const struct internal_config *internal_conf = 93 eal_get_internal_configuration(); 94 95 if (internal_conf->no_hpet) 96 rte_panic("Error, HPET called, but no HPET present\n"); 97 98 return eal_hpet_resolution_hz; 99 } 100 101 uint64_t 102 rte_get_hpet_cycles(void) 103 { 104 uint32_t t, msb; 105 uint64_t ret; 106 const struct internal_config *internal_conf = 107 eal_get_internal_configuration(); 108 109 if (internal_conf->no_hpet) 110 rte_panic("Error, HPET called, but no HPET present\n"); 111 112 t = eal_hpet->counter_l; 113 msb = eal_hpet_msb; 114 ret = (msb + 2 - (t >> 30)) / 4; 115 ret <<= 32; 116 ret += t; 117 return ret; 118 } 119 120 #endif 121 122 #ifdef RTE_LIBEAL_USE_HPET 123 /* 124 * Open and mmap /dev/hpet (high precision event timer) that will 125 * provide our time reference. 126 */ 127 int 128 rte_eal_hpet_init(int make_default) 129 { 130 int fd, ret; 131 struct internal_config *internal_conf = 132 eal_get_internal_configuration(); 133 134 if (internal_conf->no_hpet) { 135 RTE_LOG(NOTICE, EAL, "HPET is disabled\n"); 136 return -1; 137 } 138 139 fd = open(DEV_HPET, O_RDONLY); 140 if (fd < 0) { 141 RTE_LOG(ERR, EAL, "ERROR: Cannot open "DEV_HPET": %s!\n", 142 strerror(errno)); 143 internal_conf->no_hpet = 1; 144 return -1; 145 } 146 eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0); 147 if (eal_hpet == MAP_FAILED) { 148 RTE_LOG(ERR, EAL, "ERROR: Cannot mmap "DEV_HPET"!\n" 149 "Please enable CONFIG_HPET_MMAP in your kernel configuration " 150 "to allow HPET support.\n" 151 "To run without using HPET, unset RTE_LIBEAL_USE_HPET " 152 "in your build configuration or use '--no-hpet' EAL flag.\n"); 153 close(fd); 154 internal_conf->no_hpet = 1; 155 return -1; 156 } 157 close(fd); 158 159 eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities & 160 CLK_PERIOD_MASK) >> 161 CLK_PERIOD_SHIFT); 162 163 eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) / 164 (uint64_t)eal_hpet_resolution_fs; 165 166 RTE_LOG(INFO, EAL, "HPET frequency is ~%"PRIu64" kHz\n", 167 eal_hpet_resolution_hz/1000); 168 169 eal_hpet_msb = (eal_hpet->counter_l >> 30); 170 171 /* create a thread that will increment a global variable for 172 * msb (hpet is 32 bits by default under linux) */ 173 ret = rte_ctrl_thread_create(&msb_inc_thread_id, "hpet-msb-inc", NULL, 174 hpet_msb_inc, NULL); 175 if (ret != 0) { 176 RTE_LOG(ERR, EAL, "ERROR: Cannot create HPET timer thread!\n"); 177 internal_conf->no_hpet = 1; 178 return -1; 179 } 180 181 if (make_default) 182 eal_timer_source = EAL_TIMER_HPET; 183 return 0; 184 } 185 #endif 186 187 uint64_t 188 get_tsc_freq(void) 189 { 190 #ifdef CLOCK_MONOTONIC_RAW 191 #define NS_PER_SEC 1E9 192 #define CYC_PER_10MHZ 1E7 193 194 struct timespec sleeptime = {.tv_nsec = NS_PER_SEC / 10 }; /* 1/10 second */ 195 196 struct timespec t_start, t_end; 197 uint64_t tsc_hz; 198 199 if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { 200 uint64_t ns, end, start = rte_rdtsc(); 201 nanosleep(&sleeptime,NULL); 202 clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); 203 end = rte_rdtsc(); 204 ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); 205 ns += (t_end.tv_nsec - t_start.tv_nsec); 206 207 double secs = (double)ns/NS_PER_SEC; 208 tsc_hz = (uint64_t)((end - start)/secs); 209 /* Round up to 10Mhz. 1E7 ~ 10Mhz */ 210 return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_10MHZ); 211 } 212 #endif 213 return 0; 214 } 215 216 int 217 rte_eal_timer_init(void) 218 { 219 220 eal_timer_source = EAL_TIMER_TSC; 221 222 set_tsc_freq(); 223 return 0; 224 } 225