retry PM activation

This commit is contained in:
liquidraver committed 2026-07-18 20:30:43 +02:00
1 parent 16c7ae506a
commit 1b9410df21
3 files changed
+133 -19

No files matched your search

+108 -8
View File
@@ -11,6 +11,8 @@
* - 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)
* - 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
*/
@@ -24,6 +26,7 @@
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/pm/device.h>
#include <string.h>
#if defined(CONFIG_SOC_NRF52840)
#include <nrfx.h>
@@ -44,17 +47,25 @@ LOG_MODULE_REGISTER(zephcore_gps, CONFIG_ZEPHCORE_GPS_LOG_LEVEL);
#define HAS_GNSS 0
#endif
/* ========== GPS Module Capability Flags ==========
* GPS power management uses GPIO only (no PM_DEVICE):
/* ========== GPS Power Strategy ==========
* Module power is GPIO/regulator controlled — GNSS driver PM is not used
* for the module itself:
* - Wio Tracker L1 (L76K): FORCE_ON pin LOW = hardware standby (~360µA,
* Vcc stays on, ephemeris/almanac/RTC preserved, hot-start 1-2s)
* - T1000-E (AG3335): GPS_EN LOW + VRTC HIGH = warm standby (ephemeris
* preserved via backup RAM, ~1-2µA VRTC current)
* - All boards: gps-enable alias → GPIO power control
*
* PM_DEVICE is intentionally NOT used — the system-managed PM subsystem
* auto-suspends devices during idle, calling modem_chat_run_script() from
* an unexpected context which can deadlock the system. */
* CONFIG_PM_DEVICE is on globally, but nothing suspends automatically —
* system-managed suspend is compiled only under CONFIG_PM (off everywhere).
* This manager makes exactly two kinds of PM calls, both main-thread only:
* - a one-time RESUME of the GNSS device at boot (gnss-nmea-generic inits
* suspended under CONFIG_PM_DEVICE and never opens its pipe otherwise);
* - suspend/resume of the GNSS UARTE around standby/off (an armed UARTE RX
* holds HFCLK ≈0.5-1 mA on nRF52840 even with the module powered off).
* The old "PM broke GPS" deadlock was modem_chat_run_script() being reached
* from the system workqueue via driver PM hooks — the air530z driver is
* PM-less now and every PM call here stays on the main thread. */
/* ========== GPS State - Power Management ========== */
#if HAS_GNSS
@@ -863,13 +874,60 @@ void gps_power_off_for_shutdown(void)
#if DT_NODE_HAS_STATUS(DT_NODELABEL(gnss), okay) && \
DT_NODE_HAS_STATUS(DT_BUS(DT_NODELABEL(gnss)), okay)
#define HAS_GPS_UART 1
#if !HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR
static const struct device *gps_uart_dev = DEVICE_DT_GET(DT_BUS(DT_NODELABEL(gnss)));
#endif
#else
#define HAS_GPS_UART 0
#endif
/* ========== GNSS UART Suspend/Resume (device PM) ==========
* nRF UARTE only. An armed UARTE RX holds HFCLK (~0.5-1 mA on nRF52840)
* even when the GPS module is powered off or silent, so standby/off
* suspends the UART device and every wake resumes it first.
*
* Verified symmetric in uart_nrfx_uarte.c under the still-open modem pipe:
* suspend saves the RX-interrupt state, STOPRXes, disables the peripheral
* and applies the sleep pinctrl; resume restores all of it. Other UART
* drivers (legacy nordic,nrf-uart on RAK4631, ESP32) are deliberately not
* gated in — their suspend/resume round-trip is unverified and the HFCLK
* cost is UARTE-specific.
*
* Every GPS UART node must carry a sleep pinctrl state (all boards do):
* without one, suspend fails *after* disabling RX while the PM state stays
* ACTIVE, so the next resume no-ops with -EALREADY — a dead GPS. */
#if HAS_GPS_UART && defined(CONFIG_PM_DEVICE) && \
DT_NODE_HAS_COMPAT(DT_BUS(DT_NODELABEL(gnss)), nordic_nrf_uarte)
#define HAS_GPS_UART_PM 1
#else
#define HAS_GPS_UART_PM 0
#endif
#if HAS_GPS_UART && \
(HAS_GPS_UART_PM || (!HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR))
static const struct device *gps_uart_dev = DEVICE_DT_GET(DT_BUS(DT_NODELABEL(gnss)));
#endif
/* Suspend/resume the GNSS UART. Main thread only (like all GPS power
* paths — pm_device_action_run() calls the driver synchronously).
* Ordering: resume BEFORE powering the module / sending the wake byte;
* suspend AFTER the module is off / sleep commands were sent. */
#if HAS_GPS_UART_PM
static void gps_uart_set_power(bool on)
{
if (!device_is_ready(gps_uart_dev)) {
return;
}
int ret = pm_device_action_run(gps_uart_dev,
on ? PM_DEVICE_ACTION_RESUME
: PM_DEVICE_ACTION_SUSPEND);
if (ret == 0) {
LOG_INF("GPS UART %s", on ? "resumed" : "suspended");
} else if (ret != -EALREADY) {
LOG_WRN("GPS UART %s failed: %d", on ? "resume" : "suspend", ret);
}
}
#else
static inline void gps_uart_set_power(bool on) { ARG_UNUSED(on); }
#endif
#if HAS_GPS_UART && !HAS_GPS_POWER_CONTROL && !HAS_GPS_POWER_REGULATOR
/* Send raw bytes to the GPS UART using blocking poll_out.
* Safe to call even though modem_chat/modem_ubx owns the UART pipe:
@@ -999,6 +1057,10 @@ static void gps_go_to_standby(void)
gps_software_sleep();
#endif
/* Module is off/asleep — release the UART until the next wake
* (nRF: drops the HFCLK request held by the armed RX). */
gps_uart_set_power(false);
/* NOTE: gnss_configured stays true — L76K retains PCAS settings in
* flash across power cycles. Re-running gnss_configure() after GPIO
* wake would call modem_chat_run_script() before the chip has booted,
@@ -1023,6 +1085,10 @@ static void gps_start_acquiring(void)
consecutive_good_fixes = 0;
gnss_activity_seen_this_cycle = false;
/* Bring the UART back before the module powers up / the wake byte
* goes out, so the first NMEA sentences aren't lost. */
gps_uart_set_power(true);
#if HAS_GPS_POWER_CONTROL || HAS_GPS_POWER_REGULATOR
gps_power_control(true);
#elif HAS_GPS_UART
@@ -1283,6 +1349,27 @@ static int gnss_init(void)
}
}
#ifdef CONFIG_PM_DEVICE
/* Some upstream GNSS drivers (gnss-nmea-generic) start suspended under
* CONFIG_PM_DEVICE and never open their modem pipe until resumed — no
* NMEA would ever flow (the old "PM broke GPS" trap). Resume once
* here: main thread at boot, the one safe context for the modem_chat
* scripts a resume may run. PM-less drivers (luatos,air530z) return
* -ENOSYS. Retried like device_init above — opening the pipe while
* the module is mid-sentence can fail transiently. */
int pm_ret = pm_device_action_run(gnss_dev, PM_DEVICE_ACTION_RESUME);
for (int attempt = 1; pm_ret != 0 && pm_ret != -EALREADY &&
pm_ret != -ENOSYS && attempt < 3; attempt++) {
LOG_WRN("GNSS PM resume failed (%d), retrying", pm_ret);
k_msleep(100);
pm_ret = pm_device_action_run(gnss_dev, PM_DEVICE_ACTION_RESUME);
}
if (pm_ret != 0 && pm_ret != -EALREADY && pm_ret != -ENOSYS) {
LOG_ERR("GNSS PM resume failed: %d", pm_ret);
return -ENODEV;
}
#endif
LOG_INF("GNSS device %s initialized", gnss_dev->name);
gps_available = true;
return 0;
@@ -1337,6 +1424,10 @@ void gps_ensure_power_state(bool should_be_enabled)
if (!should_be_enabled) {
LOG_INF("GPS: Powering off at boot (disabled in prefs)");
gps_power_control(false);
/* GPS stays off — release the UART too. Without this, the RX
* armed at driver init would hold HFCLK for the entire uptime
* of every GPS-disabled node. */
gps_uart_set_power(false);
gps_current_state = GPS_STATE_OFF;
}
#else
@@ -1391,6 +1482,11 @@ void gps_enable(bool enable)
gps_current_state = GPS_STATE_ACQUIRING;
consecutive_good_fixes = 0;
/* Resume the GNSS UART first so no NMEA is lost at power-on
* (it may be suspended from a boot-with-GPS-off or a prior
* disable). */
gps_uart_set_power(true);
/* Power on GPS - uses lazy GPIO init */
gps_power_control(true);
@@ -1424,6 +1520,10 @@ void gps_enable(bool enable)
#else
gps_power_control(false); /* No VRTC — full power off */
#endif
/* GPS is off until re-enabled — release the UART. */
gps_uart_set_power(false);
gps_current_state = GPS_STATE_OFF;
consecutive_good_fixes = 0;
@@ -97,11 +97,11 @@ index 74708edbd62..7de70ad93c8 100644
-
- return ret;
-}
+/* PM intentionally removed — the L76K ignores $PMTK161,0 (AT6558-based,
+ * not genuine MediaTek). GPS standby uses GPIO FORCE_ON pin instead.
+ * Also, CONFIG_PM_DEVICE_SYSTEM_MANAGED can auto-suspend devices during
+ * idle, calling modem_chat_run_script() from an unexpected context and
+ * potentially deadlocking the system. */
+/* PM hooks intentionally removed — the L76K ignores $PMTK161,0 (AT6558-
+ * based, not genuine MediaTek), so PM-driven software sleep can never
+ * work; standby drives the FORCE_ON GPIO and the app suspends the UART
+ * instead (ZephyrGPSManager, main thread only). PM-less also keeps
+ * modem_chat scripts out of PM callbacks (deadlock risk off sysworkq). */
static int luatos_air530z_set_fix_rate(const struct device *dev, uint32_t fix_interval_ms)
{
+20 -6
View File
@@ -28,9 +28,23 @@ CONFIG_THREAD_NAME=n
CONFIG_ZEPHCORE_RESET_ON_FATAL_ERROR=y
# ========== Power Management ==========
# Note: nRF52840 automatically uses WFI (Wait For Interrupt) in idle thread.
# Power savings come from event-driven design: CPU sleeps in WFI whenever
# k_event_wait() blocks. GPS standby uses GPIO power control (not PM_DEVICE).
# CONFIG_PM_DEVICE intentionally DISABLED — it enables SYSTEM_MANAGED PM which
# auto-suspends all devices (including GNSS) during idle, calling modem_chat
# from an unexpected context and potentially deadlocking the system.
# Device PM only — CONFIG_PM (system PM) stays off. nRF52/nRF54L have no
# devicetree power-states in Zephyr 4.4 (WFI in the idle thread is already
# the low-power System ON state); ESP32/STM32WL do have states but need
# their own bring-up (BLE/WiFi/radio wake paths) before CONFIG_PM can go on.
#
# CONFIG_PM_DEVICE alone suspends nothing automatically: PM actions run only
# when we call pm_device_action_run() (always from the main thread). The
# system-managed auto-suspend (PM_DEVICE_SYSTEM_MANAGED) is compiled only
# under CONFIG_PM; it is pinned off here so a platform that later enables
# CONFIG_PM can't silently start suspending devices from the idle thread.
#
# Why device PM is on:
# - gnss-nmea-generic inits pm_device_init_suspended() under CONFIG_PM_DEVICE
# and never opens its modem pipe until resumed; ZephyrGPSManager resumes it
# once at boot (this was the old "PM broke GPS" trap).
# - GPS standby suspends the GNSS UARTE — an armed UARTE RX holds HFCLK
# (~0.5-1 mA continuous on nRF52840) even with the GPS module powered off.
# See adapters/gps/ZephyrGPSManager.cpp for the full strategy.
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_SYSTEM_MANAGED=n