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