Section 7.4. System Clock: Of Time and Timers


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 RTC[9] 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:

[9] Manufactured by several vendors, most notably Motorola, with the mc146818. (This RTC is no longer in production. The Dallas DS12885 or equivalent is used instead.)

 ---------------------------------------------------------------------- /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  }) ----------------------------------------------------------------------- 




The Linux Kernel Primer. A Top-Down Approach for x86 and PowerPC Architectures
The Linux Kernel Primer. A Top-Down Approach for x86 and PowerPC Architectures
ISBN: 131181637
EAN: N/A
Year: 2005
Pages: 134

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net