Linux: Module request: rtc-rv3028

Created on 28 Mar 2019  ·  60Comments  ·  Source: raspberrypi/linux

I would very much appreciate inclusion of the rv3028 RTC driver module if possible.

The PR that brought it into upstream is here: https://github.com/torvalds/linux/commit/e6e7376cfd7b3f9b63de3a22792f64d9bfb2ab53

It looks like it requires (or requires updates to) 'rtc_add_group' and rtc_nvram_register along with some constants not defined in rtc.h for 4.14.y so I'm not sure how straight forward this is?

Thank you.

Waiting for external input

Most helpful comment

Because I'm feeling completionistic, I wrote a script that sets the BSM in EEPROM too:

#!/bin/bash

# Thanks to https://lang-ship.com/reference/Arduino/libraries/RTC_RV-3028-C7_Arduino_Library/class_r_v3028.html#a9cbc9a009d4e5dbfeb29e366140be42b
# And the folks at https://github.com/raspberrypi/linux/issues/2912

function wait_for_EEBusy_done {
   busy=$((0x80))
   while (( busy == 0x80 ))
   do
      status=$( i2cget -y 1 0x52 0x0E )
      busy=$((status & 0x80))
   done
}

rmmod rtc_rv3028

wait_for_EEBusy_done

# disable auto refresh
register=$( i2cget -y 1 0x52 0x0F )
writeback=$((register | 0x08))
i2cset -y 1 0x52 0x0F $writeback

# enable BSM in level switching mode
register=$( i2cget -y 1 0x52 0x37 )
writeback=$((register | 0x0C))
i2cset -y 1 0x52 0x37 $writeback

# update EEPROM
i2cset -y 1 0x52 0x27 0x00
i2cset -y 1 0x52 0x27 0x11

wait_for_EEBusy_done

# reenable auto refresh
register=$( i2cget -y 1 0x52 0x0F )
writeback=$((register & ~0x08))
i2cset -y 1 0x52 0x0F $writeback

wait_for_EEBusy_done

modprobe rtc_rv3028

Cut power (with no backup) and plugged it back in, register 0x37 was still set.

Best part? You only need to run it once and it's set forever.

All 60 comments

Is 4.14 a requirement for you? 4.19 is our current master branch.

Not at all! In my case it happened to be the kernel available via apt and I was attempting to build the module out-of-tree for testing.

Thank you- on it.

Built and testing against 4.19.30 using (as root):

modprobe rtc-rv3028
echo rv3028 0x52 > /sys/class/i2c-adapter/i2c-1/new_device
hwclock --systohc
hwclock --show

I believe the following dtoverlay is required for battery backup functionality to be enabled:

/dts-v1/;
/plugin/;

/ {
    compatible = "brcm,bcm2708";

    fragment@0 {
        target = <&i2c_arm>;
        __overlay__ {
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";

            rv3028@52 {
                compatible = "microcrystal,rv3028";
                reg = <0x52>;
                trickle-resistor-ohms = <11000>;
                status = "okay";
            };
        };
    };
};

Could this please be merged into https://github.com/raspberrypi/linux/blob/e2d2941326922b63d722ebc46520c3a2287b675f/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ?

I believe the existing address, and trickle-resistor-ohms parameters would apply.

Thank you for cranking this out so quickly, much appreciated!

Could this please be merged into https://github.com/raspberrypi/linux/blob/e2d2941326922b63d722ebc46520c3a2287b675f/arch/arm/boot/dts/overlays/i2c-rtc-overlay.dts ?

Already done. 8-)

I believe the existing address, and trickle-resistor-ohms parameters would apply.

Not the address - that's fixed - but I could add the trickle-resistor.

Now updated with trickle-resistor support - use the trickle-resistor-ohms parameter.

Actually I'm wrong regarding backup battery functionality. Looking at the datasheet for details on register 0x37 suggests it requires BSM (Backup Switchover Mode) to be changed, but this driver currently does not support that at all. I'll have to add a backup-switchover-mode property to make this settable.

Brilliant! Thank you.

This patch adds suggested functionality for backup-switchover-mode.

Without this parameter supplied, the RTC is never told to use its backup battery and thus loses the time on power off.

Possible options are:

  • 0 - Switchover Disabled
  • 1 - Enable Direct Switching Mode (switchover when Vdd < Vbackup)
  • 2 - Enable Standby Mode (go into standby when Vdd < Vbackup, does not draw current)
  • 3 - Enable level Switching Mode (switchover when Vdd < Vbackup and Vdd < Vddsw and Vbackup > Vddsw.

I'll also ping it over to the module author:

diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c
index d04c2d481..d674a8257 100644
--- a/drivers/rtc/rtc-rv3028.c
+++ b/drivers/rtc/rtc-rv3028.c
@@ -74,6 +74,7 @@

 #define RV3028_BACKUP_TCE              BIT(5)
 #define RV3028_BACKUP_TCR_MASK         GENMASK(1,0)
+#define RV3028_BACKUP_BSM_MASK  0b00001100

 #define OFFSET_STEP_PPT                        953674

@@ -601,6 +602,7 @@ static int rv3028_probe(struct i2c_client *client)
        struct rv3028_data *rv3028;
        int ret, status;
        u32 ohms;
+       u8 bsm;
        struct nvmem_config nvmem_cfg = {
                .name = "rv3028_nvram",
                .word_size = 1,
@@ -671,6 +673,17 @@ static int rv3028_probe(struct i2c_client *client)
        if (ret)
                return ret;

+       /* setup backup switchover mode */
+       if (!device_property_read_u8(&client->dev, "backup-switchover-mode",
+                                 &bsm))  {
+               ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,
+                       RV3028_BACKUP_BSM_MASK,
+                       (bsm & 0b11) << 2);
+
+               if (ret)
+                   return ret;
+       }
+
        /* setup trickle charger */
        if (!device_property_read_u32(&client->dev, "trickle-resistor-ohms",
                                      &ohms)) {

Can you submit this as a PR, ideally with the required overlay parameter?

If you're happy with the rest of the functionality I'll merge it now.

No problem, will get a PR under way once it's merged.

And, yes, everything else seems to work as expected.

Thanks again!

Have at it!

@Gadgetoid This appears to have been fixed - can the issue be closed?

I'm think I'm having trouble with the backup-switchover-mode parameter.

On my RPi4, I've set in my /boot/config.txt
dtoverlay=i2c-rtc,rv3028,backup-switchover-mode=1

I can successfully hwclock -w and then hwclock -r, but on reboot (with battery backup) I always get

pi@raspberrypi:~ $ dmesg | grep rv3028
[   22.951325] rtc-rv3028 1-0052: Voltage low, data loss detected.
[   22.974359] rtc-rv3028 1-0052: Voltage low, data is invalid.
[   22.975039] rtc-rv3028 1-0052: registered as rtc0

with no data retained. I've also tried setting the parameter to other values with no luck. Also tried invalid value (4) but don't see a "invalid backup switchover mode value" in my dmesg. Am I using the overlay wrong? Any tip appreciated!

Have you run hwclock --systohc? My understanding is that this is needed to reset the internal state.

Thanks @pelwell , in the hwclock man page -w is equivalent to -systohc so I think I've covered it

Here's a more detailed dump:

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.19.58-v7l+ #1245 SMP Fri Jul 12 17:31:45 BST 2019 armv7l GNU/Linux
pi@raspberrypi:~ $ tail -n 1 /boot/config.txt
dtoverlay=i2c-rtc,rv3028,backup-switchover-mode=1
pi@raspberrypi:~ $ sudo hwclock -r
hwclock: ioctl(RTC_RD_TIME) to /dev/rtc0 to read the time failed: Invalid argument
pi@raspberrypi:~ $ dmesg | grep rtc
[    5.490099] rtc-rv3028 1-0052: Voltage low, data loss detected.
[    5.499733] rtc-rv3028 1-0052: Voltage low, data is invalid.
[    5.500603] rtc-rv3028 1-0052: registered as rtc0
[    5.682168] rtc-rv3028 1-0052: Voltage low, data is invalid.
[    5.683034] rtc-rv3028 1-0052: Voltage low, data is invalid.
[    5.683915] rtc-rv3028 1-0052: Voltage low, data is invalid.
[   27.867501] rtc-rv3028 1-0052: Voltage low, data is invalid.
[   27.868361] rtc-rv3028 1-0052: Voltage low, data is invalid.
[   27.869222] rtc-rv3028 1-0052: Voltage low, data is invalid.
pi@raspberrypi:~ $ sudo hwclock -w
pi@raspberrypi:~ $ sudo hwclock -r
2020-01-02 16:50:06.026608-08:00
pi@raspberrypi:~ $ sudo 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: -- -- UU -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --                        
pi@raspberrypi:~ $ sudo rmmod rtc_rv3028
pi@raspberrypi:~ $ sudo i2cdump -y 1 0x52
No size specified (using byte-data access)
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef
00: 26 51 00 00 03 01 20 80 80 80 00 00 00 00 10 20    &Q..?? ???....?
10: 84 00 00 00 00 00 00 00 00 00 00 9c 00 00 00 00    ?..........?....
20: 00 00 00 00 00 00 4f 00 33 00 00 24 62 0c 13 15    ......O.3..$b???
30: 00 00 00 00 00 c0 ff 90 99 19 00 00 00 00 00 00    .....?.???......
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00    ................

Am communicating with a rep from microcrystal and they pointed out from this register dump that neither the time setting or the switchover config is reflected here

    Address 0x37 = 0x90 => switchover is not configured
    Address 0x02 = 0x00 => RTC hour  is not equal to 16 (time set 2020-01-02 16:50:06)
    Address 0x04 = 0x03 => RTC day is not equal to 02(time set 2020-01-02 16:50:06)

If I were you I'd try the --systohc variant anyway, But I think you're on the right lines in looking for a failure to write the data.

See no difference with the --systohc variant.

I was able to unload the driver, set the switchover register with i2cset -y 1 0x52 0x37 1, and see it reflected in the register dump. But moot point since is seems like the time itself isn't being written and reflected in the register dump

this is all with an RV-3028-C7 eval board

so the correct setting was actually ic2set -y 1 0x52 0x37 0x06, and doing that the battery switchover works. Can use i2cset as workaround. Not sure what part of backup-switchover-mode is not working

This is what I get for my working RV3028, setup with dtoverlay=i2c-rtc,rv3028. It was sitting unpowered for a couple of days. I can't say that i know what it means, but I'll post it anyway.

pi@raspberrypi:~ $ sudo rmmod rtc_rv3028
pi@raspberrypi:~ $ sudo i2cdump -y 1 0x52
No size specified (using byte-data access)
…….0...1....2...3....4....5....6....7....8....9...a....b....c...d....e...f 0123456789abcdef
00: 03 30 13 04 04 02 20 80 80 80 00 00 00 00 30 20 ?0???? ???....0
10: 84 00 00 00 00 00 00 00 00 00 00 31 2a 09 00 00 ?..........1*?..
20: 00 00 00 00 00 00 73 00 33 00 00 14 62 0e 16 17 ......s.3..?b???
30: 00 00 00 00 00 c0 ff 9c a9 19 00 00 00 00 00 00 .....?.???......
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
pi@raspberrypi:~ $

I struck out trying to get a battery for my other board. All I could find were packs of 10 or more, no singles. I have another RV3028 on its way here and may just wait till it shows up. Then try its battery in my dud breakout. I don't really want to mess with taking the battery out of my one working setup.

Is there a solution for this problem yet? I have the same issue.

I did the following and have mine working.
I installed the pimoroni software and ran the set-time.py file. This sets the battery backup as on.
I then enabled i2c via raspberry Pi Configuration
Then opened a terminal window and ran
sudo i2cdetect -y 1
The RV3028 shows up as 52
I then edited the config.txt
sudo nano /boot/config.txt
and added
dtoverlay=i2c-rtc,rv3028
ctrl x, y, enter
sudo reboot
Then open the terminal window and ran
sudo i2cdetect -y 1
The 52 is now UU which means its under system control
Next I disable the “fake hwclock” which interferes with the ‘real’ hwclock
sudo apt-get -y remove fake-hwclock
sudo update-rc.d -f fake-hwclock remove
sudo systemctl disable fake-hwclock
Now with the fake-hw clock off, you can start the original ‘hardware clock’ script.
sudo nano /lib/udev/hwclock-set
and comment out these lines:

if [ -e /run/systemd/system ] ; then

exit 0

fi

/sbin/hwclock --rtc=$dev --systz --badyear
/sbin/hwclock --rtc=$dev --systz
ctrl x, y, enter
Sync time from Pi to RTC
sudo hwclock -w
sudo hwclock -r
sudo reboot
A side effect of doing all this is now set-time.py and get-time.py no longer work, they error out.
If you remark out the dtoverlay=i2c-rtc,rv3028 entry in config.txt and reboot, they will then work though.

A workaround is to set the backup switchover register (see application manual) to desired value manually

e.g. ic2set -f -y 1 0x52 0x37 0x04

However, with dtoverlay=i2c-rtc,rv3028 I kept checking with i2cdump periodically (on a raspberry pi doing nothing) and found eventually (e.g. 12h) the register resets to disable switchover mode. So you would need to keep doing ic2set periodically.

I also tried with dtoverlay=i2c-rtc,rv3028,backup-switchover-mode=1, but I found at sometimes on reboot the register value was not persisting, so would have to be re-set with ic2set.

I may either
a) not use any dtoverlay, and likely just interact with the RTC via https://github.com/pimoroni/rv3028-python
b) use dtoverlay=i2c-rtc,rv3028, but surround any interaction with the hwclock with modprobe/rmmod/i2cset to ensure the driver is only active when I want it to be, and make sure the switchover mode is set correctly when I'm done

So far with the driver unloaded the backup switchover register persists consistently

@Gadgetoid You have some experience with this device and the driver - do you understand why the switchover mode is being disabled?

in this other thread (https://github.com/pimoroni/rv3028-python/issues/9) I suggest it could be related to eeprom writing

I wonder if it's the driver, or a nuance of the RTC. p.39 of the application manual (3.15.6 EEPROM BACKUP REGISTER), Symbol BSM states "To read/write from/to the EEPROM, the user has to disable the Backup Switchover function by setting the BSM field to 00 or 10 (see routine in EEPROM READ/WRITE CONDITIONS)". So I wonder if the driver does some EEPROM write which ends up resetting BSM to off.

response from @Gadgetoid

So I wonder if the driver does some EEPROM write which ends up resetting BSM to off.

I can't see any evidence of this happening in the rv3028 driver, but it does include functions for reading/writing the EEPROM.

Interestingly this would suggest a bug in rv3028_eeprom_write since it does not appear to reset backup-switchover-mode.

Backup Switchover Mode is enabled - depending on the config property - here:

https://github.com/raspberrypi/linux/blob/ab8652c03fa081b27de7e28a74c2536cb2aa3e5b/drivers/rtc/rtc-rv3028.c#L676-L689

Reading the driver, the BSM bits appear to be handled correctly - the register that contains them (RV3028_BACKUP) is only written using regmap_update_bits with suitable masks, so the BSM state appears not to be accidentally dropped. If there is a bug in the driver, it's not obvious.

Thanks for all the replies. I am seeing the same with i2cdump that the switchover mode is occasionally being reset to 10. I'll look through the driver.

You might be able to use the kernel's trace mechanism to monitor the I2C traffic. There's a brief example here: https://riptutorial.com/linux-kernel/example/11983/tracing-i2c-events

cool, I've enabled tracing and will report back any findings :)

Woohoo, I managed to catch it in action with trace. Here's the trace output between my i2cdump calls where it showed the register change. According to the i2cdumps, the register 0x37 changed from 0x04 to 0x90.

This is suspiciously around when the clock goes from 23rd to 00th hour.

     kworker/0:2-6579  [000] .... 18117.852162: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-54-58-23-02-10-02-20]
     kworker/0:2-6579  [000] .... 18117.853874: i2c_result: i2c-1 n=1 ret=1
     kworker/0:2-6579  [000] .... 18117.853882: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:2-6579  [000] .... 18117.853885: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:2-6579  [000] .... 18117.854727: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:2-6579  [000] .... 18117.854728: i2c_result: i2c-1 n=2 ret=2
     kworker/0:2-6579  [000] .... 18117.854742: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:2-6579  [000] .... 18117.854743: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:2-6579  [000] .... 18117.855584: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:2-6579  [000] .... 18117.855586: i2c_result: i2c-1 n=2 ret=2
     kworker/0:2-6579  [000] .... 18117.855592: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:2-6579  [000] .... 18117.855593: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:2-6579  [000] .... 18117.857516: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [54-58-23-02-10-02-20]
     kworker/0:2-6579  [000] .... 18117.857518: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6717  [000] .... 18814.908913: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-31-10-00-04-11-02-20]
     kworker/0:1-6717  [000] .... 18814.910620: i2c_result: i2c-1 n=1 ret=1
     kworker/0:1-6717  [000] .... 18814.910631: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:1-6717  [000] .... 18814.910634: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:1-6717  [000] .... 18814.911477: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:1-6717  [000] .... 18814.911479: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6717  [000] .... 18814.911492: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:1-6717  [000] .... 18814.911493: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:1-6717  [000] .... 18814.912334: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:1-6717  [000] .... 18814.912336: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6717  [000] .... 18814.912342: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:1-6717  [000] .... 18814.912343: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:1-6717  [000] .... 18814.914271: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [31-10-00-04-11-02-20]
     kworker/0:1-6717  [000] .... 18814.914273: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 19510.893096: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-07-22-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 19510.894802: i2c_result: i2c-1 n=1 ret=1
     kworker/0:0-6748  [000] .... 19510.894813: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 19510.894815: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 19510.895657: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 19510.895659: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 19510.895672: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 19510.895673: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 19510.896513: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 19510.896515: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 19510.896520: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:0-6748  [000] .... 19510.896521: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:0-6748  [000] .... 19510.898445: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [07-22-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 19510.898446: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20206.856625: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-43-33-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 20206.858334: i2c_result: i2c-1 n=1 ret=1
     kworker/0:0-6748  [000] .... 20206.858341: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 20206.858343: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 20206.859185: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 20206.859187: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20206.859200: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 20206.859202: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 20206.860043: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 20206.860045: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20206.860051: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:0-6748  [000] .... 20206.860052: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:0-6748  [000] .... 20206.861975: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [43-33-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 20206.861977: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20904.950027: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-21-45-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 20904.951734: i2c_result: i2c-1 n=1 ret=1
     kworker/0:0-6748  [000] .... 20904.951742: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 20904.951744: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 20904.952587: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 20904.952589: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20904.952601: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:0-6748  [000] .... 20904.952602: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:0-6748  [000] .... 20904.953443: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:0-6748  [000] .... 20904.953445: i2c_result: i2c-1 n=2 ret=2
     kworker/0:0-6748  [000] .... 20904.953451: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:0-6748  [000] .... 20904.953453: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:0-6748  [000] .... 20904.955383: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [21-45-00-04-11-02-20]
     kworker/0:0-6748  [000] .... 20904.955385: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6971  [000] .... 21599.933329: i2c_write: i2c-1 #0 a=052 f=0000 l=8 [00-56-56-00-04-11-02-20]
     kworker/0:1-6971  [000] .... 21599.935041: i2c_result: i2c-1 n=1 ret=1
     kworker/0:1-6971  [000] .... 21599.935049: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:1-6971  [000] .... 21599.935051: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:1-6971  [000] .... 21599.935896: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:1-6971  [000] .... 21599.935898: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6971  [000] .... 21599.935913: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [0e]
     kworker/0:1-6971  [000] .... 21599.935914: i2c_read: i2c-1 #1 a=052 f=0001 l=1
     kworker/0:1-6971  [000] .... 21599.936754: i2c_reply: i2c-1 #1 a=052 f=0001 l=1 [10]
     kworker/0:1-6971  [000] .... 21599.936756: i2c_result: i2c-1 n=2 ret=2
     kworker/0:1-6971  [000] .... 21599.936762: i2c_write: i2c-1 #0 a=052 f=0000 l=1 [00]
     kworker/0:1-6971  [000] .... 21599.936763: i2c_read: i2c-1 #1 a=052 f=0001 l=7
     kworker/0:1-6971  [000] .... 21599.938686: i2c_reply: i2c-1 #1 a=052 f=0001 l=7 [56-56-00-04-11-02-20]
     kworker/0:1-6971  [000] .... 21599.938688: i2c_result: i2c-1 n=2 ret=2

That's an interesting tracing for two reasons:

  1. It consists solely of a block of four operations that repeats every 11.6 minutes:
    Write the time
    Read the status register (always 0x10 = UF)
    Read the status register again (still 0x10 = UF)
    Read back the time, which is unchanged.
    Pause
    There's no indication that the device is capable of counting.
  2. It doesn't show access to any other registers, so i2cdump appears to be bypassing the tracing (unless the period covered by the trace didn't cover a time when i2cdump was used).

Just got this back from MicroCrystal. Haven't had a chance to look into it yet, but might expalin what is being seen near midnight:

Anyway, the “automatic backup switchover” is disabled by default and must be set in the “Configuration EEPROM” (address 0x37), not only in the “RAM mirror”!
Indeed, the switchover function uses the RAM Mirror settings which are updated by the EEPROM once a day at around midnight or after a power fail.
Please have a look at the attached application manual chapter 4.2 for Backup Switchover function and chapter 4.6 for EEPROM read/write.

Please note that the Linux (kernel) driver developed by Micro Crystal does not implement the switchover control.
You must unload the rtc_rv-3028 module ($ sudo rmmod rtc_rv3028) and set the field BSM = 01 or 11 “manually” with the i2C get/set (Linux I2c-tools) commands

If you come up with a patch then I'd be happy to host it here for testing until it is upstreamed.

  1. It doesn't show access to any other registers, so i2cdump appears to be bypassing the tracing (unless the period covered by the trace didn't cover a time when i2cdump was used).

Note: I only pasted the trace output between the i2cdump calls where I saw the change (not including the trace output from i2cdump)

App Manual interesting bits:

3.15 CONFIGURATION EEPROM WITH RAM MIRROR REGISTERS

All Configuration EEPROM at addresses 2Bh and 30h to 37h are memorized in the EEPROM and mirrored in the RAM. Functions become active as soon as the RAM mirror bytes are written.

3.13 EEPROM MEMORY CONTROL REGISTERS - 27h EE Command

Configuration EEPROM registers or to read or write from/to a single EEPROM Memory byte.
Before using this commands, the automatic refresh function has to be disabled (EERD = 1) and the busy status bit EEbusy has to indicate, that the last transfer has been finished (EEbusy = 0). Before entering the command 11h, 12h, 21h or 22h, EECMD has to be written with 00h

includes commands for UPDATE (all ram -> eeprom), REFRESH (all eeprom -> ram), as well as individual bytes

4.6 EEPROM READ/WRITE

4.6.2 Automatic refresh (all eeprom -> ram)

To keep the integrity of the configuration data, all data of the Configuration RAM are refreshed by the data in the Configuration EEPROM each 24 hours, at date increment (at the beginning of the last second before midnight).

4.6.3 Update (all ram -> eeprom)

Before starting to change the configuration stored in the EEPROM, the auto refresh of the registers from the EEPROM has to be disabled by writing 1 into the EERD control bit.

3.15.6 EEPROM BACKUP REGISTER

Backup Switchover Mode
To read/write from/to the EEPROM, the user has to disable the Backup Switchover function by setting the BSM field to 00 or 10 (see routine in EEPROM READ/WRITE CONDITIONS)

If I interpret this correct, by design, to write to the EEPROM you must disable backup switchover. The BSM value in EEPROM can only be "off". Any refresh of eeprom -> ram will effectively turn off BSM overwriting whatever the setting is to "off". Currently (default?) automatic refresh is enabled, so once a day at midnight, the BSM value stored in in the ram mirror is reset to "off".

Sounds like auto refresh needs disabled, since this is counterintuitive (if you want BSM enabled, you don't want it to reset once a day).

So still with dtoverlay=i2c-rtc,rv3028 without the backup-switchover-mode parameter, I am reproducing the loss of BSM:

# enable BSM
sudo i2cset -f -y 1 0x52 0x37 0x04

# confirm register 0x37 is set correctly
sudo i2cdump -f -y 1 0x52

# change date to 1 second before midnight (according to the RTC - this is offset from my local timezone)
sudo date -s "2020-02-11 15:59"
sudo hwclock -w

# confirm register 0x37 is set correctly
sudo i2cdump -f -y 1 0x52

# wait for 00:00
sudo hwclock -r

# now see register 0x37 is no longer set correctly
sudo i2cdump -f -y 1 0x52

Now I can fix this by disabling EERD (0Fh Control 1 register) by writing 1 to bit 3 with sudo i2cset -f -y 0x52 0x0f 0x08 and seeing the value in register 0x37 persist across 23:59 to 00:00

I can confirm this works.

So that is one issue. The other seems to be that the backup-switchover-mode parameter does not actually seem to work (does not set the register 0x37). In fact I could not get the warning on this line to show up even using invalid values https://github.com/raspberrypi/linux/blob/ab8652c03fa081b27de7e28a74c2536cb2aa3e5b/drivers/rtc/rtc-rv3028.c#L687

Any thoughts?

It's because the overlay treats backup-switchover-mode as a u32, which is sensible, but the driver reads it as a u8. This wouldn't be a problem if device tree stored values in little-endian, but in big endian it ends up reading one of the leading zeroes.

Sorry for the radio silence here, I've been juggling other things- looks like I'd probably broken my own testing efforts by mixing experiments with Python and kernel drivers.

I last touched this stuff a good year or so ago, so it'll take me a while to get up to speed but I think - with the very useful information supplied above (thank you) - I should be able to fix these bugs.

Edit: This patch should- I think- fix the issue ( as elucidated by pelwell) with reading backup-switchover-mode from the config:

diff --git a/drivers/rtc/rtc-rv3028.c b/drivers/rtc/rtc-rv3028.c
index b69d8e6408aa..48b5d1332a7a 100644
--- a/drivers/rtc/rtc-rv3028.c
+++ b/drivers/rtc/rtc-rv3028.c
@@ -602,7 +602,7 @@ static int rv3028_probe(struct i2c_client *client)
        struct rv3028_data *rv3028;
        int ret, status;
        u32 ohms;
-       u8 bsm;
+       u32 bsm;
        struct nvmem_config nvmem_cfg = {
                .name = "rv3028_nvram",
                .word_size = 1,
@@ -674,7 +674,7 @@ static int rv3028_probe(struct i2c_client *client)
                return ret;

        /* setup backup switchover mode */
-       if (!device_property_read_u8(&client->dev, "backup-switchover-mode",
+       if (!device_property_read_u32(&client->dev, "backup-switchover-mode",
                                     &bsm))  {
                if (bsm <= 3) {
                        ret = regmap_update_bits(rv3028->regmap, RV3028_BACKUP,

Now from what I can tell regarding EEPROM <-> RAM, there's no hard requirement to completely disable backup-switchover mode in order to write EEPROM values. The EEPROM READ/WRITE CONDITIONS (section 4.6.8) seem to (albeit with all the clarity of a bucket of mud) stipulate that the device doesn't enter VBAT mode during an EEPROM write, and should be powered by Vdd >= 1.5v.

Or- rather - it seems that Vdd can drop below the threshold required to stably write the EEPROM (1.5v), while still operating normally until it completes a switch to VBAT mode. This is noteworthy since the minimum VBACKUP voltage is 1.1v which is lower than this threshold- so a condition could occur where a drop on Vdd is sufficient to cause an unstable EEPROM write, but not sufficient enough to kick in VBAT mode.

I'm not entirely sure why you'd ever want a periodic refresh from EEPROM -> RAM, but then I'm not sure it's entirely safe to disable. That said it would appear to be much simpler (given the idiosyncracies of this kernel driver code) to disable EEPROM -> RAM auto-refresh than issue the sequence of commands and waits required to save the BSM bits into backup EEPROM.

I guess we have two easy choices:

  1. Disable EEPROM -> RAM refresh implicitly when BSM mode is set
  2. Add an option (like disable-eeprom-ram-refresh) that explicitly disables this feature and require it

And one... difficult one:

  1. Set the BSM register and refresh the EEPROM from RAM.

Choice 3 requires a sequence similar to that detailed in (MIT licensed) code here: https://lang-ship.com/reference/Arduino/libraries/RTC_RV-3028-C7_Arduino_Library/class_r_v3028.html#a9cbc9a009d4e5dbfeb29e366140be42b

All of these options have drawbacks.

  1. potentially silently disables a feature the user may otherwise be relying upon- can we assume that the RV3028 is "owned" by the system and just "do the right thing?"
    2, requires the user to understand what disable-eeprom-ram-refresh is there for, and makes the BSM mode setting weirdly interdependent upon it
  2. may cause a write to EEPROM at the precise moment the user is pulling the power, and thus result in corruption of the EEPROM values- since the user has no knowledge of when the kernel might be triggering this write action (is this paranoia?).

Anyway, I don't want to work in a vacuum here, I'm extremely new to kernel module development, so I patiently await your feedback! Thanks all.

I'd imagine if anyone is using the EEPROM or features other than basic timekeeping they are writing their own custom code to talk over i2c and set various registers.

I'd vote for # 1 and include a note in the overlay doc that autorefresh is implicitly disabled

If it helps anyone, my hwclock-start.service currently calls this script on every boot:

#!/bin/bash

# disable auto refresh
i2cset -f -y 1 0x52 0x0F 0x08
# enable BSM
i2cset -f -y 1 0x52 0x37 0x04

# sync time (fails on first install until hwclock -w)
# if this fails, set date to 2000
/sbin/hwclock --hctosys --utc || { date -s "2000-01-01 00:00"; hwclock -w; }

I asked Micro Crystal about the backup, and their application engineer said that the EEPROM->RAM refresh allows automatic self-recovery of configurations in the event of total power loss, since a refresh occurs at power-on. The datasheet also says the automatic refresh is "to keep the integrity of the configuration data", which makes sense since capacitor-based RAM slowly leaks charge and needs to be occasionally rewritten to keep its data.

So the RAM->EEPROM update shouldn't actually be needed, as long as the Pi writes the configuration RAM at the appropriate times (on power on and once every 24 hours.)

As for the options you mentioned:

  1. As above; will mess with any user-set values in the configuration RAM.
  2. Maybe instead add enable-eeprom-ram-refresh or two different values for backup-switchover-mode? As long as the kernel driver is refreshing the RAM itself, the EEPROM->RAM refresh would be an "advanced feature" only needed by users who are directly setting their own configurations.
  3. The "proper" way to do it. I'm pretty sure the EEPROM only needs to be written once, so as long as the EEPROM is written immediately when the Pi boots or similar, the risk of power being pulled during an EEPROM write should be minimal.

@amahoneyLIT: As a newbie to writing such custom code, is it safe to use i2cset -f like that without disabling the driver first? I'm currently using it to set the alarm on the RV3028, and if I could do that without rmmod and modprobe that would be preferable.

@TrevorMuraro I was doing the rmmod/modprobe dance before, but haven't had any trouble using "-f" yet. Maybe I'll add it back to my script; I was also tired of typing those commands. Probably safer to use rmmod/modprobe though, especially if you don't have to type it out.

Because I'm feeling completionistic, I wrote a script that sets the BSM in EEPROM too:

#!/bin/bash

# Thanks to https://lang-ship.com/reference/Arduino/libraries/RTC_RV-3028-C7_Arduino_Library/class_r_v3028.html#a9cbc9a009d4e5dbfeb29e366140be42b
# And the folks at https://github.com/raspberrypi/linux/issues/2912

function wait_for_EEBusy_done {
   busy=$((0x80))
   while (( busy == 0x80 ))
   do
      status=$( i2cget -y 1 0x52 0x0E )
      busy=$((status & 0x80))
   done
}

rmmod rtc_rv3028

wait_for_EEBusy_done

# disable auto refresh
register=$( i2cget -y 1 0x52 0x0F )
writeback=$((register | 0x08))
i2cset -y 1 0x52 0x0F $writeback

# enable BSM in level switching mode
register=$( i2cget -y 1 0x52 0x37 )
writeback=$((register | 0x0C))
i2cset -y 1 0x52 0x37 $writeback

# update EEPROM
i2cset -y 1 0x52 0x27 0x00
i2cset -y 1 0x52 0x27 0x11

wait_for_EEBusy_done

# reenable auto refresh
register=$( i2cget -y 1 0x52 0x0F )
writeback=$((register & ~0x08))
i2cset -y 1 0x52 0x0F $writeback

wait_for_EEBusy_done

modprobe rtc_rv3028

Cut power (with no backup) and plugged it back in, register 0x37 was still set.

Best part? You only need to run it once and it's set forever.

Sorry @Gadgetoid, I didn't see your edit with the patch, because GitHub.

I prefer option 1. Automatically writing the EEPROM sounds dangerous, and I think that anyone using the BSM option is probably clued up enough to be able to find this thread should anything go wrong (which I doubt).

Option 3:
Setting the backup switchover mode (bsm) in RAM is a bit weak, without also disabling the
EEPROM->RAM mirror function. The mirror function is there for a reason, so maybe not the best idea to disable it. A better way to do it is to read the EEPROM bsm, and if it's not set, then set it. For reference, this is an implementation of option 3 mentioned above. Writing eeprom on every boot-up would be a risk of wearing out flash and higher risk of memory-write during power-down. If you just set bsm once when the device tree value changes, that's not too bad.

The code attached below reads the device tree parameter: backup-switchover-mode = <3>; If it doesn't match the eeprom value, we do the eeprom write. The low voltage flag must also be cleared, by writing to the seconds register by setting date/time, and then "hwclock -w".

Perhaps someone can take a look at the attached patch and clean it up for proper submission. I've tested it, and it seems to work as intended. This is a co-authored work between myself and working partner. We don't have the time to clean it up, or figure out formatting the code for a proper release, so please bear with me. I've also realized the documentation should have been updated in the patch to be formally correct.

rv-3028.patch.txt

I notice that your patch duplicates and extends @Gadgetoid's patch, but excludes updating the BACKUP register. Is this intentional?

And do you (or anyone reading) think the TCE and TCR values should also be written to the EEPROM? If so, the EEPROM update for both/either could be moved and made to service both needs.

@pelwell All I did was change @Gadgetoid 's patch to read/write the EEPROM value instead of the RAM value for BSM. The calls to rv3028_eeprom_write and rv3028_eeprom_read appear to take care of the refresh and flags, and since the routines are already there, I figured might as well use them. We don't need to write the RAM BACKUP value anymore. It's taken care of in the rv3028_eeprom_write when the RAM refresh is turned back on. With that in mind, I guess any previously set RAM values would be overwritten in that register. It's fine for now since the trickle charge is setup after the BSM setup.

I suppose this could be extended to support the TCR and TCE in EEPROM, great idea. I'm not using those functions at all, as I'm using external charger.

OK - I'll update my work-in-progress and turn it into a Pull Request for testing.

@pelwell This might need a bit more thinking to get it right for "all" the use cases. It doesn't make sense to extend to TCR and TCE in separate EEPROM writes. The EEPROM backup register should probably only be written once and only if one of the individual values doesn't match.

Maybe a better approach is just a single device-tree value for BACKUP of 0x00 to 0xFF representing the entire register? Trying to keep things simple, but functional for different use cases. Other ideas welcome of course.

I've tried to be a bit smarter than that: https://github.com/raspberrypi/linux/pull/3616

So do we really need to write to eeprom and use auto-refresh?

Per @TrevorMuraro 's response:

I asked Micro Crystal about the backup, and their application engineer said that the EEPROM->RAM refresh allows automatic self-recovery of configurations in the event of total power loss, since a refresh occurs at power-on. The datasheet also says the automatic refresh is "to keep the integrity of the configuration data", which makes sense since capacitor-based RAM slowly leaks charge and needs to be occasionally rewritten to keep its data.

So is the risk that the RTC registers in RAM degrade after some unknown period much greater than 24h if we don't auto-refresh?

backup-switchover-mode parameter does not actually seem to work (does not set the register 0x37). In fact I could not get the warning on this line to show up even using invalid values
dev_warn(&client->dev, "invalid backup switchover mode valuen");

@pelwell is this still an issue?

It's because the overlay treats backup-switchover-mode as a u32, which is sensible, but the driver reads it as a u8. This wouldn't be a problem if device tree stored values in little-endian, but in big endian it ends up reading one of the leading zeroes.

Yes, it is still an issue - I've updated the PR accordingly.

Has anyone managed to try the patch in PR #3616?

Anyone? I'd like to merge the patch in #3616, but not without some confirmation.

Anyone? I'd like to merge the patch in #3616, but not without some confirmation.

I just tried it with the 4.19.93, but modified the patch a tiny bit to still include rtc-core.h. I am using it with dtoverlay=i2c-rtc,rv3028,backup-switchover-mode=1 but I always get the "Voltage low, data loss detected." error on startup. With that said, my custom hardware is not really verified at this point so it could be a HW issue.

Let me know if there is something you'd like me to try out.

I'm limited on time but may be able to test it, depending on how complicated it is to build (which I also don't know how to do). Is there a guide someone can point me to?

Building kernel info:
https://www.raspberrypi.org/documentation/linux/kernel/building.md

assuming that works you can apply the suggested patch with:

curl -L https://github.com/raspberrypi/linux/pull/3616/commits/78c4fadc9398ead67ccf302db555df6d55914dac.patch | git am -3

I got it to work for me now. All I did was add a ram_refresh function that I called after the eeprom write that you did in #3616. Since the setting has to be in RAM to be active, to my understanding, that step is necessary if you don't want to wait until the next automatic refresh for it to take effect. @pelwell I can create a PR if you want me to.

Yes, a single PR with #3616 and your own patch would be great.

3797 is the one.

Was this page helpful?
0 / 5 - 0 ratings