Torizon RTC and RTC on SOM

I need to use internal RTC (SOM) IMX8d and a rechargeable battery.

I tried to adjust time in different way

date -s “2 June 2024 14:05:00”
timedatectl set-time “2024-01-31 11:13:54”
with set_clock in c++ program.

all work fine also inside a docker, but when I switch off the board the date returns to the default value.

I connected a rechargeable battery to PIN 40 of the SOM but maybe it is not using that rtc.

dmesg | grep rtc print following
[ 1.461967] imx-sc-rtc scu:rtc: registered as rtc1
[ 1.653132] rtc-ds1307: probe of 1-0068 failed with error -5

timedatectl
Local time: Wed 2024-01-31 11:14:07 UTC
Universal time: Wed 2024-01-31 11:14:07 UTC
RTC time: n/a
Time zone: Universal (UTC, +0000)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no

sudo hwclock -w --verbose
hwclock from util-linux 2.37.4
System Time: 1706699773.777470
Trying to open: /dev/rtc0
Trying to open: /dev/rtc
Trying to open: /dev/misc/rtc
No usable clock interface found.
hwclock: Cannot access the Hardware Clock via any known method.

Date/time setting commands from inside a container only affect the container (as they should).

Using timedatectl set-time “2024-01-31 11:13:54” from inside a container will work on the container only if the container uses systemd for life-cycle management or if the host’s systemd socket is exposed to the container using --privileged --volume /run/dbus/system_bus_socket:/run/dbus/system_bus_socket (which is not recommended).

I suppose you ran this command from the host (not the container):

dmesg | grep rtc print following
[ 1.461967] imx-sc-rtc scu:rtc: registered as rtc1
[ 1.653132] rtc-ds1307: probe of 1-0068 failed with error -5

The first response is to registering the SoC’s built-in secure RTC. The second response indicate that it tried to probe a DS1307 RTC chip which does not seem to be present. That is an external RTC chip which would be on a carrier board as it is not on the SoM. Your platform then probably has only one RTC which is /dev/rtc1. You can check with

# ls -l /dev/rtc*
lrwxrwxrwx 1 root root      4 Mar 21 04:42 /dev/rtc -> rtc0
crw------- 1 root root 251, 0 Mar 21 04:42 /dev/rtc0
crw------- 1 root root 251, 1 Mar 21 04:42 /dev/rtc1
# dmesg |grep rtc
[    0.222372] snvs_rtc 30370000.snvs:snvs-rtc-lp: registered as rtc1
[    0.299625] rtc-ds1307 0-0032: oscillator failed, set time!
[    0.300339] rtc-ds1307 0-0032: registered as rtc0
[    0.302462] rtc-ds1307 0-0032: hctosys: unable to read the hardware clock

The above output is from a Verdin i.MX8MP SoM on a Verdin development carrier board which has a DS1307. There is no battery inserted, hence the RTC is not set when the kernel boots (error messages oscillator failed, set time! and hctosys: unable to read the hardware clock). The RTC still works though as systemd-timedated sets it (in this case from a network time server as the board is connected to a network with NTP server):

# timedatectl  
               Local time: Fri 2024-03-22 18:30:48 UTC
           Universal time: Fri 2024-03-22 18:30:48 UTC
                 RTC time: Fri 2024-03-22 18:30:48
                Time zone: Universal (UTC, +0000)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no

Using sudo hwclock -w --verbose by default uses the /dev/rtc device which is typically a link to the actual RTC device (see above). The output suggests that your platform has the /dev/rtc1 device but no /dev/rtc link.

You can either create the link or use sudo hwclock -w --verbose -f /dev/rtc1.

It is typically not necessary to manually sync the RTC from system time and vice versa as the kernel does that on startup and shutdown. Also timedatectl, more accurately systemd-timedated, synchronizes the RTC (but also using /dev/rtc).

The kernel uses these configs to determine how to synchronize the RTC:

CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE="rtc0"

The former two indicate to the kernel to sync system time from RTC using rtc0 on startup. The latter two indicate to sync RTC from system time on shutdown using rtc0. They are enabled and set by the Torizon kernel config.

yes i ran commands from host

ok rtc1 is internal som rtc, and I have to link, I tried to do it how you suggested with

sudo hwclock -w --verbose -f /dev/rtc1 i have the following

hwclock from util-linux 2.37.4
System Time: 1706700316.702108
Using the rtc interface to the clock.
Last drift adjustment done at 1706700059 seconds after 1969
Last calibration done at 1706700059 seconds after 1969
Hardware clock is on UTC time
Assuming hardware clock is kept in UTC time.
RTC type: ‘imx-sc-rtc’
Using delay: 0.000000 seconds
missed it - 1706700316.703719 is too far past 1706700316.000000 (0.703719 > 0.001000)
1706700317.000000 is close enough to 1706700317.000000 (0.000000 < 0.002000)
Set RTC to 1706700317 (1706700316 + 1; refsystime = 1706700316.000000)
Setting Hardware Clock to 11:25:17 = 1706700317 seconds since 1969
ioctl(RTC_SET_TIME) was successful.
Not adjusting drift factor because the --update-drift option was not used.
New /etc/adjtime data:
0.000000 1706700316 0.000000
1706700316
UTC

but if I run for example

sudo hwclock --systohc -v

hwclock from util-linux 2.37.4
System Time: 1651167895.158884
Trying to open: /dev/rtc0
Trying to open: /dev/rtc
Trying to open: /dev/misc/rtc
No usable clock interface found.
hwclock: Cannot access the Hardware Clock via any known method.

maybe the link has not worked because it tries to open always /dev/rtc0

this is the output of ls -l /dev/rtc* when I restart
crw------- 1 root root 252, 1 Apr 28 17:42 /dev/rtc1

Using sudo hwclock -f <device> ... will use the device once. You will have to do it for all subsequent uses of hwclock too.

You can create the link with sudo ln -s rtc1 /dev/rtc. Then you will be able to use hwclock without -f. However, that link will not be permanent either. You will have to recreate it every time after the system boots.

Thank you very much Rudolf, now I got it and it works properly but

  • How can I create this link definitive maybe with

CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE=“rtc1”
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE=“rtc1”

and if yes, my application never ends and the device is never powered off, but power supply could go down and if my application changes current date time how is it possible save by c++ also into rtc1,
when I use

int ret = (clock_settime(CLOCK_REALTIME, &newclktime));

system time is written but rtc1 not, the only way to write that works properly is

timedatectl set-time …

but it works only in the host not in the docker.

Changing the kernel config will allow the kernel to sync system time from rtc1 and vice versa.
The symbolic link is not created by that but by a udev rule:

# The first rtc device is symlinked to /dev/rtc
KERNEL=="rtc0", SYMLINK+="rtc"

typically found in /etc/udev/rules.d/localextra.rules

This does int ret = (clock_settime(CLOCK_REALTIME, &newclktime)); does not set the hardware rtc. It sets the system’s realtime clock counter which is the one that is restored from hardware rtc either by the kernel or systemd-timedated. And, btw, your application would need to have the correct privileges to execute this API call. I commonly see that embedded systems run applications as root which I do not recommend. Run as a regular user but in the application’s systemd service file use AmbientCapabilities=CAP_SYS_TIME to allow the application to set the system clock.

Correct, timedatectl updates the hardware rtc via systemd-timedated. If you want your application to update the system time as well as the hardware rtc, I recommend using the dbus interface org.freedesktop.timesync1.

Rudolf thank you now it works but I didn’t understand if this kernel configs

CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE=“rtc1”
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE=“rtc1”

should be set by torizon core builder in tcbuild.yaml in the section

kernel:
arguments:

or I have to use yocto and create my torizon core image?

Because without this configuration at the boot I can’t have rtc1 copied in the system time, or I need to do this manually by code (in the host or in the container)

Hello @fabrizio.camagna,

You cannot set these kernel configs using Torizoncore builder. You will have to build the image from Yocto for this. You may refer to this article for how to do that: Custom meta layers, recipes and images in Yocto Project (hello-world examples) | Toradex Developer Center

The linux-toradex kernel supports the Yocto Project’s kernel configuration scheme. Hence configuring the kernel is as simple as creating a kernel configuration snippet and bbappend file in your own layer:

meta-mylayer/recipes-kernel/linux/linux-toradex/rtc.cfg:

CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE=“rtc1”
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE=“rtc1”

meta-mylayer/recipes-kernel/linux/linux-toradex_%.bbappend:

FILESEXTRAPATHS:prepend := "${THISDIR}/${PN}:"

SRC_URI += " \
    file://rtc.cfg \
"

When building the kernel the configuration snippet will be merged into the default configuration of the kernel provided by Toradex aka the defconfig file.

Hello @fabrizio.camagna,

Could you make some progress here?

I’m tryng to create my image with rtc configuration, at the moment I’ve created my torizon image and it’s working but now I have to add a new layer with

CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE=“rtc1”
CONFIG_RTC_SYSTOHC=y
CONFIG_RTC_SYSTOHC_DEVICE=“rtc1”

I tried with Yocto and my first Torizon new image with my meta-layer and new configuration of HCTTOSYS

with zcat /proc/config.gz | grep rtc now I have i

CONFIG_RTC_HCTOSYS_DEVICE=“rtc1”
CONFIG_RTC_SYSTOHC_DEVICE=“rtc1”

now if I set the time (as happened before) system time and rtc time are updated

sudo timedatectl set-time ‘2024-04-08 11:53:00’, RTC is written

Local time: Mon 2024-04-08 11:53:02 UTC
Universal time: Mon 2024-04-08 11:53:02 UTC
RTC time: Mon 2024-04-08 11:53:02
Time zone: Universal (UTC, +0000)
System clock synchronized: no
NTP service: inactive
RTC in local TZ: no

but when I turn off and turn on it is loaded properly from rtc1 to system time and this is what I wanted!!!

If I change only system time, with shutdown command (shutdown -r 1) rtc1 is written, with reboot not but maybe is normal.

Anyway thank you very much guys, now I have only find a way to write hardware rtc from my C++ code!!!

Hello @fabrizio.camagna,

It is good to know that you are able to write and use the rtc on your project.

A possible approach would be to call the hwclock program in your c++ code.
This would avoid the need for re-implementing the logic to interact with the HW RTC which is already done on hwclock.

Best Regards,
Bruno

Thank you I did it with date and hwclock launched by system(“date …”) and system(“hwclock -hctosys”), in this way there isn’t problem with timezone and daylight.