Files
ZephCore/zephcore/adapters/clock/ZephyrRTCDiscover.h
T
Pieter ViljoenandClaude Opus 5.5 ad2df09dc5 rak4631: use a fitted RAK12002 RTC, and keep its backup switchover enabled
The RAK4631 overlay declared no zephcore,rtc-i2c candidate, so a fitted
RAK12002 (an RV3028 at 0x52) was never probed or written, and every
power cut started the node at 1970. Declaring it is not enough: the
RV3028 ships with backup switchover disabled (EEPROM 37h BSM = 00), so a
power cut still loses the time.

The overlay now declares the RV3028 with the zero-mask rtc-i2c.dtsi uses
for it, and a new optional rv3028-eeprom-config descriptor property
holds (register, mask, value) triplets for its EEPROM configuration
registers 35h-37h. The RAK4631 sets direct switchover with the trickle
charger on (TCE, BSM = 01, TCR 3 kOhm), as the RV-3028-C7 Application
Manual (Rev. 1.4) 7.3 prescribes for a capacitor backup, FEDE set as
3.15.6 says it always should be, and CLKOUT off. The configuration has
to live in EEPROM: the chip reloads its configuration RAM from EEPROM at
power-on and every midnight (4.6.2).

The store runs only when a read identified the chip (RTC_FOUND, not an
adoption on a failed second read) and a BSF-checked time read just
before it showed no switchover. It follows 3.15.6 (BSM 00 or 10 for any
EEPROM access): EERD held, switchover off in RAM, each configured byte
compared against the EEPROM with a single-byte read and written only if
it differs, and a closing Refresh that restores the stored switchover
mode, read back on every boot. 36h, the factory trim, is never written.
Switchover is turned back on only once EEbusy reads 0; otherwise it
stays off until a retry or the daily refresh. A failed store sets the
config in RAM (also only once EEbusy reads 0). A failed or skipped store
is retried every 10 minutes, at most 3 times per boot, each retry
identifying the chip again first.

A switchover disables the chip's I2C mid-transfer (4.2), so at a node
with rv3028-eeprom-config the time is read at boot and written on each
sync with BSF cleared before and checked after. A read that saw a
switchover gives no time. A write sets year 00h first and the real year
last, so a power loss cutting it between its first and last byte leaves
year 2000 and the next boot takes no time from it, and a write not
confirmed is repeated every 5 s, up to 12 times, with the time run on.

All of the RV3028 code is compiled only when some node carries
rv3028-eeprom-config, so other boards, thinknode_m1 among them, build
the same RTC object as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:22:14 -07:00

80 lines
3.8 KiB
C

/*
* SPDX-License-Identifier: MIT
*
* Boot-time hardware-RTC auto-discovery (compact raw-I2C reader).
*
* Probes every I2C RTC chip declared with the "zephcore,rtc-i2c" binding
* (boards/common/rtc-i2c.dtsi, or a board's own overlay). Chips that aren't
* physically present fail the probe and are skipped — like the environment
* sensors.
*
* If a present chip holds a valid time, zephcore_rtc_restore() returns it so
* the soft clock can be seeded at boot (shown tagged "L" — local). Every
* ZephyrRTCClock::setCurrentTime() writes it back via zephcore_rtc_save(), so
* time survives the next power-off.
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Probe the declared RTC chips in order, stopping at the first that holds a
* sane time (year >= 2025 and its power-loss flag clear): store its Unix
* epoch in *epoch_out and return true. The first chip found (valid time or
* not) is remembered as the write-back target. A failed first read means
* nothing is there, and a first read of all 0xFF (an erased EEPROM) is
* skipped. Otherwise a device is passed over only if two reads each show it
* is not that RTC: a bit set that the data sheet shows as 0 (the
* descriptor's zero-mask), seconds, minutes, date or month out of range
* while the power-loss flag does not read as set (an unreadable flag counts
* as not set), or, on the second read, all 0xFF. A failed second read never
* rules a device out. A time is taken only from a clean read whose fields,
* hours and year are each in range (12-hour mode is not decoded). Returns
* false if none present or no trustworthy time is held.
*
* At a descriptor with rv3028-eeprom-config, the time is read once more
* with the chip's backup switch flag (BSF) cleared before and checked
* after, and is not taken if the read failed or BSF was set. A chip adopted
* on a read that identified it (not on a failed second read), with BSF
* clear, has that config stored at once: after the power-on refresh (up to
* ~66 ms), backup switchover is turned off while the EEPROM is read, each
* byte that differs is written, and a refresh from EEPROM restores it.
* Measured on a RAK4631: about 14 ms when nothing is written, 52 ms for two
* bytes, then up to 2 ms for DSM to react (4.2.2). If the store fails, the
* config is set in RAM, which lasts until the chip's next refresh from
* EEPROM (daily while EERD is clear), and only once EEbusy reads 0;
* otherwise switchover stays as the store left it, off once the store has
* disabled it, until a retry or that refresh. A failed store, or one
* skipped at boot, is retried on the system work queue every 10 minutes, at
* most 3 times, each retry identifying the chip again first.
*/
bool zephcore_rtc_restore(uint32_t *epoch_out);
/*
* Persist an authoritative epoch to the discovered RTC chip and clear its
* power-loss flag. No-op if no RTC was discovered. Safe to call often, but
* intended only for real syncs (GPS/app/CLI), not per-packet clock nudges.
* If restore adopted nothing but skipped a candidate whose first read was all
* 0xFF, the first save probes again, once per boot, in the caller's context.
* A device ruled out on its second read is not probed again for that reason.
*
* At a descriptor with rv3028-eeprom-config the time is written with BSF
* cleared before and checked after, year 00h first and the real year last,
* so a write cut by a power loss reads at boot as "time not yet set". A
* write not confirmed is repeated every 5 s on the system work queue, with
* the time run on, until one is confirmed, a newer save replaces it, or 12
* repeats (13 attempts in all) have failed. Call only from the system work
* queue: the repeat shares state with this call.
*/
void zephcore_rtc_save(uint32_t epoch);
#ifdef __cplusplus
}
#endif