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