Home > Articles > Operating Systems, Server > Linux/UNIX/Open Source

  • Print
  • + Share This
This chapter is from the book

7.4 System Clock: Of Time and Timers

For scheduling, the kernel uses the system clock to know how long a task has been running. We already covered the system clock in Chapter 5 by using it as an example for the discussion on interrupts. Here, we explore the Real-Time Clock and its uses and implementation; but first, let’s recap clocks in general.

The clock is a periodic signal applied to a processor, which allows it to function in the time domain. The processor depends on the clock signal to know when it can perform its next function, such as adding two integers or fetching data from memory. The speed of this clock signal (1.4GHz, 2GHz, and so on) has historically been used to compare the processing speed of systems at the local electronics store.

At any given moment, your system has several clocks and/or timers running. Simple examples include the time of day displayed in the bottom corner of your screen (otherwise known as wall time), the cursor patiently pulsing on a cluttered desktop, or your laptop screensaver taking over because of inactivity. More complicated examples of timekeeping include audio and video playback, key repeat (holding a key down), how fast communications ports run, and, as previously discussed, how long a task can run.

7.4.1 Real-Time Clock: What Time Is It?

The Linux interface to wall clock time is accomplished through the /dev/rtc device driver ioctl() function. The device for this driver is called a Real-Time Clock (RTC). The RTC9 provides timekeeping functions with a small 114-byte user NVRAM. The input to this device is a 32.768KHz oscillator and a connection for battery backup. Some discrete models of the RTC have the oscillator and battery built in, while other RTCs are now built in to the peripheral bus controller (for example, the Southbridge) of a processor chipset. The RTC not only reports the time of day, but it is also a programmable timer that is capable of interrupting the system. The frequency of interrupts varies from 2Hz to 8,192Hz. The RTC can also interrupt daily, like an alarm clock. Here, we explore the RTC code:

–----------------------------------------------------------------------
/include/linux/rtc.h

/*
 * ioctl calls that are permitted to the /dev/rtc interface, if
 * any of the RTC drivers are enabled.
 */

70 #define RTC_AIE_ON  _IO(’p’, 0x01) /* Alarm int. enable on */
71 #define RTC_AIE_OFF  _IO(’p’, 0x02) /* ... off  */
72 #define RTC_UIE_ON  _IO(’p’, 0x03) /* Update int. enable on */
73 #define RTC_UIE_OFF  _ IO(’p’, 0x04) /* ... off  */
74 #define RTC_PIE_ON  _IO(’p’, 0x05) /* Periodic int. enable on */
75 #define RTC_PIE_OFF  _IO(’p’, 0x06) /* ... off  */
76 #define RTC_WIE_ON  _IO(’p’, 0x0f) /* Watchdog int. enable on */
77 #define RTC_WIE_OFF  _IO(’p’, 0x10) /* ... off  */

78 #define RTC_ALM_SET  _IOW(’p’, 0x07, struct rtc_time) /* Set alarm time */
79 #define RTC_ALM_READ _IOR(’p’, 0x08, struct rtc_time) /* Read alarm time*/
80 #define RTC_RD_TIME  _IOR(’p’, 0x09, struct rtc_time) /* Read RTC time */
81 #define RTC_SET_TIME _IOW(’p’, 0x0a, struct rtc_time) /* Set RTC time */
82 #define RTC_IRQP_READ _IOR(’p’, 0x0b, unsigned long) /* Read IRQ rate*/
83 #define RTC_IRQP_SET _IOW(’p’, 0x0c, unsigned long) /* Set IRQ rate */
84 #define RTC_EPOCH_READ _IOR(’p’, 0x0d, unsigned long) /* Read epoch */
85 #define RTC_EPOCH_SET _IOW(’p’, 0x0e, unsigned long) /* Set epoch */
86 
87 #define RTC_WKALM_SET _IOW(’p’, 0x0f, struct rtc_wkalrm)/*Set wakeupalarm*/
88 #define RTC_WKALM_RD _IOR(’p’, 0x10, struct rtc_wkalrm)/*Get wakeupalarm*/
89 
90 #define RTC_PLL_GET  _IOR(’p’, 0x11, struct rtc_pll_info) /* Get PLL correction */
91 #define RTC_PLL_SET  _IOW(’p’, 0x12, struct rtc_pll_info) /* Set PLL correction */ 
-----------------------------------------------------------------------

The ioctl() control functions are listed in include/linux/rtc.h. At this writing, not all the ioctl() calls for the RTC are implemented for the PPC architecture. These control functions each call lower-level hardware-specific functions (if implemented). The example in this section uses the RTC_RD_TIME function.

The following is a sample ioctl() call to get the time of day. This program simply opens the driver and queries the RTC hardware for the current date and time, and prints the information to stderr. Note that only one user can access the RTC driver at a time. The code to enforce this is shown in the driver discussion.

–----------------------------------------------------------------------
Documentation/rtc.txt
/*
 * Trimmed down version of code in /Documentation/rtc.txt
 *
 */


int main(void) {

int fd, retval = 0;
//unsigned long tmp, data;
struct rtc_time rtc_tm;

fd = open ("/dev/rtc", O_RDONLY);


/* Read the RTC time/date */
retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);

/* print out the time from the rtc_tm variable */

close(fd);
return 0;

} /* end main */
------------------------------------------------------------------------

This code is a segment of a more complete example in /Documentation/ rtc.txt. The two main lines of code in this program are the open() command and the ioctl() call. open() tells us which driver we will use (/dev/rtc) and ioctl() indicates a specific path through the code down to the physical RTC interface by way of the RTC_RD_TIME command. The driver code for the open() command resides in the driver source, but its only significance to this discussion is which device driver was opened.

7.4.2 Reading the PPC Real-Time Clock

At kernel compile time, the appropriate code tree (x86, PPC, MIPS, and so on) is inserted. The source branch for PPC is discussed here in the source code file for the generic RTC driver for non-x86 systems:

–----------------------------------------------------------------------
/drivers/char/genrtc.c 
276 static int gen_rtc_ioctl(struct inode *inode, struct file *file,
277  unsigned int cmd, unsigned long arg)
278 {
279  struct rtc_time wtime;
280  struct rtc_pll_info pll;
281 
282  switch (cmd) {
283 
284  case RTC_PLL_GET:
... 
290  case RTC_PLL_SET:
...
298  case RTC_UIE_OFF: /* disable ints from RTC updates. */
...
302  case RTC_UIE_ON: /* enable ints for RTC updates. */
...
305  case RTC_RD_TIME: /* Read the time/date from RTC */
306  
307  memset(&wtime, 0, sizeof(wtime));
308  get_rtc_time(&wtime);
309 
310  return copy_to_user((void *)arg,&wtime,sizeof(wtime)) ? -EFAULT:0;
311
312  case RTC_SET_TIME: /* Set the RTC */
313  return -EINVAL;
314  }
...
353 static int gen_rtc_open(struct inode *inode, struct file *file)
354 {
355  if (gen_rtc_status & RTC_IS_OPEN)
356  return -EBUSY;
357  gen_rtc_status |= RTC_IS_OPEN;
------------------------------------------------------------------------

This code is the case statement for the ioctl command set. Because we made the ioctl call from the user space test program with the RTC_RD_TIME flag, control is transferred to line 305. The next call is at line 308, get_rtc_time(&wtime) in rtc.h (see the following code). Before leaving this code segment, note line 353. This allows only one user to access, via open(), the driver at a time by setting the status to RTC_IS_OPEN:

–----------------------------------------------------------------------
include/asm-ppc/rtc.h
045 static inline unsigned int get_rtc_time(struct rtc_time *time)
046 {
047  if (ppc_md.get_rtc_time) {
048   unsigned long nowtime;
049  
050  nowtime = (ppc_md.get_rtc_time)();
051  
052   to_tm(nowtime, time);
053  
054   time->tm_year -= 1900;
055 time->tm_mon -= 1; /* Make sure userland has a 0-based month */
056  }
057  return RTC_24H;
058 }
------------------------------------------------------------------------

The inline function get_rtc_time() calls the function that the structure variable pointed at by ppc_md.get_rtc_time on line 50. Early in the kernel initialization, this variable is set in chrp_setup.c:

–----------------------------------------------------------------------
arch/ppc/platforms/chrp_setup.c
447 chrp_init(unsigned long r3, unsigned long r4, unsigned long r5,
448 unsigned long r6, unsigned long r7)
449 {
...
477  ppc_md.time_init = chrp_time_init;
478  ppc_md.set_rtc_time = chrp_set_rtc_time;
479  ppc_md.get_rtc_time = chrp_get_rtc_time;
480  ppc_md.calibrate_decr = chrp_calibrate_decr;
------------------------------------------------------------------------

The function chrp_get_rtc_time() (on line 479) is defined in chrp_time.c in the following code segment. Because the time information in CMOS memory is updated on a periodic basis, the block of read code is enclosed in a for loop, which rereads the block if the update is in progress:

–----------------------------------------------------------------------
arch/ppc/platforms/chrp_time.c
122 unsigned long __chrp chrp_get_rtc_time(void)
123 {
124  unsigned int year, mon, day, hour, min, sec;
125  int uip, i;
... 
141  for ( i = 0; i<1000000; i++) {
142  uip = chrp_cmos_clock_read(RTC_FREQ_SELECT);
143  sec = chrp_cmos_clock_read(RTC_SECONDS);
144  min = chrp_cmos_clock_read(RTC_MINUTES);
145  hour = chrp_cmos_clock_read(RTC_HOURS);
146  day = chrp_cmos_clock_read(RTC_DAY_OF_MONTH);
147  mon = chrp_cmos_clock_read(RTC_MONTH);
148  year = chrp_cmos_clock_read(RTC_YEAR);
149  uip |= chrp_cmos_clock_read(RTC_FREQ_SELECT);
150  if ((uip & RTC_UIP)==0) break;
151  }
152  if (!(chrp_cmos_clock_read(RTC_CONTROL)
153  & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
154  {
155  BCD_TO_BIN(sec);
156  BCD_TO_BIN(min);
157  BCD_TO_BIN(hour);
158  BCD_TO_BIN(day);
159  BCD_TO_BIN(mon);
160  BCD_TO_BIN(year); 
161  }
...
054 int __chrp chrp_cmos_clock_read(int addr)
055 {  if (nvram_as1 != 0)
056  outb(addr>>8, nvram_as1);
057  outb(addr, nvram_as0);
058  return (inb(nvram_data));
059 }
------------------------------------------------------------------------

Finally, in chrp_get_rtc_time(), the values of the individual components of the time structure are read from the RTC device by using the function chrp_cmos_clock_read. These values are formatted and returned in the rtc_tm structure that was passed into the ioctl call back in the userland test program.

7.4.3 Reading the x86 Real-Time Clock

The methodology for reading the RTC on the x86 system is similar to, but somewhat more compact and robust than, the PPC method. Once again, we follow the open driver /dev/rtc, but this time, the build has compiled the file rtc.c for the x86 architecture. The source branch for x86 is discussed here:

–----------------------------------------------------------------------
drivers/char/rtc.c 
...
352 static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
353 {
...
switch (cmd) {
...
482 case RTC_RD_TIME: /* Read the time/date from RTC */
483 {
484  rtc_get_rtc_time(&wtime);
485  break;
486 }
...
1208 void rtc_get_rtc_time(struct rtc_time *rtc_tm)
1209 {
...
1238  spin_lock_irq(&rtc_lock);
1239  rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS);
1240  rtc_tm->tm_min = CMOS_READ(RTC_MINUTES);
1241  rtc_tm->tm_hour = CMOS_READ(RTC_HOURS);
1242  rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH);
1243  rtc_tm->tm_mon = CMOS_READ(RTC_MONTH);
1244  rtc_tm->tm_year = CMOS_READ(RTC_YEAR);
1245  ctrl = CMOS_READ(RTC_CONTROL);
...
1249 spin_unlock_irq(&rtc_lock);
1250
1251 if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
1252 {
1253  BCD_TO_BIN(rtc_tm->tm_sec);
1254  BCD_TO_BIN(rtc_tm->tm_min);
1255  BCD_TO_BIN(rtc_tm->tm_hour);
1256  BCD_TO_BIN(rtc_tm->tm_mday);
1257  BCD_TO_BIN(rtc_tm->tm_mon);
1258  BCD_TO_BIN(rtc_tm->tm_year);
1259 }
------------------------------------------------------------------------

The test program uses the ioctl() flag RTC_RD_TIME in its call to the driver rtc.c. The ioctl switch statement then fills the time structure from the CMOS memory of the RTC. Here is the x86 implementation of how the RTC hardware is read:

–----------------------------------------------------------------------
include/asm-i386/mc146818rtc.h 
...
018 #define CMOS_READ(addr) ({ \
019  outb_p((addr),RTC_PORT(0)); \
020  inb_p(RTC_PORT(1)); \
021 })
-----------------------------------------------------------------------
  • + Share This
  • 🔖 Save To Your Account