mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-25 02:09:42 +00:00
![David Brownell](/assets/img/avatar_default.png)
Minor /dev/hpet updates and bugfixes: * Remove dead code, mostly remnants of an incomplete/unusable kernel interface ... noted when addressing "sparse" warnings: + hpet_unregister() and a routine it calls + hpet_task and all references, including hpet_task_lock + hpet_data.hd_flags (and HPET_DATA_PLATFORM) * Correct and improve boot message: + displays *counter* (shared between comparators) bit width, not *timer* bit widths (which are often mixed) + relabel "timers" as "comparators"; this is less confusing, they are not independent like normal timers are (sigh) + display MHz not Hz; it's never less than 10 MHz. * Tighten and correct the userspace interface code + don't accidentally program comparators in 64-bit mode using 32-bit values ... always force comparators into 32-bit mode + provide the correct bit definition flagging comparators with periodic capability ... the ABI is unchanged * Update Documentation/hpet.txt + be more correct and current + expand description a bit + don't mention that now-gone kernel interface Plus, add a FIXME comment for something that could cause big trouble on systems with more capable HPETs than at least Intel seems to ship. It seems that few folk use this userspace interface; it's not very usable given the general lack of HPET IRQ routing. I'm told that the only real point of it any more is to mmap for fast timestamps; IMO that's handled better through the gettimeofday() vsyscall. Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Acked-by: Clemens Ladisch <clemens@ladisch.de> Signed-off-by: Ingo Molnar <mingo@elte.hu>
299 lines
6.4 KiB
Text
299 lines
6.4 KiB
Text
High Precision Event Timer Driver for Linux
|
|
|
|
The High Precision Event Timer (HPET) hardware follows a specification
|
|
by Intel and Microsoft which can be found at
|
|
|
|
http://www.intel.com/technology/architecture/hpetspec.htm
|
|
|
|
Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision")
|
|
and up to 32 comparators. Normally three or more comparators are provided,
|
|
each of which can generate oneshot interupts and at least one of which has
|
|
additional hardware to support periodic interrupts. The comparators are
|
|
also called "timers", which can be misleading since usually timers are
|
|
independent of each other ... these share a counter, complicating resets.
|
|
|
|
HPET devices can support two interrupt routing modes. In one mode, the
|
|
comparators are additional interrupt sources with no particular system
|
|
role. Many x86 BIOS writers don't route HPET interrupts at all, which
|
|
prevents use of that mode. They support the other "legacy replacement"
|
|
mode where the first two comparators block interrupts from 8254 timers
|
|
and from the RTC.
|
|
|
|
The driver supports detection of HPET driver allocation and initialization
|
|
of the HPET before the driver module_init routine is called. This enables
|
|
platform code which uses timer 0 or 1 as the main timer to intercept HPET
|
|
initialization. An example of this initialization can be found in
|
|
arch/x86/kernel/hpet.c.
|
|
|
|
The driver provides a userspace API which resembles the API found in the
|
|
RTC driver framework. An example user space program is provided below.
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <memory.h>
|
|
#include <malloc.h>
|
|
#include <time.h>
|
|
#include <ctype.h>
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/time.h>
|
|
#include <linux/hpet.h>
|
|
|
|
|
|
extern void hpet_open_close(int, const char **);
|
|
extern void hpet_info(int, const char **);
|
|
extern void hpet_poll(int, const char **);
|
|
extern void hpet_fasync(int, const char **);
|
|
extern void hpet_read(int, const char **);
|
|
|
|
#include <sys/poll.h>
|
|
#include <sys/ioctl.h>
|
|
#include <signal.h>
|
|
|
|
struct hpet_command {
|
|
char *command;
|
|
void (*func)(int argc, const char ** argv);
|
|
} hpet_command[] = {
|
|
{
|
|
"open-close",
|
|
hpet_open_close
|
|
},
|
|
{
|
|
"info",
|
|
hpet_info
|
|
},
|
|
{
|
|
"poll",
|
|
hpet_poll
|
|
},
|
|
{
|
|
"fasync",
|
|
hpet_fasync
|
|
},
|
|
};
|
|
|
|
int
|
|
main(int argc, const char ** argv)
|
|
{
|
|
int i;
|
|
|
|
argc--;
|
|
argv++;
|
|
|
|
if (!argc) {
|
|
fprintf(stderr, "-hpet: requires command\n");
|
|
return -1;
|
|
}
|
|
|
|
|
|
for (i = 0; i < (sizeof (hpet_command) / sizeof (hpet_command[0])); i++)
|
|
if (!strcmp(argv[0], hpet_command[i].command)) {
|
|
argc--;
|
|
argv++;
|
|
fprintf(stderr, "-hpet: executing %s\n",
|
|
hpet_command[i].command);
|
|
hpet_command[i].func(argc, argv);
|
|
return 0;
|
|
}
|
|
|
|
fprintf(stderr, "do_hpet: command %s not implemented\n", argv[0]);
|
|
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
hpet_open_close(int argc, const char **argv)
|
|
{
|
|
int fd;
|
|
|
|
if (argc != 1) {
|
|
fprintf(stderr, "hpet_open_close: device-name\n");
|
|
return;
|
|
}
|
|
|
|
fd = open(argv[0], O_RDONLY);
|
|
if (fd < 0)
|
|
fprintf(stderr, "hpet_open_close: open failed\n");
|
|
else
|
|
close(fd);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
hpet_info(int argc, const char **argv)
|
|
{
|
|
}
|
|
|
|
void
|
|
hpet_poll(int argc, const char **argv)
|
|
{
|
|
unsigned long freq;
|
|
int iterations, i, fd;
|
|
struct pollfd pfd;
|
|
struct hpet_info info;
|
|
struct timeval stv, etv;
|
|
struct timezone tz;
|
|
long usec;
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "hpet_poll: device-name freq iterations\n");
|
|
return;
|
|
}
|
|
|
|
freq = atoi(argv[1]);
|
|
iterations = atoi(argv[2]);
|
|
|
|
fd = open(argv[0], O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
fprintf(stderr, "hpet_poll: open of %s failed\n", argv[0]);
|
|
return;
|
|
}
|
|
|
|
if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
|
|
fprintf(stderr, "hpet_poll: HPET_IRQFREQ failed\n");
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, HPET_INFO, &info) < 0) {
|
|
fprintf(stderr, "hpet_poll: failed to get info\n");
|
|
goto out;
|
|
}
|
|
|
|
fprintf(stderr, "hpet_poll: info.hi_flags 0x%lx\n", info.hi_flags);
|
|
|
|
if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
|
|
fprintf(stderr, "hpet_poll: HPET_EPI failed\n");
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, HPET_IE_ON, 0) < 0) {
|
|
fprintf(stderr, "hpet_poll, HPET_IE_ON failed\n");
|
|
goto out;
|
|
}
|
|
|
|
pfd.fd = fd;
|
|
pfd.events = POLLIN;
|
|
|
|
for (i = 0; i < iterations; i++) {
|
|
pfd.revents = 0;
|
|
gettimeofday(&stv, &tz);
|
|
if (poll(&pfd, 1, -1) < 0)
|
|
fprintf(stderr, "hpet_poll: poll failed\n");
|
|
else {
|
|
long data;
|
|
|
|
gettimeofday(&etv, &tz);
|
|
usec = stv.tv_sec * 1000000 + stv.tv_usec;
|
|
usec = (etv.tv_sec * 1000000 + etv.tv_usec) - usec;
|
|
|
|
fprintf(stderr,
|
|
"hpet_poll: expired time = 0x%lx\n", usec);
|
|
|
|
fprintf(stderr, "hpet_poll: revents = 0x%x\n",
|
|
pfd.revents);
|
|
|
|
if (read(fd, &data, sizeof(data)) != sizeof(data)) {
|
|
fprintf(stderr, "hpet_poll: read failed\n");
|
|
}
|
|
else
|
|
fprintf(stderr, "hpet_poll: data 0x%lx\n",
|
|
data);
|
|
}
|
|
}
|
|
|
|
out:
|
|
close(fd);
|
|
return;
|
|
}
|
|
|
|
static int hpet_sigio_count;
|
|
|
|
static void
|
|
hpet_sigio(int val)
|
|
{
|
|
fprintf(stderr, "hpet_sigio: called\n");
|
|
hpet_sigio_count++;
|
|
}
|
|
|
|
void
|
|
hpet_fasync(int argc, const char **argv)
|
|
{
|
|
unsigned long freq;
|
|
int iterations, i, fd, value;
|
|
sig_t oldsig;
|
|
struct hpet_info info;
|
|
|
|
hpet_sigio_count = 0;
|
|
fd = -1;
|
|
|
|
if ((oldsig = signal(SIGIO, hpet_sigio)) == SIG_ERR) {
|
|
fprintf(stderr, "hpet_fasync: failed to set signal handler\n");
|
|
return;
|
|
}
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "hpet_fasync: device-name freq iterations\n");
|
|
goto out;
|
|
}
|
|
|
|
fd = open(argv[0], O_RDONLY);
|
|
|
|
if (fd < 0) {
|
|
fprintf(stderr, "hpet_fasync: failed to open %s\n", argv[0]);
|
|
return;
|
|
}
|
|
|
|
|
|
if ((fcntl(fd, F_SETOWN, getpid()) == 1) ||
|
|
((value = fcntl(fd, F_GETFL)) == 1) ||
|
|
(fcntl(fd, F_SETFL, value | O_ASYNC) == 1)) {
|
|
fprintf(stderr, "hpet_fasync: fcntl failed\n");
|
|
goto out;
|
|
}
|
|
|
|
freq = atoi(argv[1]);
|
|
iterations = atoi(argv[2]);
|
|
|
|
if (ioctl(fd, HPET_IRQFREQ, freq) < 0) {
|
|
fprintf(stderr, "hpet_fasync: HPET_IRQFREQ failed\n");
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, HPET_INFO, &info) < 0) {
|
|
fprintf(stderr, "hpet_fasync: failed to get info\n");
|
|
goto out;
|
|
}
|
|
|
|
fprintf(stderr, "hpet_fasync: info.hi_flags 0x%lx\n", info.hi_flags);
|
|
|
|
if (info.hi_flags && (ioctl(fd, HPET_EPI, 0) < 0)) {
|
|
fprintf(stderr, "hpet_fasync: HPET_EPI failed\n");
|
|
goto out;
|
|
}
|
|
|
|
if (ioctl(fd, HPET_IE_ON, 0) < 0) {
|
|
fprintf(stderr, "hpet_fasync, HPET_IE_ON failed\n");
|
|
goto out;
|
|
}
|
|
|
|
for (i = 0; i < iterations; i++) {
|
|
(void) pause();
|
|
fprintf(stderr, "hpet_fasync: count = %d\n", hpet_sigio_count);
|
|
}
|
|
|
|
out:
|
|
signal(SIGIO, oldsig);
|
|
|
|
if (fd >= 0)
|
|
close(fd);
|
|
|
|
return;
|
|
}
|