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