linux/tools/testing/selftests/timers/rtcpie.c
<<
>>
Prefs
   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 */
  28static const char default_rtc[] = "/dev/rtc0";
  29
  30int 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
 134done:
 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