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 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