162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Real Time Clock Periodic Interrupt test program 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Since commit 6610e0893b8bc ("RTC: Rework RTC code to use timerqueue for 662306a36Sopenharmony_ci * events"), PIE are completely handled using hrtimers, without actually using 762306a36Sopenharmony_ci * any underlying hardware RTC. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <linux/rtc.h> 1362306a36Sopenharmony_ci#include <sys/ioctl.h> 1462306a36Sopenharmony_ci#include <sys/time.h> 1562306a36Sopenharmony_ci#include <sys/types.h> 1662306a36Sopenharmony_ci#include <fcntl.h> 1762306a36Sopenharmony_ci#include <unistd.h> 1862306a36Sopenharmony_ci#include <stdlib.h> 1962306a36Sopenharmony_ci#include <errno.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "../kselftest.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/* 2462306a36Sopenharmony_ci * This expects the new RTC class driver framework, working with 2562306a36Sopenharmony_ci * clocks that will often not be clones of what the PC-AT had. 2662306a36Sopenharmony_ci * Use the command line to specify another RTC if you need one. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic const char default_rtc[] = "/dev/rtc0"; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciint main(int argc, char **argv) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci int i, fd, retval, irqcount = 0; 3362306a36Sopenharmony_ci unsigned long tmp, data, old_pie_rate; 3462306a36Sopenharmony_ci const char *rtc = default_rtc; 3562306a36Sopenharmony_ci struct timeval start, end, diff; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci switch (argc) { 3862306a36Sopenharmony_ci case 2: 3962306a36Sopenharmony_ci rtc = argv[1]; 4062306a36Sopenharmony_ci break; 4162306a36Sopenharmony_ci case 1: 4262306a36Sopenharmony_ci fd = open(default_rtc, O_RDONLY); 4362306a36Sopenharmony_ci if (fd == -1) { 4462306a36Sopenharmony_ci printf("Default RTC %s does not exist. Test Skipped!\n", default_rtc); 4562306a36Sopenharmony_ci exit(KSFT_SKIP); 4662306a36Sopenharmony_ci } 4762306a36Sopenharmony_ci close(fd); 4862306a36Sopenharmony_ci break; 4962306a36Sopenharmony_ci default: 5062306a36Sopenharmony_ci fprintf(stderr, "usage: rtctest [rtcdev] [d]\n"); 5162306a36Sopenharmony_ci return 1; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci fd = open(rtc, O_RDONLY); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (fd == -1) { 5762306a36Sopenharmony_ci perror(rtc); 5862306a36Sopenharmony_ci exit(errno); 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Read periodic IRQ rate */ 6262306a36Sopenharmony_ci retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate); 6362306a36Sopenharmony_ci if (retval == -1) { 6462306a36Sopenharmony_ci /* not all RTCs support periodic IRQs */ 6562306a36Sopenharmony_ci if (errno == EINVAL) { 6662306a36Sopenharmony_ci fprintf(stderr, "\nNo periodic IRQ support\n"); 6762306a36Sopenharmony_ci goto done; 6862306a36Sopenharmony_ci } 6962306a36Sopenharmony_ci perror("RTC_IRQP_READ ioctl"); 7062306a36Sopenharmony_ci exit(errno); 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci fprintf(stderr, "Counting 20 interrupts at:"); 7562306a36Sopenharmony_ci fflush(stderr); 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */ 7862306a36Sopenharmony_ci for (tmp=2; tmp<=64; tmp*=2) { 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci retval = ioctl(fd, RTC_IRQP_SET, tmp); 8162306a36Sopenharmony_ci if (retval == -1) { 8262306a36Sopenharmony_ci /* not all RTCs can change their periodic IRQ rate */ 8362306a36Sopenharmony_ci if (errno == EINVAL) { 8462306a36Sopenharmony_ci fprintf(stderr, 8562306a36Sopenharmony_ci "\n...Periodic IRQ rate is fixed\n"); 8662306a36Sopenharmony_ci goto done; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci perror("RTC_IRQP_SET ioctl"); 8962306a36Sopenharmony_ci exit(errno); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci fprintf(stderr, "\n%ldHz:\t", tmp); 9362306a36Sopenharmony_ci fflush(stderr); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Enable periodic interrupts */ 9662306a36Sopenharmony_ci retval = ioctl(fd, RTC_PIE_ON, 0); 9762306a36Sopenharmony_ci if (retval == -1) { 9862306a36Sopenharmony_ci perror("RTC_PIE_ON ioctl"); 9962306a36Sopenharmony_ci exit(errno); 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci for (i=1; i<21; i++) { 10362306a36Sopenharmony_ci gettimeofday(&start, NULL); 10462306a36Sopenharmony_ci /* This blocks */ 10562306a36Sopenharmony_ci retval = read(fd, &data, sizeof(unsigned long)); 10662306a36Sopenharmony_ci if (retval == -1) { 10762306a36Sopenharmony_ci perror("read"); 10862306a36Sopenharmony_ci exit(errno); 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci gettimeofday(&end, NULL); 11162306a36Sopenharmony_ci timersub(&end, &start, &diff); 11262306a36Sopenharmony_ci if (diff.tv_sec > 0 || 11362306a36Sopenharmony_ci diff.tv_usec > ((1000000L / tmp) * 1.10)) { 11462306a36Sopenharmony_ci fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n", 11562306a36Sopenharmony_ci diff.tv_sec, diff.tv_usec, 11662306a36Sopenharmony_ci (1000000L / tmp)); 11762306a36Sopenharmony_ci fflush(stdout); 11862306a36Sopenharmony_ci exit(-1); 11962306a36Sopenharmony_ci } 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci fprintf(stderr, " %d",i); 12262306a36Sopenharmony_ci fflush(stderr); 12362306a36Sopenharmony_ci irqcount++; 12462306a36Sopenharmony_ci } 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci /* Disable periodic interrupts */ 12762306a36Sopenharmony_ci retval = ioctl(fd, RTC_PIE_OFF, 0); 12862306a36Sopenharmony_ci if (retval == -1) { 12962306a36Sopenharmony_ci perror("RTC_PIE_OFF ioctl"); 13062306a36Sopenharmony_ci exit(errno); 13162306a36Sopenharmony_ci } 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cidone: 13562306a36Sopenharmony_ci ioctl(fd, RTC_IRQP_SET, old_pie_rate); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci close(fd); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci return 0; 14262306a36Sopenharmony_ci} 143