Aopen Chromebase with HDMI RTC and Alpine Linux


Introduction

The Aopen Chromebase (or Chromebox) Mini seems to be equipped with a real time clock (RTC):

[    0.321120] rk808-rtc rk808-rtc: registered as rtc0
[    0.321596] rk808-rtc rk808-rtc: setting system clock to 2013-01-18T15:30:40 UTC (1358523040)

The problem is the lack of a battery to keep the time when power is removed. Note the 2013 date in the above dmesg output.
This post details how to add an external battery backed RTC module connected to the HDMI port.

Hardware

I used a DS3231 RTC module like this:

DS3231

I removed an HDMI connector from a cable and used a small breadboard for testing:

test-setup

Pinout:

HDMI DS3231
15-SCL C
16-SDA D
17-GND -
18-VCC +
19-HPD +

Note the 22k ohm resistor in the picture that connects 5v VCC to the hot plug detect pin (19-HPD).
There is contradictory information about the value and even the need for this resistor at all. See references at the bottom of this page.
I tested with the 22k resistor and without and both configurations worked.
I did not have a 1k resistor to test with but I assume that would work fine and aligns with this comment from the ti.com link:

Note that many Sink devices simply connect the HPD signal to the +5V Power signal through a 1000 ohm resistor.

A custom PCB from https://jlcpcb.com/ enabled me to assemble a clean and compact final solution, despite my ineptness with a soldering iron. I used a DS3231 module from Amazon and desoldered the battery (reuse) and pin header (discard). I went with a 1k ohm resistor in the final assembly.

Parts list:

MFR. Part LCSC Part Description
DS3231 NA Real Time Clock Module
HYC109-HDMIA19-160 C711355 19 male HDMI Board Edge, Straddle Mount Connector
MF1/4W-1KΩ±1%T52 C713997 Metal Film Resistors 1k± 250mW

PCB

Parts

Assembly

Connected

Software

The below kernel patch needs to be reverted. The commit message was “Switch to builtin HDMI DDC bus on rk3288-veyron”.
I was not able to communicate with the external RTC until I backed this change out.

--- a/arch/arm/boot/dts/rk3288-veyron.dtsi
+++ b/arch/arm/boot/dts/rk3288-veyron.dtsi
@@ -175,7 +175,8 @@
 };
 
 &hdmi {
-   ddc-i2c-bus = <&i2c5>;
+   pinctrl-names = "default";
+   pinctrl-0 = <&hdmi_ddc>;
    status = "okay";
 };
 
@@ -346,14 +347,6 @@
    i2c-scl-rising-time-ns = <300>;     /* 225ns measured */
 };
 
-&i2c5 {
-   status = "okay";
-
-   clock-frequency = <100000>;
-   i2c-scl-falling-time-ns = <300>;
-   i2c-scl-rising-time-ns = <1000>;
-};
-
 &io_domains {
    status = "okay";

Make sure the below kernel options are set.
The century option is probably not required but I set it anyway.

CONFIG_RTC_DRV_DS1307=y
CONFIG_RTC_DRV_DS1307_CENTURY=y

I tried CONFIG_I2C_CHARDEV built into the kernel instead of as a module but the /dev/i2c-* device files weren’t created on boot.
Loading the i2c-dev module does create them and allows the userspace tools to work correctly so I decided to just leave it as a module.
Loading the module is only needed when using tools like i2cdetect and isn’t required for the system to make use of the rtc.

cbtest:~$ doas i2cdetect -l
cbtest:~$ doas i2cdetect -y 5
Error: Could not open file '/dev/i2c-5' or '/dev/i2c/5': No such file or directory
cbtest:~$ doas modprobe i2c-dev
cbtest:~$ doas i2cdetect -l
i2c-0   i2c         rk3x-i2c                            I2C adapter
i2c-1   i2c         rk3x-i2c                            I2C adapter
i2c-2   i2c         rk3x-i2c                            I2C adapter
i2c-3   i2c         rk3x-i2c                            I2C adapter
i2c-4   i2c         rk3x-i2c                            I2C adapter
i2c-5   i2c         rk3x-i2c                            I2C adapter
i2c-6   i2c         DP-AUX                              I2C adapter
cbtest:~$ doas i2cdetect -y 5
     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: -- -- -- -- -- -- -- --

Before adding the new new device we only see rtc0:

cbtest:~$ ls /dev/rtc*
/dev/rtc   /dev/rtc0

Add the device:

/home/cb # echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-5/new_device

We should see rtc1 now:

cbtest:~$ ls /dev/rtc*
/dev/rtc   /dev/rtc0  /dev/rtc1

Use hwclock to read the time from /dev/rtc1

cbtest:~$ doas hwclock -r --verbose --rtc=/dev/rtc1
hwclock from util-linux 2.38.1
System Time: 1668960978.460699
Using the rtc interface to the clock.
Assuming hardware clock is kept in UTC time.
Waiting for clock tick...
ioctl(3, RTC_UIE_ON, 0): Invalid argument
Waiting in loop for time from /dev/rtc1 to change
...got clock tick
Time read from Hardware Clock: 2022/11/20 16:33:47
Hw clock time : 2022/11/20 16:33:47 = 1668962027 seconds since 1969
Time since last adjustment is 1668962027 seconds
Calculated Hardware Clock drift is 0.000000 seconds
2022-11-20 11:33:46.613956-05:00

Testing looks good but we want the system to use /dev/rtc1 automatically on boot.
The symlink for /dev/rtc is currently pointing to /dev/rtc0

cbtest:~$ ls -alh /dev/rtc
lrwxrwxrwx    1 root     root           4 Nov 20 11:05 /dev/rtc -> rtc0

Create a udev rules file name /etc/udev/rules.d/99-rtc1.rules with the following content:

KERNELS=="i2c-5", SUBSYSTEMS=="i2c", DRIVERS=="", ATTRS{name}=="rk3x-i2c", RUN+="/bin/sh -c 'echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-5/new_device'"
KERNEL=="rtc1", SUBSYSTEM=="rtc", DRIVER=="", ATTR{name}=="rtc-ds1307 5-0068", SYMLINK+="rtc"

The above entries first create the rtc1 device and then change the /dev/rtc symlink to point to /dev/rtc1
Unfortunately, the hwclock utility by default looks for /dev/rtc0 first and then /dev/rtc so just changing the /dev/rtc target isn’t enough.

Edit /etc/conf.d/hwclock to use rtc1 via our retargeted symlink:

clock_args="--rtc=/dev/rtc"

Reboot and /dev/rtc should now be pointing at the new /dev/rtc1

cbtest:~$ ls -alh /dev/rtc*
lrwxrwxrwx    1 root     root           4 Jan 18  2013 /dev/rtc -> rtc1
crw-------    1 root     root      250,   0 Jan 18  2013 /dev/rtc0
crw-------    1 root     root      250,   1 Jan 18  2013 /dev/rtc1

The hwclock init script sets the system time based on the rtc1 time at boot and also updates it on shutdown based on the current system clock, which may have been updated by NTP.

References

https://mitxela.com/projects/ddc-oled
https://blog.danman.eu/emulating-hdmi-connection/
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/arch/arm/boot/dts/rk3288-veyron.dtsi?h=v5.15.74&id=bf09924f21767e6bb7cb3aae48c48c2c2ab8261a
https://unix.stackexchange.com/questions/246605/how-can-the-link-target-of-dev-rtc-be-changed
https://electronics.stackexchange.com/questions/331242/how-to-trigger-hot-plug-detection-in-hdmi-interface
https://e2e.ti.com/support/interface-group/interface/f/interface-forum/580292/tpd12s016-hpd-signal-powered-by-different-power-supply
https://forums.raspberrypi.com/viewtopic.php?t=330201#p1977333
https://bugs.launchpad.net/ubuntu/+source/linux-raspi/+bug/1981078
https://learn.adafruit.com/adding-a-real-time-clock-to-raspberry-pi/set-rtc-time
https://man7.org/linux/man-pages/man8/hwclock.8.html
https://busware.de/tiki-index.php?page=RTUL
https://movella.force.com/XsensKnowledgebase/s/article/My-MTi-6XX-is-not-detected-Installing-the-MTi-USB-dongle-driver-for-Linux?language=en_US
https://www.lcsc.com/
https://jlcpcb.com/