RTC support in embedded Linux box

Real Time Clock (RTC) is a small but important piece of hardware in an embedded Linux box. It saves time and keep time valid while box is in shutdown state. When the box boots again next time, it will provide a valid time to Linux system.

Linux RTC

Real Time Clock in Linux

Two clocks are important in Linux system: a ‘hardware clock’, also known as RTC, CMOS or BIOS clock. This is the battery backed clock that keeps time even when the system is shut down. The second clock is called the ‘system clock’ or ‘kernel clock’ and is maintained by Linux OS. At boot time, the hardware clock is read and used to set the system clock. From that point onward the system clock is used to track time. Typically during the linux system shutdown process, Linux will flush its time into RTC to save it.

For many SoC, it has RTC embedded. However in my platform an external RTC is connected to SoC through I2C bus with typical address 0x68. This external RTC cnsumes less current than embedded one.

Check the two clocks

The simplest commands to check two clocks are: “date” and “hwclock”

bash-4.3# # the command date can be used to check the current system time
bash-4.3# date
Wed Aug  2 10:11:35 Universal 2017
bash-4.3# # command hwclock can read clock from RTC device
bash-4.3# hwclock -r
Fri Jan  1 01:01:49 2010  0.000000 seconds
bash-4.3#
bash-4.3# # hwclock can push the system into RTC device
bash-4.3# hwclock -w
bash-4.3#
bash-4.3# hwclock -r
Wed Aug  2 10:13:19 2017  0.000000 seconds
bash-4.3# date
Wed Aug  2 10:13:22 Universal 2017
bash-4.3# #Now both times are synced.

As shown in the above command, there are two different clocks, and they can be synced using hwclock.

What need to be done ?

Linux kernel is able to support mutiple RTC devices in the same time. This can be checked by “ls -l /dev/rtc*” to see how many RTC devices are registered with Linux kernel.

What takes to make RTC devices work properly?

  • Make sure hardware setup is in place, either connected through I2C, or SPI bus.
  • Properly configured in device Tree file (DTS) to make it enabled. In my case, I need disable RTC inside SoC, and enable external RTC.
  •  
    bash# cat armada-7040-sparrow-v6.dts
    .......
                            rtc@284000 {
                                    compatible = "marvell,armada8k-rtc";
                                    reg = <0x284000 0x20 0x284080 0x24>;
                                    reg-names = "rtc", "rtc-soc";
                                    #address-cells = <0x1>;
                                    #size-cells = <0x0>;
                                    interrupts = <0x0 0x4d 0x4>;
                                    status = "disabled";
                            };
    .......
                            i2c@701100 {
                                    compatible = "marvell,mv78230-i2c";
                                    reg = <0x701100 0x20>;
                                    #address-cells = <0x1>;
                                    #size-cells = <0x0>;
                                    interrupts = <0x0 0x79 0x4>;
                                    timeout-ms = <0x3e8>;
                                    clocks = <0x26 0x1 0x15>;
                                    status = "okay";
                                    clock-frequency = <0x186a0>;
                                    rtc@68 {
                                            compatible = "dallas,ds1337";
                                            reg = <0x68>;
                                    };
    
                            };
    .......
  • Configure Linux kernel with proper driver, and tell linux kernel the correct RTC instance to use in order to save time.
  • In the following example, DS1307 driver is enabled in kernel config, which will be the only RTC device rgistered in the system, so "rtc0" is set for system to sync to.
     
    
    CONFIG_RTC_LIB=y
    CONFIG_RTC_CLASS=y
    CONFIG_RTC_HCTOSYS=y
    CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
    CONFIG_RTC_SYSTOHC=y
    CONFIG_RTC_SYSTOHC_DEVICE="rtc0"
    # CONFIG_RTC_DEBUG is not set
    
    #
    # RTC interfaces
    #
    CONFIG_RTC_INTF_SYSFS=y
    CONFIG_RTC_INTF_PROC=y
    CONFIG_RTC_INTF_DEV=y
    # CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
    # CONFIG_RTC_DRV_TEST is not set
    
    #
    # I2C RTC drivers
    #
    # CONFIG_RTC_DRV_ABB5ZES3 is not set
    # CONFIG_RTC_DRV_ABX80X is not set
    CONFIG_RTC_DRV_DS1307=y
    # CONFIG_RTC_DRV_DS1374 is not set

Check the box…

 

bash-4.3#  dmesg | grep rtc
[    0.000000] Kernel command line: Image.sparrow.rtc1337 initrd=initramfs-0328.cpio.gz dtb=armada-7040-sparrow-v6.dtb console=ttyS0,115200 emergency init=/bin/bash crashkernel=64M
[    4.157793] rtc-ds1307 0-0068: rtc core: registered ds1337 as rtc0
[    4.232706] rtc-ds1307 0-0068: setting system clock to 2017-08-02 10:09:01 UTC (1501668541)
bash-4.3# ls -l /dev/rtc*
crw------- 1 0 0 253, 0 Jan  1  1970 /dev/rtc0
bash-4.3# 
bash-4.3# date && hwclock -r
Wed Aug  2 10:33:25 Universal 2017
Wed Aug  2 10:33:24 2017  0.000000 seconds
bash-4.3#

As it is shown from dmesg, external RTC is registered as “rtc0”, SoC RTC is not seen by kernel since it is disabled in DTS. Both clocks are synced.