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