upstream sync and gps stuff

This commit is contained in:
liquidraver committed 2026-10-02 23:10:09 +02:00
1 parent 5b779478b0
commit f9d01cf33a
21 files changed
+296 -45

No files matched your search

+33 -1
View File
@@ -23,16 +23,48 @@ static void rtc_save_work_fn(struct k_work *work)
static K_WORK_DEFINE(s_rtc_save_work, rtc_save_work_fn);
#endif
/* The last time read, kept across a reset that keeps RAM (reboot, crash,
* watchdog), as upstream's ESP32RTCClock does. ESP32 keeps it in RTC slow
* memory: the bootloader may reuse ordinary noinit RAM. Power-on leaves
* garbage, which the magic rejects. */
#if defined(CONFIG_SOC_FAMILY_ESPRESSIF_ESP32)
#define RTC_BACKUP_ATTR Z_GENERIC_SECTION(.rtc_noinit)
#else
#define RTC_BACKUP_ATTR __noinit
#endif
static RTC_BACKUP_ATTR uint32_t _rtc_backup_time;
static RTC_BACKUP_ATTR uint32_t _rtc_backup_magic;
#define RTC_BACKUP_MAGIC 0xAA55CC33
#define RTC_TIME_MIN 1772323200 // 1 Mar 2026
ZephyrRTCClock::ZephyrRTCClock()
{
if (_rtc_backup_magic == RTC_BACKUP_MAGIC && _rtc_backup_time > RTC_TIME_MIN) {
epoch_offset = _rtc_backup_time;
}
}
uint32_t ZephyrRTCClock::getCurrentTime()
{
uint32_t uptime_sec = (uint32_t)(k_uptime_get() / 1000);
return epoch_offset + uptime_sec;
uint32_t now = epoch_offset + uptime_sec;
/* upstream does this in tick(), which nothing calls here */
if (now > RTC_TIME_MIN && now != _rtc_backup_time) {
_rtc_backup_time = now;
_rtc_backup_magic = RTC_BACKUP_MAGIC;
}
return now;
}
void ZephyrRTCClock::seedCurrentTime(uint32_t time)
{
uint32_t uptime_sec = (uint32_t)(k_uptime_get() / 1000);
epoch_offset = time - uptime_sec;
/* As upstream's setCurrentTime(): unconditional, so a set below
* RTC_TIME_MIN (clkreboot) replaces the backup and is not restored. */
_rtc_backup_time = time;
_rtc_backup_magic = RTC_BACKUP_MAGIC;
}
void ZephyrRTCClock::setCurrentTime(uint32_t time)
+2
View File
@@ -11,6 +11,8 @@ namespace mesh {
class ZephyrRTCClock : public RTCClock {
public:
/* Starts from the time kept across the last reset, if any (see the .cpp). */
ZephyrRTCClock();
uint32_t getCurrentTime() override;
/* Also written to the board's hardware RTC, if it has one, as upstream's
* AutoDiscoverRTCClock does: coalesced on the system work queue, so
+37 -8
View File
@@ -9,8 +9,8 @@
*
* Power strategy:
* - Direct GPIO toggle via gps-enable alias (all boards)
* - T1000-E warm standby: VRTC stays powered during standby, preserving
* ephemeris/almanac/RTC in backup RAM for fast re-acquisition (3-8s vs 15-45s)
* - T1000-E / MeshTracker X1: AG3335 RTC backup sleep during standby (VRTC
* stays powered, see gps_power.cpp) for fast re-acquisition
* - GNSS UARTE suspended (device PM) while GPS is off/standby — releases
* HFCLK on nRF52840 (~0.5-1 mA), resumed before every wake
* - Full power-off only on user-disable or System OFF
@@ -83,6 +83,8 @@ static uint64_t standby_interval_ms = 0; /* How long standby lasts (for next-wak
static const uint32_t gps_acquire_timeout_ms = CONFIG_ZEPHCORE_GPS_FIX_TIMEOUT_SEC * 1000U;
static const uint32_t gps_first_fix_timeout_ms = CONFIG_ZEPHCORE_GPS_FIRST_FIX_TIMEOUT_SEC * 1000U;
static uint32_t gps_wake_interval_ms = CONFIG_ZEPHCORE_GPS_POLL_INTERVAL_SEC * 1000U;
/* prefs.gps_standby_max, see gps_set_standby_max_sec() */
static uint32_t gps_standby_max_sec = GPS_STANDBY_MAX_DEFAULT_SEC;
/* Duty cycle vs always-on: a non-zero standby interval duty-cycles; interval 0
* keeps the GPS in continuous acquisition (never sleeps) so it streams fresh
@@ -528,7 +530,11 @@ static void gps_go_to_standby(void)
* RTC survive the cut and re-acquisition is a warm/hot start, not cold.
* Other non-GPIO boards: software sleep via UART commands (PMTK + UBX). */
gps_reapply_cancel();
gps_module_power(false);
/* A standby-pin board keeps the supply only for a short interval: its
* Standby draws all the time, a cold start only for a few minutes, so
* past gps_standby_max_sec the supply cut is the cheaper one. */
gps_module_power(false, !HAS_GPS_WAKEUP ||
wake_interval / 1000U <= gps_standby_max_sec);
/* Module is off/asleep — release the UART until the next wake
* (nRF: drops the HFCLK request held by the armed RX). */
@@ -878,12 +884,12 @@ void gps_enable(bool enable)
k_work_cancel_delayable(&gps_timeout_work);
gps_reapply_cancel();
/* Power off GPS — warm standby if VRTC available (Arduino sleep_gps),
* full power off otherwise. Warm standby preserves ephemeris/RTC
* in AG3335 backup RAM for fast re-acquisition (1-8s vs 15-45s).
/* Power off GPS — AG3335 RTC backup sleep where the board has it
* (Arduino stop_gps), full power off otherwise.
* Boards with no power line get the UART sleep commands, as in
* the duty cycle's standby. */
gps_module_power(false);
* the duty cycle's standby. A standby-pin board cuts the supply
* here: its Standby still draws, and this off has no end. */
gps_module_power(false, !HAS_GPS_WAKEUP);
/* GPS is off until re-enabled — release the UART. */
gps_uart_set_power(false);
@@ -941,6 +947,29 @@ uint32_t gps_get_poll_interval_sec(void)
#endif
}
bool gps_has_standby_pin(void)
{
return HAS_GPS_WAKEUP;
}
uint32_t gps_get_standby_max_sec(void)
{
#if HAS_GNSS
return gps_standby_max_sec;
#else
return 0;
#endif
}
void gps_set_standby_max_sec(uint32_t sec)
{
#if HAS_GNSS
gps_standby_max_sec = sec;
#else
ARG_UNUSED(sec);
#endif
}
void gps_set_poll_interval_sec(uint32_t interval)
{
#if HAS_GNSS
+7
View File
@@ -105,6 +105,13 @@ void gps_ensure_power_state(bool should_be_enabled);
void gps_get_position(struct gps_position *pos);
uint32_t gps_get_poll_interval_sec(void);
void gps_set_poll_interval_sec(uint32_t interval);
/* Boards with a module standby pin beside a supply switch (gps-wakeup alias):
* duty intervals up to this many seconds use the pin (supply kept, hot start
* on wake), longer ones cut the supply. 0 = always cut. Takes effect at the
* next standby. */
bool gps_has_standby_pin(void);
uint32_t gps_get_standby_max_sec(void);
void gps_set_standby_max_sec(uint32_t sec);
/* Get RTC time from GPS (returns Unix timestamp, 0 if no valid time) */
int64_t gps_get_utc_time(void);
+7
View File
@@ -62,6 +62,13 @@
#define HAS_GPS_POWER_CONTROL 0
#endif
/* Module standby pin beside a supply switch (gps-wakeup alias, see gps_power.cpp). */
#if DT_NODE_EXISTS(DT_ALIAS(gps_wakeup))
#define HAS_GPS_WAKEUP 1
#else
#define HAS_GPS_WAKEUP 0
#endif
/* GPS powered from a PMU regulator rail (chosen zephcore,gps-power). */
#if DT_NODE_EXISTS(DT_CHOSEN(zephcore_gps_power))
#define HAS_GPS_POWER_REGULATOR 1
+88 -8
View File
@@ -25,8 +25,8 @@ LOG_MODULE_DECLARE(zephcore_gps, CONFIG_ZEPHCORE_GPS_LOG_LEVEL);
* every wake is a warm start, not a cold one. (Backup mode, the deeper
* state, requires cutting VCC while V_BCKP holds the RTC domain; this
* board has no VCC switch, so Standby is the floor available to us.)
* - T1000-E (AG3335): GPS_EN de-asserted + VRTC asserted = warm standby (ephemeris
* preserved via backup RAM, ~1-2µA VRTC current)
* - T1000-E, MeshTracker X1 (AG3335): RTC backup sleep ($PAIR650,0), then
* GPS_EN de-asserted with VRTC kept; a GPS_RTC_INT pulse wakes it
* - All boards: gps-enable alias → GPIO power control
*
* CONFIG_PM_DEVICE is on globally, but nothing suspends automatically —
@@ -144,11 +144,50 @@ static const struct gpio_dt_spec gps_resetb_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps
#define HAS_GPS_RESETB 0
#endif
/* Module standby pin (L76K WAKEUP: asserted = run, de-asserted = Standby, RF
* off with the core powered) on a board that ALSO has a supply switch
* (gps-enable): standby uses this pin and keeps the supply, so a wake is a
* hot start; a full off (`gps off`, boot with GPS disabled, System OFF) cuts
* the supply, since Standby still draws. A board whose only control is the
* standby pin (Wio Tracker L1) declares it as gps-enable instead. */
#if HAS_GPS_WAKEUP
static const struct gpio_dt_spec gps_wakeup_gpio = GPIO_DT_SPEC_GET(DT_ALIAS(gps_wakeup), gpios);
#endif
/* T1000-E has extra GPS control pins that require a specific init sequence */
#define HAS_T1000_GPS_CONTROL (HAS_GPS_VRTC || HAS_GPS_RESET || HAS_GPS_SLEEP)
/* AG3335 RTC backup sleep (upstream AirohaSleep.h; T1000-E, MeshTracker X1):
* the module is told to save its state and sleep before GPS_EN is cut, VRTC
* keeps its RTC domain powered, and a GPS_RTC_INT pulse wakes it. */
#define HAS_GPS_BACKUP_SLEEP \
(HAS_GPS_POWER_CONTROL && HAS_GPS_VRTC && HAS_GPS_RTCINT && HAS_GPS_UART)
#if HAS_GPS_POWER_CONTROL
static bool gps_gpio_configured = false;
/* T1000-E sequence only: the module was powered on by us / is in backup sleep. */
static bool gps_module_on = false;
static bool gps_backup_sleeping = false;
#endif
#if HAS_GPS_BACKUP_SLEEP
/* Upstream stop_gps(). The ack ($PAIR001,650,0) cannot be read here, the GNSS
* driver owns the UART RX, so the command goes out blind and upstream's 50 ms
* ack window plus 50 ms grace is waited out. A missed command is the old
* behaviour: a power cut with VRTC kept. */
static void gps_enter_backup_sleep(void)
{
static const char pair_sleep[] = "$PAIR650,0*25\r\n";
gpio_pin_configure_dt(&gps_vrtc_gpio, GPIO_OUTPUT_ACTIVE);
gpio_pin_configure_dt(&gps_rtcint_gpio, GPIO_OUTPUT_INACTIVE);
gps_uart_send((const uint8_t *)pair_sleep, sizeof(pair_sleep) - 1);
k_msleep(100);
gpio_pin_configure_dt(&gps_enable_gpio, GPIO_OUTPUT_INACTIVE);
gps_module_on = false;
gps_backup_sleeping = true;
LOG_INF("GPS power OFF (RTC backup sleep)");
}
#endif
/* The off levels, shared by gps_power_control(false) and System OFF: reset
@@ -164,6 +203,12 @@ static bool gps_gpio_configured = false;
* the power-on path, so the pins may not be outputs yet. */
static void gps_drive_off_levels(bool keep_vrtc, bool system_off)
{
#if HAS_GPS_WAKEUP
/* Low before the supply goes: no pin driven into an unpowered module. */
if (gpio_is_ready_dt(&gps_wakeup_gpio)) {
gpio_pin_configure_dt(&gps_wakeup_gpio, GPIO_OUTPUT_INACTIVE);
}
#endif
#if HAS_GPS_RESET
if (gpio_is_ready_dt(&gps_reset_gpio)) {
gpio_pin_configure_dt(&gps_reset_gpio, GPIO_OUTPUT_ACTIVE);
@@ -231,10 +276,15 @@ void gps_power_control(bool on, bool keep_vrtc)
* (driver PM can hang on modem_pipe_close / modem_chat_run_script).
* The GNSS driver's modem pipe stays open.
*
* T1000-E (HAS_GPS_VRTC): Use warm standby (keep VRTC) for app toggle
* so UART/chip state is preserved. Matches Arduino sleep_gps().
* T1000-E / X1 (HAS_GPS_BACKUP_SLEEP): standby and app toggle use the
* AG3335 RTC backup sleep, as Arduino stop_gps().
* Simple boards (Wio etc.): Full power off/on via GPS_EN. */
if (on) {
#if HAS_GPS_WAKEUP
if (gpio_is_ready_dt(&gps_wakeup_gpio)) {
gpio_pin_configure_dt(&gps_wakeup_gpio, GPIO_OUTPUT_ACTIVE);
}
#endif
#if HAS_T1000_GPS_CONTROL
/* T1000-E power-on sequence (from Arduino target.cpp start_gps())
* Must follow this exact order with delays. Levels are ASSERTED /
@@ -265,7 +315,9 @@ void gps_power_control(bool on, bool keep_vrtc)
#endif
#if HAS_GPS_RESET
if (gpio_is_ready_dt(&gps_reset_gpio)) {
/* Not when waking from backup sleep: upstream resets the module
* once, in begin(), and a reset here would make the wake a restart. */
if (!gps_backup_sleeping && gpio_is_ready_dt(&gps_reset_gpio)) {
gpio_pin_configure_dt(&gps_reset_gpio, GPIO_OUTPUT_ACTIVE);
k_msleep(10);
gpio_pin_set_dt(&gps_reset_gpio, 0); /* Release reset (logical) */
@@ -279,9 +331,12 @@ void gps_power_control(bool on, bool keep_vrtc)
#endif
#if HAS_GPS_RTCINT
/* GPS_RTC_INT (P0.15) — held de-asserted during normal operation */
/* GPS_RTC_INT: a 5 ms pulse wakes the AG3335 from RTC backup sleep
* (upstream start_gps(), on every power-on); otherwise de-asserted. */
if (gpio_is_ready_dt(&gps_rtcint_gpio)) {
gpio_pin_configure_dt(&gps_rtcint_gpio, GPIO_OUTPUT_INACTIVE);
gpio_pin_configure_dt(&gps_rtcint_gpio, GPIO_OUTPUT_ACTIVE);
k_msleep(5);
gpio_pin_set_dt(&gps_rtcint_gpio, 0);
}
#endif
@@ -294,7 +349,10 @@ void gps_power_control(bool on, bool keep_vrtc)
}
#endif
gps_gpio_configured = true;
LOG_INF("GPS power ON (T1000-E sequence)");
LOG_INF("GPS power ON (T1000-E sequence%s)",
gps_backup_sleeping ? ", wake from backup sleep" : "");
gps_module_on = true;
gps_backup_sleeping = false;
#else
/* Simple boards - just GPS_EN */
if (!gps_gpio_configured) {
@@ -315,7 +373,29 @@ void gps_power_control(bool on, bool keep_vrtc)
}
#endif
} else {
#if HAS_GPS_BACKUP_SLEEP
if (keep_vrtc && gps_module_on) {
gps_enter_backup_sleep();
return;
}
if (keep_vrtc && gps_backup_sleeping) {
return; /* already asleep (disabled while in standby) */
}
#endif
#if HAS_GPS_WAKEUP
/* Standby by the module's own pin, supply kept: the wake is a hot
* start and skips the reset pulse (gps_backup_sleeping). */
if (keep_vrtc && gpio_is_ready_dt(&gps_wakeup_gpio)) {
gpio_pin_configure_dt(&gps_wakeup_gpio, GPIO_OUTPUT_INACTIVE);
gps_module_on = false;
gps_backup_sleeping = true;
LOG_INF("GPS standby (WAKEUP pin, supply kept)");
return;
}
#endif
gps_drive_off_levels(keep_vrtc, false);
gps_module_on = false;
gps_backup_sleeping = false;
#if HAS_GPS_VRTC
LOG_INF("GPS power OFF (%s)", keep_vrtc ?