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