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/*
  22 * This expects the new RTC class driver framework, working with
  23 * clocks that will often not be clones of what the PC-AT had.
  24 * Use the command line to specify another RTC if you need one.
  25 */
  26static const char default_rtc[] = "/dev/rtc0";
  27
  28int main(int argc, char **argv)
  29{
  30        int i, fd, retval, irqcount = 0;
  31        unsigned long tmp, data, old_pie_rate;
  32        const char *rtc = default_rtc;
  33        struct timeval start, end, diff;
  34
  35        switch (argc) {
  36        case 2:
  37                rtc = argv[1];
  38                /* FALLTHROUGH */
  39        case 1:
  40                break;
  41        default:
  42                fprintf(stderr, "usage:  rtctest [rtcdev] [d]\n");
  43                return 1;
  44        }
  45
  46        fd = open(rtc, O_RDONLY);
  47
  48        if (fd ==  -1) {
  49                perror(rtc);
  50                exit(errno);
  51        }
  52
  53        /* Read periodic IRQ rate */
  54        retval = ioctl(fd, RTC_IRQP_READ, &old_pie_rate);
  55        if (retval == -1) {
  56                /* not all RTCs support periodic IRQs */
  57                if (errno == EINVAL) {
  58                        fprintf(stderr, "\nNo periodic IRQ support\n");
  59                        goto done;
  60                }
  61                perror("RTC_IRQP_READ ioctl");
  62                exit(errno);
  63        }
  64        fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", old_pie_rate);
  65
  66        fprintf(stderr, "Counting 20 interrupts at:");
  67        fflush(stderr);
  68
  69        /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
  70        for (tmp=2; tmp<=64; tmp*=2) {
  71
  72                retval = ioctl(fd, RTC_IRQP_SET, tmp);
  73                if (retval == -1) {
  74                        /* not all RTCs can change their periodic IRQ rate */
  75                        if (errno == EINVAL) {
  76                                fprintf(stderr,
  77                                        "\n...Periodic IRQ rate is fixed\n");
  78                                goto done;
  79                        }
  80                        perror("RTC_IRQP_SET ioctl");
  81                        exit(errno);
  82                }
  83
  84                fprintf(stderr, "\n%ldHz:\t", tmp);
  85                fflush(stderr);
  86
  87                /* Enable periodic interrupts */
  88                retval = ioctl(fd, RTC_PIE_ON, 0);
  89                if (retval == -1) {
  90                        perror("RTC_PIE_ON ioctl");
  91                        exit(errno);
  92                }
  93
  94                for (i=1; i<21; i++) {
  95                        gettimeofday(&start, NULL);
  96                        /* This blocks */
  97                        retval = read(fd, &data, sizeof(unsigned long));
  98                        if (retval == -1) {
  99                                perror("read");
 100                                exit(errno);
 101                        }
 102                        gettimeofday(&end, NULL);
 103                        timersub(&end, &start, &diff);
 104                        if (diff.tv_sec > 0 ||
 105                            diff.tv_usec > ((1000000L / tmp) * 1.10)) {
 106                                fprintf(stderr, "\nPIE delta error: %ld.%06ld should be close to 0.%06ld\n",
 107                                       diff.tv_sec, diff.tv_usec,
 108                                       (1000000L / tmp));
 109                                fflush(stdout);
 110                                exit(-1);
 111                        }
 112
 113                        fprintf(stderr, " %d",i);
 114                        fflush(stderr);
 115                        irqcount++;
 116                }
 117
 118                /* Disable periodic interrupts */
 119                retval = ioctl(fd, RTC_PIE_OFF, 0);
 120                if (retval == -1) {
 121                        perror("RTC_PIE_OFF ioctl");
 122                        exit(errno);
 123                }
 124        }
 125
 126done:
 127        ioctl(fd, RTC_IRQP_SET, old_pie_rate);
 128
 129        fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
 130
 131        close(fd);
 132
 133        return 0;
 134}
 135