1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Real Time Clock Periodic Interrupt test program
4 *
5 * Since commit 6610e0893b8bc ("RTC: Rework RTC code to use timerqueue for
6 * events"), PIE are completely handled using hrtimers, without actually using
7 * any underlying hardware RTC.
8 *
9 */
10
11 #include <stdio.h>
12 #include <linux/rtc.h>
13 #include <sys/ioctl.h>
14 #include <sys/time.h>
15 #include <sys/types.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <stdlib.h>
19 #include <errno.h>
20
21 /*
22 * This expects the new RTC class driver framework, working with
23 * clocks that will often not be clones of what the PC-AT had.
24 * Use the command line to specify another RTC if you need one.
25 */
26 static const char default_rtc[] = "/dev/rtc0";
27
main(int argc,char ** argv)28 int main(int argc, char **argv)
29 {
30 int i, fd, retval, irqcount = 0;
31 unsigned long tmp, data, old_pie_rate;
32 const char *rtc = default_rtc;
33 struct timeval start, end, diff;
34
35 switch (argc) {
36 case 2:
37 rtc = argv[1];
38 /* FALLTHROUGH */
39 case 1:
40 break;
41 default:
42 fprintf(stderr, "usage: rtctest [rtcdev] [d]\n");
43 return 1;
44 }
45
46 fd = open(rtc, O_RDONLY);
47
48 if (fd == -1) {
49 perror(rtc);
50 exit(errno);
51 }
52
53 /* Read periodic IRQ rate */
54 retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate);
55 if (retval == -1) {
56 /* not all RTCs support periodic IRQs */
57 if (errno == EINVAL) {
58 fprintf(stderr, "\nNo periodic IRQ support\n");
59 goto done;
60 }
61 perror("RTC_IRQP_READ ioctl");
62 exit(errno);
63 }
64 fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate);
65
66 fprintf(stderr, "Counting 20 interrupts at:");
67 fflush(stderr);
68
69 /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
70 for (tmp=2; tmp<=64; tmp*=2) {
71
72 retval = ioctl(fd, RTC_IRQP_SET, tmp);
73 if (retval == -1) {
74 /* not all RTCs can change their periodic IRQ rate */
75 if (errno == EINVAL) {
76 fprintf(stderr,
77 "\n...Periodic IRQ rate is fixed\n");
78 goto done;
79 }
80 perror("RTC_IRQP_SET ioctl");
81 exit(errno);
82 }
83
84 fprintf(stderr, "\n%ldHz:\t", tmp);
85 fflush(stderr);
86
87 /* Enable periodic interrupts */
88 retval = ioctl(fd, RTC_PIE_ON, 0);
89 if (retval == -1) {
90 perror("RTC_PIE_ON ioctl");
91 exit(errno);
92 }
93
94 for (i=1; i<21; i++) {
95 gettimeofday(&start, NULL);
96 /* This blocks */
97 retval = read(fd, &data, sizeof(unsigned long));
98 if (retval == -1) {
99 perror("read");
100 exit(errno);
101 }
102 gettimeofday(&end, NULL);
103 timersub(&end, &start, &diff);
104 if (diff.tv_sec > 0 ||
105 diff.tv_usec > ((1000000L / tmp) * 1.10)) {
106 fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n",
107 diff.tv_sec, diff.tv_usec,
108 (1000000L / tmp));
109 fflush(stdout);
110 exit(-1);
111 }
112
113 fprintf(stderr, " %d",i);
114 fflush(stderr);
115 irqcount++;
116 }
117
118 /* Disable periodic interrupts */
119 retval = ioctl(fd, RTC_PIE_OFF, 0);
120 if (retval == -1) {
121 perror("RTC_PIE_OFF ioctl");
122 exit(errno);
123 }
124 }
125
126 done:
127 ioctl(fd, RTC_IRQP_SET, old_pie_rate);
128
129 fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
130
131 close(fd);
132
133 return 0;
134 }
135