first public release
[dpdk.git] / lib / librte_eal / linuxapp / eal / eal_hpet.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2012 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without 
8  *   modification, are permitted provided that the following conditions 
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright 
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright 
14  *       notice, this list of conditions and the following disclaimer in 
15  *       the documentation and/or other materials provided with the 
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its 
18  *       contributors may be used to endorse or promote products derived 
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  * 
33  *  version: DPDK.L.1.2.3-3
34  */
35
36 #include <string.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <stdarg.h>
40 #include <stdint.h>
41 #include <fcntl.h>
42 #include <inttypes.h>
43 #include <sys/mman.h>
44 #include <sys/queue.h>
45 #include <unistd.h>
46 #include <pthread.h>
47 #include <errno.h>
48
49 #include <rte_common.h>
50 #include <rte_log.h>
51 #include <rte_cycles.h>
52 #include <rte_tailq.h>
53 #include <rte_memory.h>
54 #include <rte_memzone.h>
55 #include <rte_eal.h>
56
57 #include "eal_private.h"
58 #include "eal_internal_cfg.h"
59
60 #define DEV_HPET "/dev/hpet"
61
62 /* Maximum number of counters. */
63 #define HPET_TIMER_NUM 3
64
65 /* General capabilities register */
66 #define CLK_PERIOD_SHIFT     32 /* Clock period shift. */
67 #define CLK_PERIOD_MASK      0xffffffff00000000ULL /* Clock period mask. */
68 #define COUNT_SIZE_CAP_SHIFT 13 /* Count size capa. shift. */
69 #define COUNT_SIZE_CAP_MASK 0x0000000000002000ULL /* Count size capa. mask. */
70
71 /**
72  * HPET timer registers. From the Intel IA-PC HPET (High Precision Event
73  * Timers) Specification.
74  */
75 struct eal_hpet_regs {
76         /* Memory-mapped, software visible registers */
77         uint64_t capabilities;      /**< RO General Capabilities Register. */
78         uint64_t reserved0;         /**< Reserved for future use. */
79         uint64_t config;            /**< RW General Configuration Register. */
80         uint64_t reserved1;         /**< Reserved for future use. */
81         uint64_t isr;               /**< RW Clear General Interrupt Status. */
82         uint64_t reserved2[25];     /**< Reserved for future use. */
83         union {
84                 uint64_t counter;   /**< RW Main Counter Value Register. */
85                 struct {
86                         uint32_t counter_l; /**< RW Main Counter Low. */
87                         uint32_t counter_h; /**< RW Main Counter High. */
88                 };
89         };
90         uint64_t reserved3;         /**< Reserved for future use. */
91         struct {
92                 uint64_t config;    /**< RW Timer Config and Capability Reg. */
93                 uint64_t comp;      /**< RW Timer Comparator Value Register. */
94                 uint64_t fsb;       /**< RW FSB Interrupt Route Register. */
95                 uint64_t reserved4; /**< Reserved for future use. */
96         } timers[HPET_TIMER_NUM]; /**< Set of HPET timers. */
97 };
98
99 /* Mmap'd hpet registers */
100 static volatile struct eal_hpet_regs *eal_hpet = NULL;
101
102 /* Period at which the counter increments in femtoseconds (10^-15 seconds). */
103 static uint32_t eal_hpet_resolution_fs = 0;
104
105 /* Frequency of the counter in Hz */
106 static uint64_t eal_hpet_resolution_hz = 0;
107
108 /* Incremented 4 times during one 32bits hpet full count */
109 static uint32_t eal_hpet_msb;
110
111 static pthread_t msb_inc_thread_id;
112
113 /*
114  * This function runs on a specific thread to update a global variable
115  * containing used to process MSB of the HPET (unfortunatelly, we need
116  * this because hpet is 32 bits by default under linux).
117  */
118 static __attribute__((noreturn)) void *
119 hpet_msb_inc(__attribute__((unused)) void *arg)
120 {
121         uint32_t t;
122
123         while (1) {
124                 t = (eal_hpet->counter_l >> 30);
125                 if (t != (eal_hpet_msb & 3))
126                         eal_hpet_msb ++;
127                 sleep(10);
128         }
129 }
130
131 static inline void
132 set_rdtsc_freq(void)
133 {
134         uint64_t start;
135
136         start = rte_rdtsc();
137         sleep(1);
138         eal_hpet_resolution_hz = rte_rdtsc() - start;
139         eal_hpet_resolution_fs = (uint32_t)
140                         ((1.0 / eal_hpet_resolution_hz) / 1e-15);
141 }
142
143 /*
144  * Open and mmap /dev/hpet (high precision event timer) that will
145  * provide our time reference.
146  */
147 int
148 rte_eal_hpet_init(void)
149 {
150         int fd, ret;
151
152         if (internal_config.no_hpet) {
153                 goto use_rdtsc;
154         }
155
156         fd = open(DEV_HPET, O_RDONLY);
157         if (fd < 0) {
158                 RTE_LOG(WARNING, EAL, "WARNING: Cannot open "DEV_HPET": %s! "
159                                 "The TSC will be used instead.\n",
160                         strerror(errno));
161                 goto use_rdtsc;
162         }
163         eal_hpet = mmap(NULL, 1024, PROT_READ, MAP_SHARED, fd, 0);
164         if (eal_hpet == MAP_FAILED) {
165                 RTE_LOG(WARNING, EAL, "WARNING: Cannot mmap "DEV_HPET"! "
166                                 "The TSC will be used instead.\n");
167                 close(fd);
168                 goto use_rdtsc;
169         }
170         close(fd);
171
172         eal_hpet_resolution_fs = (uint32_t)((eal_hpet->capabilities &
173                                         CLK_PERIOD_MASK) >>
174                                         CLK_PERIOD_SHIFT);
175
176         eal_hpet_resolution_hz = (1000ULL*1000ULL*1000ULL*1000ULL*1000ULL) /
177                 (uint64_t)eal_hpet_resolution_fs;
178
179         eal_hpet_msb = (eal_hpet->counter_l >> 30);
180
181         /* create a thread that will increment a global variable for
182          * msb (hpet is 32 bits by default under linux) */
183         ret = pthread_create(&msb_inc_thread_id, NULL, hpet_msb_inc, NULL);
184         if (ret < 0) {
185                 RTE_LOG(WARNING, EAL, "WARNING: Cannot create HPET timer thread! "
186                                 "The TSC will be used instead.\n");
187                 goto use_rdtsc;
188         }
189
190         return 0;
191
192 use_rdtsc:
193         internal_config.no_hpet = 1;
194         set_rdtsc_freq();
195         return 0;
196 }
197
198 uint64_t
199 rte_get_hpet_hz(void)
200 {
201         return eal_hpet_resolution_hz;
202 }
203
204 uint64_t
205 rte_get_hpet_cycles(void)
206 {
207         uint32_t t, msb;
208         uint64_t ret;
209
210         if(internal_config.no_hpet)
211                 /* fallback to rdtsc */
212                 return rte_rdtsc();
213
214         t = eal_hpet->counter_l;
215         msb = eal_hpet_msb;
216         ret = (msb + 2 - (t >> 30)) / 4;
217         ret <<= 32;
218         ret += t;
219         return ret;
220 }
221
222 void
223 rte_delay_us(unsigned us)
224 {
225         uint64_t start;
226         uint64_t ticks;
227         ticks = (uint64_t)us * 1000ULL * 1000ULL * 1000ULL;
228         ticks /= eal_hpet_resolution_fs;
229         start = rte_get_hpet_cycles();
230         while ((rte_get_hpet_cycles() - start) < ticks)
231                 rte_pause();
232 }