Timer and clock

Updated: May 06, 2022

The kernel uses the timer callouts to work with the hardware timer (i.e., the timer/counter chip).

In many cases, a board can have several hardware timers. You choose one of these timers to use for timer_*() functions in the startup code and timer_*() kernel callouts. In general, hardware timers either count up or count down and may or may not be auto-reset (also called periodic).

The kernel uses a hardware timer to generate a periodic interrupt (i.e., the clock tick). The kernel uses this interrupt for:

  • Updating the system time and other software clocks
  • Firing software timers
  • Thread scheduling

Kernel callouts

The kernel interacts with the specified hardware timer through a set of timer kernel callouts.

timer_load()
The kernel assumes that the hardware timer starts at 0, counts up to a number (the divisor value), and then generates an interrupt. The kernel uses the following method to specify a divisor value for the hardware timer:
  1. The kernel provides the divisor value to be loaded into the hardware timer, by writing the value in the timer_load field in the qtime_entry element of the system page.
  2. The kernel calls timer_load().
  3. Because the kernel doesn't know the characteristics of the hardware timer, timer_load() validates the divisor value. For example, if the divisor value provided by the kernel is too high, the callout writes the maximum divisor value supported by the hardware timer into the hardware timer.
  4. The callout copies the value written to the hardware timer into the timer_load field in qtime_entry.
  5. The kernel reads the timer_load field in qtime_entry to see what the callout wrote to the hardware timer.
timer_reload()
The kernel assumes that the hardware timer auto-resets (i.e., it's periodic). If the timer does not auto-reset, you must implement the timer_reload() callout. The kernel calls timer_reload() at the beginning of the interrupt.
If this callout returns 1, the interrupt is treated as a clock tick. This return value is useful when several interrupt sources can generate the same interrupt, to distinguish a clock tick from the other sources.
timer_value()
If the kernel wants to know when to expect the next interrupt, it calls the timer_value() callout and uses the following formula:
jiffies_until_interrupt = divisor_value - timer_value()
where a jiffy is how long it takes for the timer count to increment by one.
If the hardware timer counts down, the timer_value() callout performs a calculation to provide a value that is equivalent to one from a count-up timer. The following pseudo-code is an example of the timer_value calculation for a timer_value() callout:
curr_hw_timer_value = // read hardware timer value here
value = divisor_value - curr_hw_timer_value
return value