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 21843b20bcSAlexandre Belloni /* 22843b20bcSAlexandre Belloni * This expects the new RTC class driver framework, working with 23843b20bcSAlexandre Belloni * clocks that will often not be clones of what the PC-AT had. 24843b20bcSAlexandre Belloni * Use the command line to specify another RTC if you need one. 25843b20bcSAlexandre Belloni */ 26843b20bcSAlexandre Belloni static const char default_rtc[] = "/dev/rtc0"; 27843b20bcSAlexandre Belloni 28843b20bcSAlexandre Belloni int main(int argc, char **argv) 29843b20bcSAlexandre Belloni { 30843b20bcSAlexandre Belloni int i, fd, retval, irqcount = 0; 31843b20bcSAlexandre Belloni unsigned long tmp, data; 32843b20bcSAlexandre Belloni const char *rtc = default_rtc; 33843b20bcSAlexandre Belloni struct timeval start, end, diff; 34843b20bcSAlexandre Belloni 35843b20bcSAlexandre Belloni switch (argc) { 36843b20bcSAlexandre Belloni case 2: 37843b20bcSAlexandre Belloni rtc = argv[1]; 38843b20bcSAlexandre Belloni /* FALLTHROUGH */ 39843b20bcSAlexandre Belloni case 1: 40843b20bcSAlexandre Belloni break; 41843b20bcSAlexandre Belloni default: 42843b20bcSAlexandre Belloni fprintf(stderr, "usage: rtctest [rtcdev] [d]\n"); 43843b20bcSAlexandre Belloni return 1; 44843b20bcSAlexandre Belloni } 45843b20bcSAlexandre Belloni 46843b20bcSAlexandre Belloni fd = open(rtc, O_RDONLY); 47843b20bcSAlexandre Belloni 48843b20bcSAlexandre Belloni if (fd == -1) { 49843b20bcSAlexandre Belloni perror(rtc); 50843b20bcSAlexandre Belloni exit(errno); 51843b20bcSAlexandre Belloni } 52843b20bcSAlexandre Belloni 53843b20bcSAlexandre Belloni /* Read periodic IRQ rate */ 54843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_IRQP_READ, &tmp); 55843b20bcSAlexandre Belloni if (retval == -1) { 56843b20bcSAlexandre Belloni /* not all RTCs support periodic IRQs */ 57843b20bcSAlexandre Belloni if (errno == EINVAL) { 58843b20bcSAlexandre Belloni fprintf(stderr, "\nNo periodic IRQ support\n"); 59843b20bcSAlexandre Belloni goto done; 60843b20bcSAlexandre Belloni } 61843b20bcSAlexandre Belloni perror("RTC_IRQP_READ ioctl"); 62843b20bcSAlexandre Belloni exit(errno); 63843b20bcSAlexandre Belloni } 64843b20bcSAlexandre Belloni fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp); 65843b20bcSAlexandre Belloni 66843b20bcSAlexandre Belloni fprintf(stderr, "Counting 20 interrupts at:"); 67843b20bcSAlexandre Belloni fflush(stderr); 68843b20bcSAlexandre Belloni 69843b20bcSAlexandre Belloni /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ 70843b20bcSAlexandre Belloni for (tmp=2; tmp<=64; tmp*=2) { 71843b20bcSAlexandre Belloni 72843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_IRQP_SET, tmp); 73843b20bcSAlexandre Belloni if (retval == -1) { 74843b20bcSAlexandre Belloni /* not all RTCs can change their periodic IRQ rate */ 75843b20bcSAlexandre Belloni if (errno == EINVAL) { 76843b20bcSAlexandre Belloni fprintf(stderr, 77843b20bcSAlexandre Belloni "\n...Periodic IRQ rate is fixed\n"); 78843b20bcSAlexandre Belloni goto done; 79843b20bcSAlexandre Belloni } 80843b20bcSAlexandre Belloni perror("RTC_IRQP_SET ioctl"); 81843b20bcSAlexandre Belloni exit(errno); 82843b20bcSAlexandre Belloni } 83843b20bcSAlexandre Belloni 84843b20bcSAlexandre Belloni fprintf(stderr, "\n%ldHz:\t", tmp); 85843b20bcSAlexandre Belloni fflush(stderr); 86843b20bcSAlexandre Belloni 87843b20bcSAlexandre Belloni /* Enable periodic interrupts */ 88843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_PIE_ON, 0); 89843b20bcSAlexandre Belloni if (retval == -1) { 90843b20bcSAlexandre Belloni perror("RTC_PIE_ON ioctl"); 91843b20bcSAlexandre Belloni exit(errno); 92843b20bcSAlexandre Belloni } 93843b20bcSAlexandre Belloni 94843b20bcSAlexandre Belloni for (i=1; i<21; i++) { 95843b20bcSAlexandre Belloni gettimeofday(&start, NULL); 96843b20bcSAlexandre Belloni /* This blocks */ 97843b20bcSAlexandre Belloni retval = read(fd, &data, sizeof(unsigned long)); 98843b20bcSAlexandre Belloni if (retval == -1) { 99843b20bcSAlexandre Belloni perror("read"); 100843b20bcSAlexandre Belloni exit(errno); 101843b20bcSAlexandre Belloni } 102843b20bcSAlexandre Belloni gettimeofday(&end, NULL); 103843b20bcSAlexandre Belloni timersub(&end, &start, &diff); 104843b20bcSAlexandre Belloni if (diff.tv_sec > 0 || 105843b20bcSAlexandre Belloni diff.tv_usec > ((1000000L / tmp) * 1.10)) { 106843b20bcSAlexandre Belloni fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", 107843b20bcSAlexandre Belloni diff.tv_sec, diff.tv_usec, 108843b20bcSAlexandre Belloni (1000000L / tmp)); 109843b20bcSAlexandre Belloni fflush(stdout); 110843b20bcSAlexandre Belloni exit(-1); 111843b20bcSAlexandre Belloni } 112843b20bcSAlexandre Belloni 113843b20bcSAlexandre Belloni fprintf(stderr, " %d",i); 114843b20bcSAlexandre Belloni fflush(stderr); 115843b20bcSAlexandre Belloni irqcount++; 116843b20bcSAlexandre Belloni } 117843b20bcSAlexandre Belloni 118843b20bcSAlexandre Belloni /* Disable periodic interrupts */ 119843b20bcSAlexandre Belloni retval = ioctl(fd, RTC_PIE_OFF, 0); 120843b20bcSAlexandre Belloni if (retval == -1) { 121843b20bcSAlexandre Belloni perror("RTC_PIE_OFF ioctl"); 122843b20bcSAlexandre Belloni exit(errno); 123843b20bcSAlexandre Belloni } 124843b20bcSAlexandre Belloni } 125843b20bcSAlexandre Belloni 126843b20bcSAlexandre Belloni done: 127843b20bcSAlexandre Belloni fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); 128843b20bcSAlexandre Belloni 129843b20bcSAlexandre Belloni close(fd); 130843b20bcSAlexandre Belloni 131843b20bcSAlexandre Belloni return 0; 132843b20bcSAlexandre Belloni } 133