Skip to content

Instantly share code, notes, and snippets.

@Lahorde
Last active Nov 12, 2020
Embed
What would you like to do?
Initialize an external I2C RTC module on raspberry using udev and systemd

Description

Enable i2c RTC using systemd. A udev rules triggers an I2C systemd service on I2C kernel module adding. Service then loads I2C driver adding a new I2C device to /sys Then kernel loads I2C RTC driver and adds an RTC device It triggers a udev rules that updates hardware clock

Application on raspberry pi

Enable I2C

  • enable I2C in config.txt, adding device_tree_param=i2c1=on

  • enable I2C driver loading at boot, refer i2c.conf

  • reboot raspberry

  • check an I2C bus is found :

    i2cdetect -l
    i2c-1   unknown         bcm2835 I2C adapter                     N/A
    
  • check I2C rtc is detected :

    i2cdetect -y 1
    0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
    00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
    10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
    60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
    70: -- -- -- -- -- -- -- --          
    

Enable I2C RTC

  • create /etc/udev/rules.d/50-i2c_start.rules

  • create /etc/conf.d/rtc-i2c

  • create /etc/systemd/system/i2c_rtc.service

  • create /etc/udev/rules.d//etc/udev/rules.d/60-rtc-i2c.rules

  • test it:

    hwclock -r
    2000-01-02 01:25:04.301281+01:00
    
  • when an NTP service is started, it should set hwclock to a valid time, if not you must set a valid system clock and then set hwclock to system clock

    hwclock --systohc
    
  • reboot

  • system clock should be fine, even if system is not connected to outside world

# /etc/udev/rules.d/50-i2c_start.rules
#
#
#I2C hardware device driver
ACTION=="add", KERNEL=="i2c_bcm2708", SUBSYSTEM=="module", TAG+="systemd"
# /etc/udev/rules.d/60-rtc-i2c.rules
#
#
ACTION=="add", SUBSYSTEM=="rtc", ATTRS{hctosys}=="0", RUN+="/usr/bin/hwclock -s --utc"
#/etc/conf.d/rtc-i2c
# configure there your RTC chip and address
CHIP="ds1307"
ADDRESS="0x68"
BUS="1"
# /etc/modules.load.d/i2c.conf
i2c-bcm2708
i2c-dev
# /etc/systemd/system/i2c_rtc.service
[Unit]
Description=Initialize i2c hardware RTC device driver
DefaultDependencies=no
Requires=sys-module-i2c_bcm2708.device
After=sys-module-i2c_bcm2708.device
Conflicts=shutdown.target
[Service]
Type=oneshot
RemainAfterExit=yes
EnvironmentFile=/etc/conf.d/rtc-i2c
ExecStart=/bin/sh -c "echo ${CHIP} ${ADDRESS} > /sys/class/i2c-adapter/i2c-${BUS}/new_device"
@solsticedhiver

This comment has been minimized.

Copy link

@solsticedhiver solsticedhiver commented Jan 31, 2019

That's a lot of file and rule. Most of it is not needed anymore. You don't have to manually load module and and set the i2c thing

Here is my setup.
I have a mcp7941x chip for the RTC.
The dtoverlay stuff takes care of module loading and configure the i2c stuff too. We only need to load i2c-dev.

Then the udev rule trigger a systemd service that calls hwclock. That's it.

Note: I use a systemd service because it seems udev has not the right to access/dev/rtcand then hwclock fails ?? Needs to be confirmed.

# /boot/config.txt

# [...]
dtparam=i2c=on
dtoverlay=i2c-rtc,mcp7941x=0x6f
# /etc/modules.d/i2c.conf
i2c-dev
i2c:mcp7941x
# /etc/udev/rules.d/rtc-i2c.rules 
KERNEL=="rtc0", SUBSYSTEM=="rtc", SUBSYSTEMS=="i2c", TAG+="systemd", ENV{SYSTEMD_WANTS}="hwclock.service"
# /etc/systemd/system/hwclock.service 
[Unit]
Description=Synchronize system clock from RTC

[Service]
ExecStart=/usr/bin/hwclock --hctosys --utc
Type=oneshot
@xlla

This comment has been minimized.

Copy link

@xlla xlla commented May 11, 2020

@solsticedhiver it's work for me on kernel 4.19.115 @aarch64

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment