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