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