Branch data Line data Source code
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 : : EAL_LOG(NOTICE, "HPET is disabled"); 143 : : return -1; 144 : : } 145 : : 146 : : fd = open(DEV_HPET, O_RDONLY); 147 : : if (fd < 0) { 148 : : EAL_LOG(ERR, "ERROR: Cannot open "DEV_HPET": %s!", 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 : : EAL_LOG(ERR, "ERROR: Cannot mmap "DEV_HPET"!"); 156 : : close(fd); 157 : : internal_conf->no_hpet = 1; 158 : : return -1; 159 : : } 160 : : close(fd); 161 : : 162 : : eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities & 163 : : CLK_PERIOD_MASK) >> 164 : : CLK_PERIOD_SHIFT); 165 : : 166 : : eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) / 167 : : (uint64_t)eal_hpet_resolution_fs; 168 : : 169 : : EAL_LOG(INFO, "HPET frequency is ~%"PRIu64" kHz", 170 : : eal_hpet_resolution_hz/1000); 171 : : 172 : : eal_hpet_msb = (eal_hpet->counter_l >> 30); 173 : : 174 : : /* create a thread that will increment a global variable for 175 : : * msb (hpet is 32 bits by default under linux) */ 176 : : ret = rte_thread_create_internal_control(&msb_inc_thread_id, "hpet-msb", 177 : : hpet_msb_inc, NULL); 178 : : if (ret != 0) { 179 : : EAL_LOG(ERR, "ERROR: Cannot create HPET timer thread!"); 180 : : internal_conf->no_hpet = 1; 181 : : return -1; 182 : : } 183 : : 184 : : if (make_default) 185 : : eal_timer_source = EAL_TIMER_HPET; 186 : : return 0; 187 : : } 188 : : #endif 189 : : 190 : : uint64_t 191 : 144 : get_tsc_freq(void) 192 : : { 193 : : #ifdef CLOCK_MONOTONIC_RAW 194 : : #define NS_PER_SEC 1E9 195 : : #define CYC_PER_10MHZ 1E7 196 : : 197 : 144 : struct timespec sleeptime = {.tv_nsec = NS_PER_SEC / 10 }; /* 1/10 second */ 198 : : 199 : : struct timespec t_start, t_end; 200 : : uint64_t tsc_hz; 201 : : 202 [ + - ]: 144 : if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { 203 : : uint64_t ns, end, start = rte_rdtsc(); 204 : 144 : nanosleep(&sleeptime,NULL); 205 : 144 : clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); 206 : : end = rte_rdtsc(); 207 : 144 : ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); 208 : 144 : ns += (t_end.tv_nsec - t_start.tv_nsec); 209 : : 210 : 144 : double secs = (double)ns/NS_PER_SEC; 211 : 144 : tsc_hz = (uint64_t)((end - start)/secs); 212 : : /* Round up to 10Mhz. 1E7 ~ 10Mhz */ 213 [ - + ]: 144 : return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_10MHZ); 214 : : } 215 : : #endif 216 : : return 0; 217 : : } 218 : : 219 : : int 220 : 164 : rte_eal_timer_init(void) 221 : : { 222 : : 223 : 164 : eal_timer_source = EAL_TIMER_TSC; 224 : : 225 : 164 : set_tsc_freq(); 226 : 164 : return 0; 227 : : }