blob: 4e7cc8d3359b9a839ed941d5dbad01598869c306 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001 High Precision Event Timer Driver for Linux
2
3The High Precision Event Timer (HPET) hardware is the future replacement for the 8254 and Real
4Time Clock (RTC) periodic timer functionality. Each HPET can have up two 32 timers. It is possible
5to configure the first two timers as legacy replacements for 8254 and RTC periodic. A specification
6done by INTEL and Microsoft can be found at http://www.intel.com/labs/platcomp/hpet/hpetspec.htm.
7
8The driver supports detection of HPET driver allocation and initialization of the HPET before the
9driver module_init routine is called. This enables platform code which uses timer 0 or 1 as the
10main timer to intercept HPET initialization. An example of this initialization can be found in
11arch/i386/kernel/time_hpet.c.
12
13The driver provides two APIs which are very similar to the API found in the rtc.c driver.
14There is a user space API and a kernel space API. An example user space program is provided
15below.
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <fcntl.h>
21#include <string.h>
22#include <memory.h>
23#include <malloc.h>
24#include <time.h>
25#include <ctype.h>
26#include <sys/types.h>
27#include <sys/wait.h>
28#include <signal.h>
29#include <fcntl.h>
30#include <errno.h>
31#include <sys/time.h>
32#include <linux/hpet.h>
33
34
35extern void hpet_open_close(int, const char **);
36extern void hpet_info(int, const char **);
37extern void hpet_poll(int, const char **);
38extern void hpet_fasync(int, const char **);
39extern void hpet_read(int, const char **);
40
41#include <sys/poll.h>
42#include <sys/ioctl.h>
43#include <signal.h>
44
45struct hpet_command {
46 char *command;
47 void (*func)(int argc, const char ** argv);
48} hpet_command[] = {
49 {
50 "open-close",
51 hpet_open_close
52 },
53 {
54 "info",
55 hpet_info
56 },
57 {
58 "poll",
59 hpet_poll
60 },
61 {
62 "fasync",
63 hpet_fasync
64 },
65};
66
67int
68main(int argc, const char ** argv)
69{
70 int i;
71
72 argc--;
73 argv++;
74
75 if (!argc) {
76 fprintf(stderr, "-hpet: requires command\n");
77 return -1;
78 }
79
80
81 for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++)
82 if (!strcmp(argv[0], hpet_command[i].command)) {
83 argc--;
84 argv++;
85 fprintf(stderr, "-hpet: executing %s\n",
86 hpet_command[i].command);
87 hpet_command[i].func(argc, argv);
88 return 0;
89 }
90
91 fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]);
92
93 return -1;
94}
95
96void
97hpet_open_close(int argc, const char **argv)
98{
99 int fd;
100
101 if (argc != 1) {
102 fprintf(stderr, "hpet_open_close: device-name\n");
103 return;
104 }
105
106 fd = open(argv[0], O_RDONLY);
107 if (fd < 0)
108 fprintf(stderr, "hpet_open_close: open failed\n");
109 else
110 close(fd);
111
112 return;
113}
114
115void
116hpet_info(int argc, const char **argv)
117{
118}
119
120void
121hpet_poll(int argc, const char **argv)
122{
123 unsigned long freq;
124 int iterations, i, fd;
125 struct pollfd pfd;
126 struct hpet_info info;
127 struct timeval stv, etv;
128 struct timezone tz;
129 long usec;
130
131 if (argc != 3) {
132 fprintf(stderr, "hpet_poll: device-name freq iterations\n");
133 return;
134 }
135
136 freq = atoi(argv[1]);
137 iterations = atoi(argv[2]);
138
139 fd = open(argv[0], O_RDONLY);
140
141 if (fd < 0) {
142 fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]);
143 return;
144 }
145
146 if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
147 fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n");
148 goto out;
149 }
150
151 if (ioctl(fd, HPET_INFO, &info) < 0) {
152 fprintf(stderr, "hpet_poll: failed to get info\n");
153 goto out;
154 }
155
156 fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags);
157
158 if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
159 fprintf(stderr, "hpet_poll: HPET_EPI failed\n");
160 goto out;
161 }
162
163 if (ioctl(fd, HPET_IE_ON, 0) < 0) {
164 fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n");
165 goto out;
166 }
167
168 pfd.fd = fd;
169 pfd.events = POLLIN;
170
171 for (i = 0; i < iterations; i++) {
172 pfd.revents = 0;
173 gettimeofday(&stv, &tz);
174 if (poll(&pfd, 1, -1) < 0)
175 fprintf(stderr, "hpet_poll: poll failed\n");
176 else {
177 long data;
178
179 gettimeofday(&etv, &tz);
180 usec = stv.tv_sec * 1000000 + stv.tv_usec;
181 usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec;
182
183 fprintf(stderr,
184 "hpet_poll: expired time = 0x%lx\n", usec);
185
186 fprintf(stderr, "hpet_poll: revents = 0x%x\n",
187 pfd.revents);
188
189 if (read(fd, &data, sizeof(data)) != sizeof(data)) {
190 fprintf(stderr, "hpet_poll: read failed\n");
191 }
192 else
193 fprintf(stderr, "hpet_poll: data 0x%lx\n",
194 data);
195 }
196 }
197
198out:
199 close(fd);
200 return;
201}
202
203static int hpet_sigio_count;
204
205static void
206hpet_sigio(int val)
207{
208 fprintf(stderr, "hpet_sigio: called\n");
209 hpet_sigio_count++;
210}
211
212void
213hpet_fasync(int argc, const char **argv)
214{
215 unsigned long freq;
216 int iterations, i, fd, value;
217 sig_t oldsig;
218 struct hpet_info info;
219
220 hpet_sigio_count = 0;
221 fd = -1;
222
223 if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) {
224 fprintf(stderr, "hpet_fasync: failed to set signal handler\n");
225 return;
226 }
227
228 if (argc != 3) {
229 fprintf(stderr, "hpet_fasync: device-name freq iterations\n");
230 goto out;
231 }
232
233 fd = open(argv[0], O_RDONLY);
234
235 if (fd < 0) {
236 fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]);
237 return;
238 }
239
240
241 if ((fcntl(fd, F_SETOWN, getpid()) == 1) ||
242 ((value = fcntl(fd, F_GETFL)) == 1) ||
243 (fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) {
244 fprintf(stderr, "hpet_fasync: fcntl failed\n");
245 goto out;
246 }
247
248 freq = atoi(argv[1]);
249 iterations = atoi(argv[2]);
250
251 if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
252 fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n");
253 goto out;
254 }
255
256 if (ioctl(fd, HPET_INFO, &info) < 0) {
257 fprintf(stderr, "hpet_fasync: failed to get info\n");
258 goto out;
259 }
260
261 fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags);
262
263 if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
264 fprintf(stderr, "hpet_fasync: HPET_EPI failed\n");
265 goto out;
266 }
267
268 if (ioctl(fd, HPET_IE_ON, 0) < 0) {
269 fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n");
270 goto out;
271 }
272
273 for (i = 0; i < iterations; i++) {
274 (void) pause();
275 fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count);
276 }
277
278out:
279 signal(SIGIO, oldsig);
280
281 if (fd >= 0)
282 close(fd);
283
284 return;
285}
286
287The kernel API has three interfaces exported from the driver:
288
289 hpet_register(struct hpet_task *tp, int periodic)
290 hpet_unregister(struct hpet_task *tp)
291 hpet_control(struct hpet_task *tp, unsigned int cmd, unsigned long arg)
292
293The kernel module using this interface fills in the ht_func and ht_data members of the
294hpet_task structure before calling hpet_register. hpet_control simply vectors to the hpet_ioctl
295routine and has the same commands and respective arguments as the user API. hpet_unregister
296is used to terminate usage of the HPET timer reserved by hpet_register.
297
298