| 1 | 199 | simons |  
 | 
      
         | 2 |  |  |         Real Time Clock Driver for Linux
 | 
      
         | 3 |  |  |         ================================
 | 
      
         | 4 |  |  |  
 | 
      
         | 5 |  |  | All PCs (even Alpha machines) have a Real Time Clock built into them.
 | 
      
         | 6 |  |  | Usually they are built into the chipset of the computer, but some may
 | 
      
         | 7 |  |  | actually have a Motorola MC146818 (or clone) on the board. This is the
 | 
      
         | 8 |  |  | clock that keeps the date and time while your computer is turned off.
 | 
      
         | 9 |  |  |  
 | 
      
         | 10 |  |  | However it can also be used to generate signals from a slow 2Hz to a
 | 
      
         | 11 |  |  | relatively fast 8192Hz, in increments of powers of two. These signals
 | 
      
         | 12 |  |  | are reported by interrupt number 8. (Oh! So *that* is what IRQ 8 is
 | 
      
         | 13 |  |  | for...) It can also function as a 24hr alarm, raising IRQ 8 when the
 | 
      
         | 14 |  |  | alarm goes off. The alarm can also be programmed to only check any
 | 
      
         | 15 |  |  | subset of the three programmable values, meaning that it could be set to
 | 
      
         | 16 |  |  | ring on the 30th second of the 30th minute of every hour, for example.
 | 
      
         | 17 |  |  | The clock can also be set to generate an interrupt upon every clock
 | 
      
         | 18 |  |  | update, thus generating a 1Hz signal.
 | 
      
         | 19 |  |  |  
 | 
      
         | 20 |  |  | The interrupts are reported via /dev/rtc (major 10, minor 135, read only
 | 
      
         | 21 |  |  | character device) in the form of an unsigned long. The low byte contains
 | 
      
         | 22 |  |  | the type of interrupt (update-done, alarm-rang, or periodic) that was
 | 
      
         | 23 |  |  | raised, and the remaining bytes contain the number of interrupts since
 | 
      
         | 24 |  |  | the last read.  Status information is reported through the pseudo-file
 | 
      
         | 25 |  |  | /proc/rtc if the /proc filesystem was enabled. The driver has built in
 | 
      
         | 26 |  |  | locking so that only one process is allowed to have the /dev/rtc
 | 
      
         | 27 |  |  | interface open at a time.
 | 
      
         | 28 |  |  |  
 | 
      
         | 29 |  |  | A user process can monitor these interrupts by doing a read(2) or a
 | 
      
         | 30 |  |  | select(2) on /dev/rtc -- either will block/stop the user process until
 | 
      
         | 31 |  |  | the next interrupt is received. This is useful for things like
 | 
      
         | 32 |  |  | reasonably high frequency data acquisition where one doesn't want to
 | 
      
         | 33 |  |  | burn up 100% CPU by polling gettimeofday etc. etc.
 | 
      
         | 34 |  |  |  
 | 
      
         | 35 |  |  | At high frequencies, or under high loads, the user process should check
 | 
      
         | 36 |  |  | the number of interrupts received since the last read to determine if
 | 
      
         | 37 |  |  | there has been any interrupt "pileup" so to speak. Just for reference, a
 | 
      
         | 38 |  |  | typical 486-33 running a tight read loop on /dev/rtc will start to suffer
 | 
      
         | 39 |  |  | occasional interrupt pileup (i.e. > 1 IRQ event since last read) for
 | 
      
         | 40 |  |  | frequencies above 1024Hz. So you really should check the high bytes
 | 
      
         | 41 |  |  | of the value you read, especially at frequencies above that of the
 | 
      
         | 42 |  |  | normal timer interrupt, which is 100Hz.
 | 
      
         | 43 |  |  |  
 | 
      
         | 44 |  |  | Programming and/or enabling interrupt frequencies greater than 64Hz is
 | 
      
         | 45 |  |  | only allowed by root. This is perhaps a bit conservative, but we don't want
 | 
      
         | 46 |  |  | an evil user generating lots of IRQs on a slow 386sx-16, where it might have
 | 
      
         | 47 |  |  | a negative impact on performance.  Note that the interrupt handler is only
 | 
      
         | 48 |  |  | a few lines of code to minimize any possibility of this effect.
 | 
      
         | 49 |  |  |  
 | 
      
         | 50 |  |  | Also, if the kernel time is synchronized with an external source, the
 | 
      
         | 51 |  |  | kernel will write the time back to the CMOS clock every 11 minutes. In
 | 
      
         | 52 |  |  | the process of doing this, the kernel briefly turns off RTC periodic
 | 
      
         | 53 |  |  | interrupts, so be aware of this if you are doing serious work. If you
 | 
      
         | 54 |  |  | don't synchronize the kernel time with an external source (via ntp or
 | 
      
         | 55 |  |  | whatever) then the kernel will keep its hands off the RTC, allowing you
 | 
      
         | 56 |  |  | exclusive access to the device for your applications.
 | 
      
         | 57 |  |  |  
 | 
      
         | 58 |  |  | The alarm and/or interrupt frequency are programmed into the RTC via
 | 
      
         | 59 |  |  | various ioctl(2) calls as listed in ./include/linux/mc146818rtc.h
 | 
      
         | 60 |  |  | Rather than write 50 pages describing the ioctl() and so on, it is
 | 
      
         | 61 |  |  | perhaps more useful to include a small test program that demonstrates
 | 
      
         | 62 |  |  | how to use them, and demonstrates the features of the driver. This is
 | 
      
         | 63 |  |  | probably a lot more useful to people interested in writing applications
 | 
      
         | 64 |  |  | that will be using this driver.
 | 
      
         | 65 |  |  |  
 | 
      
         | 66 |  |  |                                                 Paul Gortmaker
 | 
      
         | 67 |  |  |  
 | 
      
         | 68 |  |  | Update in version 1.09
 | 
      
         | 69 |  |  | ======================
 | 
      
         | 70 |  |  |  
 | 
      
         | 71 |  |  | Epoch handling is added.  Epoch is the number which should be added to the
 | 
      
         | 72 |  |  | value of the clock's year register to get the actual year.  The default
 | 
      
         | 73 |  |  | Linux epoch is therefore 1900.
 | 
      
         | 74 |  |  |  
 | 
      
         | 75 |  |  | Epochs are especially useful on Alphas where different operating systems
 | 
      
         | 76 |  |  | use different epochs, and Linux wants to be compatible with all of them.
 | 
      
         | 77 |  |  | They may eventually be helpful on Intel architecture as well, where a
 | 
      
         | 78 |  |  | value of an RTC register cannot exceed 99 due to BCD tradition originated
 | 
      
         | 79 |  |  | from DOS.
 | 
      
         | 80 |  |  |  
 | 
      
         | 81 |  |  | When the epoch is set to 1900, the new code behaves exactly like the old
 | 
      
         | 82 |  |  | one with respect to the BCD wrapping: values 00 - 69 are treated as if
 | 
      
         | 83 |  |  | they were 100 - 169.  That means that after the 2000th year epoch 1900
 | 
      
         | 84 |  |  | will be the same as epoch 2000.
 | 
      
         | 85 |  |  |  
 | 
      
         | 86 |  |  | Two new ioctls are introduced to read and set the epoch, RTC_EPOCH_READ
 | 
      
         | 87 |  |  | and RTC_EPOCH_SET.  They can be used in exactly the same manner as
 | 
      
         | 88 |  |  | RTC_IRQP_READ and RTC_IRQP_SET, so they are not included in the example
 | 
      
         | 89 |  |  | program below.
 | 
      
         | 90 |  |  |  
 | 
      
         | 91 |  |  | On Alphas an epoch autodetection is performed.  Currently 3 epochs
 | 
      
         | 92 |  |  | are recognised: Linux (1900), Digital UNIX (1952) and Windows NT (1980).
 | 
      
         | 93 |  |  |  
 | 
      
         | 94 |  |  | Nikita Schmidt  
 | 
      
         | 95 |  |  |  
 | 
      
         | 96 |  |  |  
 | 
      
         | 97 |  |  | -------------------- 8< ---------------- 8< -----------------------------
 | 
      
         | 98 |  |  |  
 | 
      
         | 99 |  |  | /*
 | 
      
         | 100 |  |  |  *      Real Time Clock Driver Test/Example Program
 | 
      
         | 101 |  |  |  *
 | 
      
         | 102 |  |  |  *      Compile with:
 | 
      
         | 103 |  |  |  *              gcc -s -Wall -Wstrict-prototypes rtctest.c -o rtctest
 | 
      
         | 104 |  |  |  *
 | 
      
         | 105 |  |  |  *      Copyright (C) 1996, Paul Gortmaker.
 | 
      
         | 106 |  |  |  *
 | 
      
         | 107 |  |  |  *      Released under the GNU General Public License, version 2,
 | 
      
         | 108 |  |  |  *      included herein by reference.
 | 
      
         | 109 |  |  |  *
 | 
      
         | 110 |  |  |  */
 | 
      
         | 111 |  |  |  
 | 
      
         | 112 |  |  | #include 
 | 
      
         | 113 |  |  | #include 
 | 
      
         | 114 |  |  | #include 
 | 
      
         | 115 |  |  | #include 
 | 
      
         | 116 |  |  | #include 
 | 
      
         | 117 |  |  | #include 
 | 
      
         | 118 |  |  | #include 
 | 
      
         | 119 |  |  | #include 
 | 
      
         | 120 |  |  |  
 | 
      
         | 121 |  |  | void main(void) {
 | 
      
         | 122 |  |  |  
 | 
      
         | 123 |  |  | int i, fd, retval, irqcount = 0;
 | 
      
         | 124 |  |  | unsigned long tmp, data;
 | 
      
         | 125 |  |  | struct rtc_time rtc_tm;
 | 
      
         | 126 |  |  |  
 | 
      
         | 127 |  |  | fd = open ("/dev/rtc", O_RDONLY);
 | 
      
         | 128 |  |  |  
 | 
      
         | 129 |  |  | if (fd ==  -1) {
 | 
      
         | 130 |  |  |         perror("/dev/rtc");
 | 
      
         | 131 |  |  |         exit(errno);
 | 
      
         | 132 |  |  | }
 | 
      
         | 133 |  |  |  
 | 
      
         | 134 |  |  | fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
 | 
      
         | 135 |  |  |  
 | 
      
         | 136 |  |  | /* Turn on update interrupts (one per second) */
 | 
      
         | 137 |  |  | retval = ioctl(fd, RTC_UIE_ON, 0);
 | 
      
         | 138 |  |  | if (retval == -1) {
 | 
      
         | 139 |  |  |         perror("ioctl");
 | 
      
         | 140 |  |  |         exit(errno);
 | 
      
         | 141 |  |  | }
 | 
      
         | 142 |  |  |  
 | 
      
         | 143 |  |  | fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading /dev/rtc:");
 | 
      
         | 144 |  |  | fflush(stderr);
 | 
      
         | 145 |  |  | for (i=1; i<6; i++) {
 | 
      
         | 146 |  |  |         /* This read will block */
 | 
      
         | 147 |  |  |         retval = read(fd, &data, sizeof(unsigned long));
 | 
      
         | 148 |  |  |         if (retval == -1) {
 | 
      
         | 149 |  |  |                 perror("read");
 | 
      
         | 150 |  |  |                 exit(errno);
 | 
      
         | 151 |  |  |         }
 | 
      
         | 152 |  |  |         fprintf(stderr, " %d",i);
 | 
      
         | 153 |  |  |         fflush(stderr);
 | 
      
         | 154 |  |  |         irqcount++;
 | 
      
         | 155 |  |  | }
 | 
      
         | 156 |  |  |  
 | 
      
         | 157 |  |  | fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
 | 
      
         | 158 |  |  | fflush(stderr);
 | 
      
         | 159 |  |  | for (i=1; i<6; i++) {
 | 
      
         | 160 |  |  |         struct timeval tv = {5, 0};     /* 5 second timeout on select */
 | 
      
         | 161 |  |  |         fd_set readfds;
 | 
      
         | 162 |  |  |  
 | 
      
         | 163 |  |  |         FD_ZERO(&readfds);
 | 
      
         | 164 |  |  |         FD_SET(fd, &readfds);
 | 
      
         | 165 |  |  |         /* The select will wait until an RTC interrupt happens. */
 | 
      
         | 166 |  |  |         retval = select(fd+1, &readfds, NULL, NULL, &tv);
 | 
      
         | 167 |  |  |         if (retval == -1) {
 | 
      
         | 168 |  |  |                 perror("select");
 | 
      
         | 169 |  |  |                 exit(errno);
 | 
      
         | 170 |  |  |         }
 | 
      
         | 171 |  |  |         /* This read won't block unlike the select-less case above. */
 | 
      
         | 172 |  |  |         retval = read(fd, &data, sizeof(unsigned long));
 | 
      
         | 173 |  |  |         if (retval == -1) {
 | 
      
         | 174 |  |  |                 perror("read");
 | 
      
         | 175 |  |  |                 exit(errno);
 | 
      
         | 176 |  |  |         }
 | 
      
         | 177 |  |  |         fprintf(stderr, " %d",i);
 | 
      
         | 178 |  |  |         fflush(stderr);
 | 
      
         | 179 |  |  |         irqcount++;
 | 
      
         | 180 |  |  | }
 | 
      
         | 181 |  |  |  
 | 
      
         | 182 |  |  | /* Turn off update interrupts */
 | 
      
         | 183 |  |  | retval = ioctl(fd, RTC_UIE_OFF, 0);
 | 
      
         | 184 |  |  | if (retval == -1) {
 | 
      
         | 185 |  |  |         perror("ioctl");
 | 
      
         | 186 |  |  |         exit(errno);
 | 
      
         | 187 |  |  | }
 | 
      
         | 188 |  |  |  
 | 
      
         | 189 |  |  | /* Read the RTC time/date */
 | 
      
         | 190 |  |  | retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
 | 
      
         | 191 |  |  | if (retval == -1) {
 | 
      
         | 192 |  |  |         perror("ioctl");
 | 
      
         | 193 |  |  |         exit(errno);
 | 
      
         | 194 |  |  | }
 | 
      
         | 195 |  |  |  
 | 
      
         | 196 |  |  | fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n",
 | 
      
         | 197 |  |  |         rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,
 | 
      
         | 198 |  |  |         rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
 | 
      
         | 199 |  |  |  
 | 
      
         | 200 |  |  | /* Set the alarm to 5 sec in the future, and check for rollover */
 | 
      
         | 201 |  |  | rtc_tm.tm_sec += 5;
 | 
      
         | 202 |  |  | if (rtc_tm.tm_sec >= 60) {
 | 
      
         | 203 |  |  |         rtc_tm.tm_sec %= 60;
 | 
      
         | 204 |  |  |         rtc_tm.tm_min++;
 | 
      
         | 205 |  |  | }
 | 
      
         | 206 |  |  | if  (rtc_tm.tm_min == 60) {
 | 
      
         | 207 |  |  |         rtc_tm.tm_min = 0;
 | 
      
         | 208 |  |  |         rtc_tm.tm_hour++;
 | 
      
         | 209 |  |  | }
 | 
      
         | 210 |  |  | if  (rtc_tm.tm_hour == 24)
 | 
      
         | 211 |  |  |         rtc_tm.tm_hour = 0;
 | 
      
         | 212 |  |  |  
 | 
      
         | 213 |  |  | retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
 | 
      
         | 214 |  |  | if (retval == -1) {
 | 
      
         | 215 |  |  |         perror("ioctl");
 | 
      
         | 216 |  |  |         exit(errno);
 | 
      
         | 217 |  |  | }
 | 
      
         | 218 |  |  |  
 | 
      
         | 219 |  |  | /* Read the current alarm settings */
 | 
      
         | 220 |  |  | retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
 | 
      
         | 221 |  |  | if (retval == -1) {
 | 
      
         | 222 |  |  |         perror("ioctl");
 | 
      
         | 223 |  |  |         exit(errno);
 | 
      
         | 224 |  |  | }
 | 
      
         | 225 |  |  |  
 | 
      
         | 226 |  |  | fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",
 | 
      
         | 227 |  |  |         rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
 | 
      
         | 228 |  |  |  
 | 
      
         | 229 |  |  | /* Enable alarm interrupts */
 | 
      
         | 230 |  |  | retval = ioctl(fd, RTC_AIE_ON, 0);
 | 
      
         | 231 |  |  | if (retval == -1) {
 | 
      
         | 232 |  |  |         perror("ioctl");
 | 
      
         | 233 |  |  |         exit(errno);
 | 
      
         | 234 |  |  | }
 | 
      
         | 235 |  |  |  
 | 
      
         | 236 |  |  | fprintf(stderr, "Waiting 5 seconds for alarm...");
 | 
      
         | 237 |  |  | fflush(stderr);
 | 
      
         | 238 |  |  | /* This blocks until the alarm ring causes an interrupt */
 | 
      
         | 239 |  |  | retval = read(fd, &data, sizeof(unsigned long));
 | 
      
         | 240 |  |  | if (retval == -1) {
 | 
      
         | 241 |  |  |         perror("read");
 | 
      
         | 242 |  |  |         exit(errno);
 | 
      
         | 243 |  |  | }
 | 
      
         | 244 |  |  | irqcount++;
 | 
      
         | 245 |  |  | fprintf(stderr, " okay. Alarm rang.\n");
 | 
      
         | 246 |  |  |  
 | 
      
         | 247 |  |  | /* Disable alarm interrupts */
 | 
      
         | 248 |  |  | retval = ioctl(fd, RTC_AIE_OFF, 0);
 | 
      
         | 249 |  |  | if (retval == -1) {
 | 
      
         | 250 |  |  |         perror("ioctl");
 | 
      
         | 251 |  |  |         exit(errno);
 | 
      
         | 252 |  |  | }
 | 
      
         | 253 |  |  |  
 | 
      
         | 254 |  |  | /* Read periodic IRQ rate */
 | 
      
         | 255 |  |  | retval = ioctl(fd, RTC_IRQP_READ, &tmp);
 | 
      
         | 256 |  |  | if (retval == -1) {
 | 
      
         | 257 |  |  |         perror("ioctl");
 | 
      
         | 258 |  |  |         exit(errno);
 | 
      
         | 259 |  |  | }
 | 
      
         | 260 |  |  | fprintf(stderr, "\nPeriodic IRQ rate was %ldHz.\n", tmp);
 | 
      
         | 261 |  |  |  
 | 
      
         | 262 |  |  | fprintf(stderr, "Counting 20 interrupts at:");
 | 
      
         | 263 |  |  | fflush(stderr);
 | 
      
         | 264 |  |  |  
 | 
      
         | 265 |  |  | /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
 | 
      
         | 266 |  |  | for (tmp=2; tmp<=64; tmp*=2) {
 | 
      
         | 267 |  |  |  
 | 
      
         | 268 |  |  |         retval = ioctl(fd, RTC_IRQP_SET, tmp);
 | 
      
         | 269 |  |  |         if (retval == -1) {
 | 
      
         | 270 |  |  |                 perror("ioctl");
 | 
      
         | 271 |  |  |                 exit(errno);
 | 
      
         | 272 |  |  |         }
 | 
      
         | 273 |  |  |  
 | 
      
         | 274 |  |  |         fprintf(stderr, "\n%ldHz:\t", tmp);
 | 
      
         | 275 |  |  |         fflush(stderr);
 | 
      
         | 276 |  |  |  
 | 
      
         | 277 |  |  |         /* Enable periodic interrupts */
 | 
      
         | 278 |  |  |         retval = ioctl(fd, RTC_PIE_ON, 0);
 | 
      
         | 279 |  |  |         if (retval == -1) {
 | 
      
         | 280 |  |  |                 perror("ioctl");
 | 
      
         | 281 |  |  |                 exit(errno);
 | 
      
         | 282 |  |  |         }
 | 
      
         | 283 |  |  |  
 | 
      
         | 284 |  |  |         for (i=1; i<21; i++) {
 | 
      
         | 285 |  |  |                 /* This blocks */
 | 
      
         | 286 |  |  |                 retval = read(fd, &data, sizeof(unsigned long));
 | 
      
         | 287 |  |  |                 if (retval == -1) {
 | 
      
         | 288 |  |  |                         perror("read");
 | 
      
         | 289 |  |  |                         exit(errno);
 | 
      
         | 290 |  |  |                 }
 | 
      
         | 291 |  |  |                 fprintf(stderr, " %d",i);
 | 
      
         | 292 |  |  |                 fflush(stderr);
 | 
      
         | 293 |  |  |                 irqcount++;
 | 
      
         | 294 |  |  |         }
 | 
      
         | 295 |  |  |  
 | 
      
         | 296 |  |  |         /* Disable periodic interrupts */
 | 
      
         | 297 |  |  |         retval = ioctl(fd, RTC_PIE_OFF, 0);
 | 
      
         | 298 |  |  |         if (retval == -1) {
 | 
      
         | 299 |  |  |                 perror("ioctl");
 | 
      
         | 300 |  |  |                 exit(errno);
 | 
      
         | 301 |  |  |         }
 | 
      
         | 302 |  |  | }
 | 
      
         | 303 |  |  |  
 | 
      
         | 304 |  |  | fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
 | 
      
         | 305 |  |  | fprintf(stderr, "\nTyping \"cat /proc/interrupts\" will show %d more events on IRQ 8.\n\n",
 | 
      
         | 306 |  |  |                                                                  irqcount);
 | 
      
         | 307 |  |  |  
 | 
      
         | 308 |  |  | close(fd);
 | 
      
         | 309 |  |  |  
 | 
      
         | 310 |  |  | } /* end main */
 |