199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 299a2dd95SBruce Richardson * Copyright(c) 2010-2014 Intel Corporation. 399a2dd95SBruce Richardson * Copyright(c) 2012-2013 6WIND S.A. 499a2dd95SBruce Richardson */ 599a2dd95SBruce Richardson 699a2dd95SBruce Richardson #include <stdio.h> 799a2dd95SBruce Richardson #include <stdint.h> 8*dbdf3d55SIsaac Boukris #include <inttypes.h> 919d5f522SAndrew Boyer #ifdef RTE_LIBEAL_USE_HPET 1019d5f522SAndrew Boyer #include <fcntl.h> 1119d5f522SAndrew Boyer #include <sys/mman.h> 1219d5f522SAndrew Boyer #include <unistd.h> 1319d5f522SAndrew Boyer #endif 1499a2dd95SBruce Richardson 1599a2dd95SBruce Richardson #include <rte_common.h> 1699a2dd95SBruce Richardson #include <rte_cycles.h> 171c1abf17SThomas Monjalon #include <rte_thread.h> 1899a2dd95SBruce Richardson 1999a2dd95SBruce Richardson #include "eal_private.h" 2099a2dd95SBruce Richardson 2199a2dd95SBruce Richardson enum timer_source eal_timer_source = EAL_TIMER_HPET; 2299a2dd95SBruce Richardson 2399a2dd95SBruce Richardson #ifdef RTE_LIBEAL_USE_HPET 2499a2dd95SBruce Richardson 2599a2dd95SBruce Richardson #define DEV_HPET "/dev/hpet" 2699a2dd95SBruce Richardson 2799a2dd95SBruce Richardson /* Maximum number of counters. */ 2899a2dd95SBruce Richardson #define HPET_TIMER_NUM 3 2999a2dd95SBruce Richardson 3099a2dd95SBruce Richardson /* General capabilities register */ 3199a2dd95SBruce Richardson #define CLK_PERIOD_SHIFT 32 /* Clock period shift. */ 3299a2dd95SBruce Richardson #define CLK_PERIOD_MASK 0xffffffff00000000ULL /* Clock period mask. */ 3399a2dd95SBruce Richardson 3499a2dd95SBruce Richardson /** 3599a2dd95SBruce Richardson * HPET timer registers. From the Intel IA-PC HPET (High Precision Event 3699a2dd95SBruce Richardson * Timers) Specification. 3799a2dd95SBruce Richardson */ 3899a2dd95SBruce Richardson struct eal_hpet_regs { 3999a2dd95SBruce Richardson /* Memory-mapped, software visible registers */ 4099a2dd95SBruce Richardson uint64_t capabilities; /**< RO General Capabilities Register. */ 4199a2dd95SBruce Richardson uint64_t reserved0; /**< Reserved for future use. */ 4299a2dd95SBruce Richardson uint64_t config; /**< RW General Configuration Register. */ 4399a2dd95SBruce Richardson uint64_t reserved1; /**< Reserved for future use. */ 4499a2dd95SBruce Richardson uint64_t isr; /**< RW Clear General Interrupt Status. */ 4599a2dd95SBruce Richardson uint64_t reserved2[25]; /**< Reserved for future use. */ 4699a2dd95SBruce Richardson union { 4799a2dd95SBruce Richardson uint64_t counter; /**< RW Main Counter Value Register. */ 4899a2dd95SBruce Richardson struct { 4999a2dd95SBruce Richardson uint32_t counter_l; /**< RW Main Counter Low. */ 5099a2dd95SBruce Richardson uint32_t counter_h; /**< RW Main Counter High. */ 5199a2dd95SBruce Richardson }; 5299a2dd95SBruce Richardson }; 5399a2dd95SBruce Richardson uint64_t reserved3; /**< Reserved for future use. */ 5499a2dd95SBruce Richardson struct { 5599a2dd95SBruce Richardson uint64_t config; /**< RW Timer Config and Capability Reg. */ 5699a2dd95SBruce Richardson uint64_t comp; /**< RW Timer Comparator Value Register. */ 5799a2dd95SBruce Richardson uint64_t fsb; /**< RW FSB Interrupt Route Register. */ 5899a2dd95SBruce Richardson uint64_t reserved4; /**< Reserved for future use. */ 5999a2dd95SBruce Richardson } timers[HPET_TIMER_NUM]; /**< Set of HPET timers. */ 6099a2dd95SBruce Richardson }; 6199a2dd95SBruce Richardson 6299a2dd95SBruce Richardson /* Mmap'd hpet registers */ 6399a2dd95SBruce Richardson static volatile struct eal_hpet_regs *eal_hpet = NULL; 6499a2dd95SBruce Richardson 6599a2dd95SBruce Richardson /* Period at which the HPET counter increments in 6699a2dd95SBruce Richardson * femtoseconds (10^-15 seconds). */ 6799a2dd95SBruce Richardson static uint32_t eal_hpet_resolution_fs = 0; 6899a2dd95SBruce Richardson 6999a2dd95SBruce Richardson /* Frequency of the HPET counter in Hz */ 7099a2dd95SBruce Richardson static uint64_t eal_hpet_resolution_hz = 0; 7199a2dd95SBruce Richardson 7299a2dd95SBruce Richardson /* Incremented 4 times during one 32bits hpet full count */ 7399a2dd95SBruce Richardson static uint32_t eal_hpet_msb; 7499a2dd95SBruce Richardson 751c1abf17SThomas Monjalon static rte_thread_t msb_inc_thread_id; 7699a2dd95SBruce Richardson 7799a2dd95SBruce Richardson /* 7899a2dd95SBruce Richardson * This function runs on a specific thread to update a global variable 7999a2dd95SBruce Richardson * containing used to process MSB of the HPET (unfortunately, we need 8099a2dd95SBruce Richardson * this because hpet is 32 bits by default under linux). 8199a2dd95SBruce Richardson */ 821c1abf17SThomas Monjalon static uint32_t 8399a2dd95SBruce Richardson hpet_msb_inc(__rte_unused void *arg) 8499a2dd95SBruce Richardson { 8599a2dd95SBruce Richardson uint32_t t; 8699a2dd95SBruce Richardson 8799a2dd95SBruce Richardson while (1) { 8899a2dd95SBruce Richardson t = (eal_hpet->counter_l >> 30); 8999a2dd95SBruce Richardson if (t != (eal_hpet_msb & 3)) 9099a2dd95SBruce Richardson eal_hpet_msb ++; 9199a2dd95SBruce Richardson sleep(10); 9299a2dd95SBruce Richardson } 931c1abf17SThomas Monjalon return 0; 9499a2dd95SBruce Richardson } 9599a2dd95SBruce Richardson 9699a2dd95SBruce Richardson uint64_t 9799a2dd95SBruce Richardson rte_get_hpet_hz(void) 9899a2dd95SBruce Richardson { 9999a2dd95SBruce Richardson const struct internal_config *internal_conf = 10099a2dd95SBruce Richardson eal_get_internal_configuration(); 10199a2dd95SBruce Richardson 10299a2dd95SBruce Richardson if (internal_conf->no_hpet) 10399a2dd95SBruce Richardson rte_panic("Error, HPET called, but no HPET present\n"); 10499a2dd95SBruce Richardson 10599a2dd95SBruce Richardson return eal_hpet_resolution_hz; 10699a2dd95SBruce Richardson } 10799a2dd95SBruce Richardson 10899a2dd95SBruce Richardson uint64_t 10999a2dd95SBruce Richardson rte_get_hpet_cycles(void) 11099a2dd95SBruce Richardson { 11199a2dd95SBruce Richardson uint32_t t, msb; 11299a2dd95SBruce Richardson uint64_t ret; 11399a2dd95SBruce Richardson const struct internal_config *internal_conf = 11499a2dd95SBruce Richardson eal_get_internal_configuration(); 11599a2dd95SBruce Richardson 11699a2dd95SBruce Richardson if (internal_conf->no_hpet) 11799a2dd95SBruce Richardson rte_panic("Error, HPET called, but no HPET present\n"); 11899a2dd95SBruce Richardson 11999a2dd95SBruce Richardson t = eal_hpet->counter_l; 12099a2dd95SBruce Richardson msb = eal_hpet_msb; 12199a2dd95SBruce Richardson ret = (msb + 2 - (t >> 30)) / 4; 12299a2dd95SBruce Richardson ret <<= 32; 12399a2dd95SBruce Richardson ret += t; 12499a2dd95SBruce Richardson return ret; 12599a2dd95SBruce Richardson } 12699a2dd95SBruce Richardson 12799a2dd95SBruce Richardson #endif 12899a2dd95SBruce Richardson 12999a2dd95SBruce Richardson #ifdef RTE_LIBEAL_USE_HPET 13099a2dd95SBruce Richardson /* 13199a2dd95SBruce Richardson * Open and mmap /dev/hpet (high precision event timer) that will 13299a2dd95SBruce Richardson * provide our time reference. 13399a2dd95SBruce Richardson */ 13499a2dd95SBruce Richardson int 13599a2dd95SBruce Richardson rte_eal_hpet_init(int make_default) 13699a2dd95SBruce Richardson { 13799a2dd95SBruce Richardson int fd, ret; 13899a2dd95SBruce Richardson struct internal_config *internal_conf = 13999a2dd95SBruce Richardson eal_get_internal_configuration(); 14099a2dd95SBruce Richardson 14199a2dd95SBruce Richardson if (internal_conf->no_hpet) { 142ae67895bSDavid Marchand EAL_LOG(NOTICE, "HPET is disabled"); 14399a2dd95SBruce Richardson return -1; 14499a2dd95SBruce Richardson } 14599a2dd95SBruce Richardson 14699a2dd95SBruce Richardson fd = open(DEV_HPET, O_RDONLY); 14799a2dd95SBruce Richardson if (fd < 0) { 148ae67895bSDavid Marchand EAL_LOG(ERR, "ERROR: Cannot open "DEV_HPET": %s!", 14999a2dd95SBruce Richardson strerror(errno)); 15099a2dd95SBruce Richardson internal_conf->no_hpet = 1; 15199a2dd95SBruce Richardson return -1; 15299a2dd95SBruce Richardson } 15399a2dd95SBruce Richardson eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0); 15499a2dd95SBruce Richardson if (eal_hpet == MAP_FAILED) { 155ae67895bSDavid Marchand EAL_LOG(ERR, "ERROR: Cannot mmap "DEV_HPET"!"); 15699a2dd95SBruce Richardson close(fd); 15799a2dd95SBruce Richardson internal_conf->no_hpet = 1; 15899a2dd95SBruce Richardson return -1; 15999a2dd95SBruce Richardson } 16099a2dd95SBruce Richardson close(fd); 16199a2dd95SBruce Richardson 16299a2dd95SBruce Richardson eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities & 16399a2dd95SBruce Richardson CLK_PERIOD_MASK) >> 16499a2dd95SBruce Richardson CLK_PERIOD_SHIFT); 16599a2dd95SBruce Richardson 16699a2dd95SBruce Richardson eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) / 16799a2dd95SBruce Richardson (uint64_t)eal_hpet_resolution_fs; 16899a2dd95SBruce Richardson 169ae67895bSDavid Marchand EAL_LOG(INFO, "HPET frequency is ~%"PRIu64" kHz", 17099a2dd95SBruce Richardson eal_hpet_resolution_hz/1000); 17199a2dd95SBruce Richardson 17299a2dd95SBruce Richardson eal_hpet_msb = (eal_hpet->counter_l >> 30); 17399a2dd95SBruce Richardson 17499a2dd95SBruce Richardson /* create a thread that will increment a global variable for 17599a2dd95SBruce Richardson * msb (hpet is 32 bits by default under linux) */ 1761c1abf17SThomas Monjalon ret = rte_thread_create_internal_control(&msb_inc_thread_id, "hpet-msb", 17799a2dd95SBruce Richardson hpet_msb_inc, NULL); 17899a2dd95SBruce Richardson if (ret != 0) { 179ae67895bSDavid Marchand EAL_LOG(ERR, "ERROR: Cannot create HPET timer thread!"); 18099a2dd95SBruce Richardson internal_conf->no_hpet = 1; 18199a2dd95SBruce Richardson return -1; 18299a2dd95SBruce Richardson } 18399a2dd95SBruce Richardson 18499a2dd95SBruce Richardson if (make_default) 18599a2dd95SBruce Richardson eal_timer_source = EAL_TIMER_HPET; 18699a2dd95SBruce Richardson return 0; 18799a2dd95SBruce Richardson } 18899a2dd95SBruce Richardson #endif 18999a2dd95SBruce Richardson 190*dbdf3d55SIsaac Boukris /* Check if the kernel deems the arch provided TSC frequency trustworthy. */ 191*dbdf3d55SIsaac Boukris 192*dbdf3d55SIsaac Boukris static bool 193*dbdf3d55SIsaac Boukris is_tsc_known_freq(void) 194*dbdf3d55SIsaac Boukris { 195*dbdf3d55SIsaac Boukris bool ret = true; /* Assume tsc_known_freq */ 196*dbdf3d55SIsaac Boukris 197*dbdf3d55SIsaac Boukris #if defined(RTE_ARCH_X86) 198*dbdf3d55SIsaac Boukris char line[2048]; 199*dbdf3d55SIsaac Boukris FILE *stream; 200*dbdf3d55SIsaac Boukris 201*dbdf3d55SIsaac Boukris stream = fopen("/proc/cpuinfo", "r"); 202*dbdf3d55SIsaac Boukris if (!stream) { 203*dbdf3d55SIsaac Boukris EAL_LOG(WARNING, "Unable to open /proc/cpuinfo"); 204*dbdf3d55SIsaac Boukris return ret; 205*dbdf3d55SIsaac Boukris } 206*dbdf3d55SIsaac Boukris 207*dbdf3d55SIsaac Boukris while (fgets(line, sizeof(line), stream)) { 208*dbdf3d55SIsaac Boukris if (strncmp(line, "flags", 5) != 0) 209*dbdf3d55SIsaac Boukris continue; 210*dbdf3d55SIsaac Boukris 211*dbdf3d55SIsaac Boukris if (!strstr(line, "tsc_known_freq")) 212*dbdf3d55SIsaac Boukris ret = false; 213*dbdf3d55SIsaac Boukris 214*dbdf3d55SIsaac Boukris break; 215*dbdf3d55SIsaac Boukris } 216*dbdf3d55SIsaac Boukris 217*dbdf3d55SIsaac Boukris fclose(stream); 218*dbdf3d55SIsaac Boukris #endif 219*dbdf3d55SIsaac Boukris 220*dbdf3d55SIsaac Boukris return ret; 221*dbdf3d55SIsaac Boukris } 222*dbdf3d55SIsaac Boukris 22399a2dd95SBruce Richardson uint64_t 224*dbdf3d55SIsaac Boukris get_tsc_freq(uint64_t arch_hz) 22599a2dd95SBruce Richardson { 22699a2dd95SBruce Richardson #ifdef CLOCK_MONOTONIC_RAW 22799a2dd95SBruce Richardson #define NS_PER_SEC 1E9 2287268f21aSIsaac Boukris #define CYC_PER_100KHZ 1E5 22999a2dd95SBruce Richardson 23099a2dd95SBruce Richardson struct timespec sleeptime = {.tv_nsec = NS_PER_SEC / 10 }; /* 1/10 second */ 23199a2dd95SBruce Richardson 23299a2dd95SBruce Richardson struct timespec t_start, t_end; 23399a2dd95SBruce Richardson uint64_t tsc_hz; 23499a2dd95SBruce Richardson 235*dbdf3d55SIsaac Boukris if (arch_hz && is_tsc_known_freq()) 236*dbdf3d55SIsaac Boukris return arch_hz; 237*dbdf3d55SIsaac Boukris 23899a2dd95SBruce Richardson if (clock_gettime(CLOCK_MONOTONIC_RAW, &t_start) == 0) { 23999a2dd95SBruce Richardson uint64_t ns, end, start = rte_rdtsc(); 24099a2dd95SBruce Richardson nanosleep(&sleeptime,NULL); 24199a2dd95SBruce Richardson clock_gettime(CLOCK_MONOTONIC_RAW, &t_end); 24299a2dd95SBruce Richardson end = rte_rdtsc(); 24399a2dd95SBruce Richardson ns = ((t_end.tv_sec - t_start.tv_sec) * NS_PER_SEC); 24499a2dd95SBruce Richardson ns += (t_end.tv_nsec - t_start.tv_nsec); 24599a2dd95SBruce Richardson 24699a2dd95SBruce Richardson double secs = (double)ns/NS_PER_SEC; 24799a2dd95SBruce Richardson tsc_hz = (uint64_t)((end - start)/secs); 248*dbdf3d55SIsaac Boukris 249*dbdf3d55SIsaac Boukris if (arch_hz) { 250*dbdf3d55SIsaac Boukris /* Make sure we're within 1% for sanity check */ 251*dbdf3d55SIsaac Boukris if (RTE_MAX(arch_hz, tsc_hz) - RTE_MIN(arch_hz, tsc_hz) > arch_hz / 100) 252*dbdf3d55SIsaac Boukris return arch_hz; 253*dbdf3d55SIsaac Boukris 254*dbdf3d55SIsaac Boukris EAL_LOG(DEBUG, 255*dbdf3d55SIsaac Boukris "Refined arch frequency %"PRIu64" to measured frequency %"PRIu64, 256*dbdf3d55SIsaac Boukris arch_hz, tsc_hz); 257*dbdf3d55SIsaac Boukris } 258*dbdf3d55SIsaac Boukris 2597268f21aSIsaac Boukris /* Round up to 100Khz. 1E5 ~ 100Khz */ 2607268f21aSIsaac Boukris return RTE_ALIGN_MUL_NEAR(tsc_hz, CYC_PER_100KHZ); 26199a2dd95SBruce Richardson } 26299a2dd95SBruce Richardson #endif 263*dbdf3d55SIsaac Boukris return arch_hz; 26499a2dd95SBruce Richardson } 26599a2dd95SBruce Richardson 26699a2dd95SBruce Richardson int 26799a2dd95SBruce Richardson rte_eal_timer_init(void) 26899a2dd95SBruce Richardson { 26999a2dd95SBruce Richardson 27099a2dd95SBruce Richardson eal_timer_source = EAL_TIMER_TSC; 27199a2dd95SBruce Richardson 27299a2dd95SBruce Richardson set_tsc_freq(); 27399a2dd95SBruce Richardson return 0; 27499a2dd95SBruce Richardson } 275