GNU Linux-libre 4.19.207-gnu1
[releases.git] / tools / testing / selftests / timers / rtcpie.c
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 #include "../kselftest.h"
22
23 /*
24  * This expects the new RTC class driver framework, working with
25  * clocks that will often not be clones of what the PC-AT had.
26  * Use the command line to specify another RTC if you need one.
27  */
28 static const char default_rtc[] = "/dev/rtc0";
29
30 int main(int argc, char **argv)
31 {
32         int i, fd, retval, irqcount = 0;
33         unsigned long tmp, data, old_pie_rate;
34         const char *rtc = default_rtc;
35         struct timeval start, end, diff;
36
37         switch (argc) {
38         case 2:
39                 rtc = argv[1];
40                 break;
41         case 1:
42                 fd = open(default_rtc, O_RDONLY);
43                 if (fd == -1) {
44                         printf("Default RTC %s does not exist. Test Skipped!\n", default_rtc);
45                         exit(KSFT_SKIP);
46                 }
47                 close(fd);
48                 break;
49         default:
50                 fprintf(stderr, "usage:  rtctest [rtcdev] [d]\n");
51                 return 1;
52         }
53
54         fd = open(rtc, O_RDONLY);
55
56         if (fd ==  -1) {
57                 perror(rtc);
58                 exit(errno);
59         }
60
61         /* Read periodic IRQ rate */
62         retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate);
63         if (retval == -1) {
64                 /* not all RTCs support periodic IRQs */
65                 if (errno == EINVAL) {
66                         fprintf(stderr, "\nNo periodic IRQ support\n");
67                         goto done;
68                 }
69                 perror("RTC_IRQP_READ ioctl");
70                 exit(errno);
71         }
72         fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate);
73
74         fprintf(stderr, "Counting 20 interrupts at:");
75         fflush(stderr);
76
77         /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
78         for (tmp=2; tmp<=64; tmp*=2) {
79
80                 retval = ioctl(fd, RTC_IRQP_SET, tmp);
81                 if (retval == -1) {
82                         /* not all RTCs can change their periodic IRQ rate */
83                         if (errno == EINVAL) {
84                                 fprintf(stderr,
85                                         "\n...Periodic IRQ rate is fixed\n");
86                                 goto done;
87                         }
88                         perror("RTC_IRQP_SET ioctl");
89                         exit(errno);
90                 }
91
92                 fprintf(stderr, "\n%ldHz:\t", tmp);
93                 fflush(stderr);
94
95                 /* Enable periodic interrupts */
96                 retval = ioctl(fd, RTC_PIE_ON, 0);
97                 if (retval == -1) {
98                         perror("RTC_PIE_ON ioctl");
99                         exit(errno);
100                 }
101
102                 for (i=1; i<21; i++) {
103                         gettimeofday(&start, NULL);
104                         /* This blocks */
105                         retval = read(fd, &data, sizeof(unsigned long));
106                         if (retval == -1) {
107                                 perror("read");
108                                 exit(errno);
109                         }
110                         gettimeofday(&end, NULL);
111                         timersub(&end, &start, &diff);
112                         if (diff.tv_sec > 0 ||
113                             diff.tv_usec > ((1000000L / tmp) * 1.10)) {
114                                 fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n",
115                                        diff.tv_sec, diff.tv_usec,
116                                        (1000000L / tmp));
117                                 fflush(stdout);
118                                 exit(-1);
119                         }
120
121                         fprintf(stderr, " %d",i);
122                         fflush(stderr);
123                         irqcount++;
124                 }
125
126                 /* Disable periodic interrupts */
127                 retval = ioctl(fd, RTC_PIE_OFF, 0);
128                 if (retval == -1) {
129                         perror("RTC_PIE_OFF ioctl");
130                         exit(errno);
131                 }
132         }
133
134 done:
135         ioctl(fd, RTC_IRQP_SET, old_pie_rate);
136
137         fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
138
139         close(fd);
140
141         return 0;
142 }