xref: /dpdk/lib/eal/linux/eal_timer.c (revision c56185fc183fc0532d2f03aaf04bbf0989ea91a5)
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