1843b20bcSAlexandre Belloni // SPDX-License-Identifier: GPL-2.0
2843b20bcSAlexandre Belloni /*
3843b20bcSAlexandre Belloni * Real Time Clock Periodic Interrupt test program
4843b20bcSAlexandre Belloni *
5843b20bcSAlexandre Belloni * Since commit 6610e0893b8bc ("RTC: Rework RTC code to use timerqueue for
6843b20bcSAlexandre Belloni * events"), PIE are completely handled using hrtimers, without actually using
7843b20bcSAlexandre Belloni * any underlying hardware RTC.
8843b20bcSAlexandre Belloni *
9843b20bcSAlexandre Belloni */
10843b20bcSAlexandre Belloni
11843b20bcSAlexandre Belloni #include <stdio.h>
12843b20bcSAlexandre Belloni #include <linux/rtc.h>
13843b20bcSAlexandre Belloni #include <sys/ioctl.h>
14843b20bcSAlexandre Belloni #include <sys/time.h>
15843b20bcSAlexandre Belloni #include <sys/types.h>
16843b20bcSAlexandre Belloni #include <fcntl.h>
17843b20bcSAlexandre Belloni #include <unistd.h>
18843b20bcSAlexandre Belloni #include <stdlib.h>
19843b20bcSAlexandre Belloni #include <errno.h>
20843b20bcSAlexandre Belloni
21*0d3e5a05SPo-Hsu Lin #include "../kselftest.h"
22*0d3e5a05SPo-Hsu Lin
23843b20bcSAlexandre Belloni /*
24843b20bcSAlexandre Belloni * This expects the new RTC class driver framework, working with
25843b20bcSAlexandre Belloni * clocks that will often not be clones of what the PC-AT had.
26843b20bcSAlexandre Belloni * Use the command line to specify another RTC if you need one.
27843b20bcSAlexandre Belloni */
28843b20bcSAlexandre Belloni static const char default_rtc[] = "/dev/rtc0";
29843b20bcSAlexandre Belloni
main(int argc,char ** argv)30843b20bcSAlexandre Belloni int main(int argc, char **argv)
31843b20bcSAlexandre Belloni {
32843b20bcSAlexandre Belloni int i, fd, retval, irqcount = 0;
336d73ceabSAlexandre Belloni unsigned long tmp, data, old_pie_rate;
34843b20bcSAlexandre Belloni const char *rtc = default_rtc;
35843b20bcSAlexandre Belloni struct timeval start, end, diff;
36843b20bcSAlexandre Belloni
37843b20bcSAlexandre Belloni switch (argc) {
38843b20bcSAlexandre Belloni case 2:
39843b20bcSAlexandre Belloni rtc = argv[1];
40*0d3e5a05SPo-Hsu Lin break;
41843b20bcSAlexandre Belloni case 1:
42*0d3e5a05SPo-Hsu Lin fd = open(default_rtc, O_RDONLY);
43*0d3e5a05SPo-Hsu Lin if (fd == -1) {
44*0d3e5a05SPo-Hsu Lin printf("Default RTC %s does not exist. Test Skipped!\n", default_rtc);
45*0d3e5a05SPo-Hsu Lin exit(KSFT_SKIP);
46*0d3e5a05SPo-Hsu Lin }
47*0d3e5a05SPo-Hsu Lin close(fd);
48843b20bcSAlexandre Belloni break;
49843b20bcSAlexandre Belloni default:
50843b20bcSAlexandre Belloni fprintf(stderr, "usage: rtctest [rtcdev] [d]\n");
51843b20bcSAlexandre Belloni return 1;
52843b20bcSAlexandre Belloni }
53843b20bcSAlexandre Belloni
54843b20bcSAlexandre Belloni fd = open(rtc, O_RDONLY);
55843b20bcSAlexandre Belloni
56843b20bcSAlexandre Belloni if (fd == -1) {
57843b20bcSAlexandre Belloni perror(rtc);
58843b20bcSAlexandre Belloni exit(errno);
59843b20bcSAlexandre Belloni }
60843b20bcSAlexandre Belloni
61843b20bcSAlexandre Belloni /* Read periodic IRQ rate */
626d73ceabSAlexandre Belloni retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate);
63843b20bcSAlexandre Belloni if (retval == -1) {
64843b20bcSAlexandre Belloni /* not all RTCs support periodic IRQs */
65843b20bcSAlexandre Belloni if (errno == EINVAL) {
66843b20bcSAlexandre Belloni fprintf(stderr, "\nNo periodic IRQ support\n");
67843b20bcSAlexandre Belloni goto done;
68843b20bcSAlexandre Belloni }
69843b20bcSAlexandre Belloni perror("RTC_IRQP_READ ioctl");
70843b20bcSAlexandre Belloni exit(errno);
71843b20bcSAlexandre Belloni }
726d73ceabSAlexandre Belloni fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate);
73843b20bcSAlexandre Belloni
74843b20bcSAlexandre Belloni fprintf(stderr, "Counting 20 interrupts at:");
75843b20bcSAlexandre Belloni fflush(stderr);
76843b20bcSAlexandre Belloni
77843b20bcSAlexandre Belloni /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
78843b20bcSAlexandre Belloni for (tmp=2; tmp<=64; tmp*=2) {
79843b20bcSAlexandre Belloni
80843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_IRQP_SET, tmp);
81843b20bcSAlexandre Belloni if (retval == -1) {
82843b20bcSAlexandre Belloni /* not all RTCs can change their periodic IRQ rate */
83843b20bcSAlexandre Belloni if (errno == EINVAL) {
84843b20bcSAlexandre Belloni fprintf(stderr,
85843b20bcSAlexandre Belloni "\n...Periodic IRQ rate is fixed\n");
86843b20bcSAlexandre Belloni goto done;
87843b20bcSAlexandre Belloni }
88843b20bcSAlexandre Belloni perror("RTC_IRQP_SET ioctl");
89843b20bcSAlexandre Belloni exit(errno);
90843b20bcSAlexandre Belloni }
91843b20bcSAlexandre Belloni
92843b20bcSAlexandre Belloni fprintf(stderr, "\n%ldHz:\t", tmp);
93843b20bcSAlexandre Belloni fflush(stderr);
94843b20bcSAlexandre Belloni
95843b20bcSAlexandre Belloni /* Enable periodic interrupts */
96843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_PIE_ON, 0);
97843b20bcSAlexandre Belloni if (retval == -1) {
98843b20bcSAlexandre Belloni perror("RTC_PIE_ON ioctl");
99843b20bcSAlexandre Belloni exit(errno);
100843b20bcSAlexandre Belloni }
101843b20bcSAlexandre Belloni
102843b20bcSAlexandre Belloni for (i=1; i<21; i++) {
103843b20bcSAlexandre Belloni gettimeofday(&start, NULL);
104843b20bcSAlexandre Belloni /* This blocks */
105843b20bcSAlexandre Belloni retval = read(fd, &data, sizeof(unsigned long));
106843b20bcSAlexandre Belloni if (retval == -1) {
107843b20bcSAlexandre Belloni perror("read");
108843b20bcSAlexandre Belloni exit(errno);
109843b20bcSAlexandre Belloni }
110843b20bcSAlexandre Belloni gettimeofday(&end, NULL);
111843b20bcSAlexandre Belloni timersub(&end, &start, &diff);
112843b20bcSAlexandre Belloni if (diff.tv_sec > 0 ||
113843b20bcSAlexandre Belloni diff.tv_usec > ((1000000L / tmp) * 1.10)) {
114843b20bcSAlexandre Belloni fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n",
115843b20bcSAlexandre Belloni diff.tv_sec, diff.tv_usec,
116843b20bcSAlexandre Belloni (1000000L / tmp));
117843b20bcSAlexandre Belloni fflush(stdout);
118843b20bcSAlexandre Belloni exit(-1);
119843b20bcSAlexandre Belloni }
120843b20bcSAlexandre Belloni
121843b20bcSAlexandre Belloni fprintf(stderr, " %d",i);
122843b20bcSAlexandre Belloni fflush(stderr);
123843b20bcSAlexandre Belloni irqcount++;
124843b20bcSAlexandre Belloni }
125843b20bcSAlexandre Belloni
126843b20bcSAlexandre Belloni /* Disable periodic interrupts */
127843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_PIE_OFF, 0);
128843b20bcSAlexandre Belloni if (retval == -1) {
129843b20bcSAlexandre Belloni perror("RTC_PIE_OFF ioctl");
130843b20bcSAlexandre Belloni exit(errno);
131843b20bcSAlexandre Belloni }
132843b20bcSAlexandre Belloni }
133843b20bcSAlexandre Belloni
134843b20bcSAlexandre Belloni done:
1356d73ceabSAlexandre Belloni ioctl(fd, RTC_IRQP_SET, old_pie_rate);
1366d73ceabSAlexandre Belloni
137843b20bcSAlexandre Belloni fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
138843b20bcSAlexandre Belloni
139843b20bcSAlexandre Belloni close(fd);
140843b20bcSAlexandre Belloni
141843b20bcSAlexandre Belloni return 0;
142843b20bcSAlexandre Belloni }
143