18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Real Time Clock Periodic Interrupt test program 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Since commit 6610e0893b8bc ("RTC: Rework RTC code to use timerqueue for 68c2ecf20Sopenharmony_ci * events"), PIE are completely handled using hrtimers, without actually using 78c2ecf20Sopenharmony_ci * any underlying hardware RTC. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <stdio.h> 128c2ecf20Sopenharmony_ci#include <linux/rtc.h> 138c2ecf20Sopenharmony_ci#include <sys/ioctl.h> 148c2ecf20Sopenharmony_ci#include <sys/time.h> 158c2ecf20Sopenharmony_ci#include <sys/types.h> 168c2ecf20Sopenharmony_ci#include <fcntl.h> 178c2ecf20Sopenharmony_ci#include <unistd.h> 188c2ecf20Sopenharmony_ci#include <stdlib.h> 198c2ecf20Sopenharmony_ci#include <errno.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include "../kselftest.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* 248c2ecf20Sopenharmony_ci * This expects the new RTC class driver framework, working with 258c2ecf20Sopenharmony_ci * clocks that will often not be clones of what the PC-AT had. 268c2ecf20Sopenharmony_ci * Use the command line to specify another RTC if you need one. 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_cistatic const char default_rtc[] = "/dev/rtc0"; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciint main(int argc, char **argv) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci int i, fd, retval, irqcount = 0; 338c2ecf20Sopenharmony_ci unsigned long tmp, data, old_pie_rate; 348c2ecf20Sopenharmony_ci const char *rtc = default_rtc; 358c2ecf20Sopenharmony_ci struct timeval start, end, diff; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci switch (argc) { 388c2ecf20Sopenharmony_ci case 2: 398c2ecf20Sopenharmony_ci rtc = argv[1]; 408c2ecf20Sopenharmony_ci break; 418c2ecf20Sopenharmony_ci case 1: 428c2ecf20Sopenharmony_ci fd = open(default_rtc, O_RDONLY); 438c2ecf20Sopenharmony_ci if (fd == -1) { 448c2ecf20Sopenharmony_ci printf("Default RTC %s does not exist. Test Skipped!\n", default_rtc); 458c2ecf20Sopenharmony_ci exit(KSFT_SKIP); 468c2ecf20Sopenharmony_ci } 478c2ecf20Sopenharmony_ci close(fd); 488c2ecf20Sopenharmony_ci break; 498c2ecf20Sopenharmony_ci default: 508c2ecf20Sopenharmony_ci fprintf(stderr, "usage: rtctest [rtcdev] [d]\n"); 518c2ecf20Sopenharmony_ci return 1; 528c2ecf20Sopenharmony_ci } 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci fd = open(rtc, O_RDONLY); 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci if (fd == -1) { 578c2ecf20Sopenharmony_ci perror(rtc); 588c2ecf20Sopenharmony_ci exit(errno); 598c2ecf20Sopenharmony_ci } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Read periodic IRQ rate */ 628c2ecf20Sopenharmony_ci retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate); 638c2ecf20Sopenharmony_ci if (retval == -1) { 648c2ecf20Sopenharmony_ci /* not all RTCs support periodic IRQs */ 658c2ecf20Sopenharmony_ci if (errno == EINVAL) { 668c2ecf20Sopenharmony_ci fprintf(stderr, "\nNo periodic IRQ support\n"); 678c2ecf20Sopenharmony_ci goto done; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci perror("RTC_IRQP_READ ioctl"); 708c2ecf20Sopenharmony_ci exit(errno); 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci fprintf(stderr, "Counting 20 interrupts at:"); 758c2ecf20Sopenharmony_ci fflush(stderr); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ 788c2ecf20Sopenharmony_ci for (tmp=2; tmp<=64; tmp*=2) { 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci retval = ioctl(fd, RTC_IRQP_SET, tmp); 818c2ecf20Sopenharmony_ci if (retval == -1) { 828c2ecf20Sopenharmony_ci /* not all RTCs can change their periodic IRQ rate */ 838c2ecf20Sopenharmony_ci if (errno == EINVAL) { 848c2ecf20Sopenharmony_ci fprintf(stderr, 858c2ecf20Sopenharmony_ci "\n...Periodic IRQ rate is fixed\n"); 868c2ecf20Sopenharmony_ci goto done; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci perror("RTC_IRQP_SET ioctl"); 898c2ecf20Sopenharmony_ci exit(errno); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci fprintf(stderr, "\n%ldHz:\t", tmp); 938c2ecf20Sopenharmony_ci fflush(stderr); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci /* Enable periodic interrupts */ 968c2ecf20Sopenharmony_ci retval = ioctl(fd, RTC_PIE_ON, 0); 978c2ecf20Sopenharmony_ci if (retval == -1) { 988c2ecf20Sopenharmony_ci perror("RTC_PIE_ON ioctl"); 998c2ecf20Sopenharmony_ci exit(errno); 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci for (i=1; i<21; i++) { 1038c2ecf20Sopenharmony_ci gettimeofday(&start, NULL); 1048c2ecf20Sopenharmony_ci /* This blocks */ 1058c2ecf20Sopenharmony_ci retval = read(fd, &data, sizeof(unsigned long)); 1068c2ecf20Sopenharmony_ci if (retval == -1) { 1078c2ecf20Sopenharmony_ci perror("read"); 1088c2ecf20Sopenharmony_ci exit(errno); 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci gettimeofday(&end, NULL); 1118c2ecf20Sopenharmony_ci timersub(&end, &start, &diff); 1128c2ecf20Sopenharmony_ci if (diff.tv_sec > 0 || 1138c2ecf20Sopenharmony_ci diff.tv_usec > ((1000000L / tmp) * 1.10)) { 1148c2ecf20Sopenharmony_ci fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", 1158c2ecf20Sopenharmony_ci diff.tv_sec, diff.tv_usec, 1168c2ecf20Sopenharmony_ci (1000000L / tmp)); 1178c2ecf20Sopenharmony_ci fflush(stdout); 1188c2ecf20Sopenharmony_ci exit(-1); 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci fprintf(stderr, " %d",i); 1228c2ecf20Sopenharmony_ci fflush(stderr); 1238c2ecf20Sopenharmony_ci irqcount++; 1248c2ecf20Sopenharmony_ci } 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Disable periodic interrupts */ 1278c2ecf20Sopenharmony_ci retval = ioctl(fd, RTC_PIE_OFF, 0); 1288c2ecf20Sopenharmony_ci if (retval == -1) { 1298c2ecf20Sopenharmony_ci perror("RTC_PIE_OFF ioctl"); 1308c2ecf20Sopenharmony_ci exit(errno); 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci } 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cidone: 1358c2ecf20Sopenharmony_ci ioctl(fd, RTC_IRQP_SET, old_pie_rate); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci close(fd); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci} 143