QSPI-bringup stability enhancements

This commit is contained in:
liquidraver
2026-09-27 13:59:33 +02:00
parent 1f1e53d1df
commit 514aa77fe8
25 changed files with 187 additions and 145 deletions
+3 -1
View File
@@ -769,7 +769,9 @@ Full command reference with constraints and remote-admin restrictions: `Repeater
### 7.2 DataStore (`adapters/datastore/`)
- **Internal**: LittleFS on flash (`/lfs`), 256-byte cache for reduced flash I/O
- **External**: Optional LittleFS on QSPI (`/ext`) with auto-migration
- **External**: Optional LittleFS on QSPI (`/ext`) with auto-migration. The flash is `zephyr,deferred-init` and `/ext` is
not automounted (`boards/common/qspi-ext.dtsi`): `zephcore_fs_mount_ext()` brings it up on first use, because a
boot-time probe could beat a cold power rail (first boot after a UF2 update) and silently drop to internal flash
- **BLE bonds**: NVS (`storage_partition`, 0xD0000 on nRF52) via Zephyr settings backend (≥1.16.2)
- **Prefs**: `prefs.json` through upstream's `ConfigSerializer`, with upstream's key names for shared fields and ZephCore's own under `zc` (see §13); the older binary file is read once to migrate and kept
- **Contacts**: 152-byte records, stored on external flash if available
+3
View File
@@ -312,6 +312,9 @@ it polls external state. Repeater and room server share one loop (`server_main_c
### 8.5 Persistence
- LittleFS `/lfs` (internal) and optional `/ext` (QSPI). BLE bonds in NVS, isolated from LittleFS on nRF52/ESP32.
- `/ext` is never probed at boot: its flash is deferred-init and `zephcore_fs_mount_ext()` brings it up on first
use, since a boot-time probe can beat a switched rail that is still cold (first boot after a UF2 update). If it
still fails, the companion falls back to `/lfs` and the next boot that mounts `/ext` keeps the `/ext` copy.
- Prefs are upstream's `prefs.json` (ZephCore fields under `zc`); the legacy binary layouts are migrated once and
kept for downgrades.
- Atomic replace for identity, prefs and channels; contacts on `/ext` only.
@@ -100,6 +100,26 @@ write exactly what is already stored is skipped.
Pending writes are now flushed before every clean reboot and power-off (CLI, UI, bootloader and OTA
paths), not only before the companion app's reboot command.
## Contacts and channels no longer disappear after an update
Boards with external QSPI flash keep contacts, channels and the advert cache on it (`/ext`); identity and
settings stay on the chip's internal flash. On some of these boards the flash is powered through a switched
rail, and the firmware used to check for the flash very early in boot, just after switching that rail on.
After a normal reboot the rail is still charged and the flash answers. After a UF2 update the rail has been
off for the whole time the bootloader ran, the flash was not ready in time, and `/ext` was silently not
used: the node came up with an empty contact list and default channels, while its name, radio settings
and keys were all intact. Reported on a ThinkNode M1 upgrading to 1.17.5.
The flash is now brought up when contacts are first loaded instead of during boot, on every board with
`/ext`: ThinkNode M1, T-Echo, T-Impulse Plus, MeshTracker X1 (which already had this), SenseCAP Solar,
Wio Tracker L1, Wio Tracker L1 Pro 1W and XIAO nRF52840.
> [!NOTE]
> **If this happened to you, your contacts were not erased.** They are still on the external flash, and
> the next boot that finds it brings them back (a plain reboot is usually enough). Contacts and channels
> added while the list was empty were stored on internal flash instead; they are dropped when the old
> list returns, so re-add those few by hand.
## Remote CLI now executes on the device (protocol v14)
The companion protocol's v14 additions — `CMD_RUN_CLI_COMMAND`/`PACKET_CLI_REPLY`, and
@@ -51,52 +51,13 @@ bool ZephyrDataStore::mount()
return false;
}
/* Check if external QSPI was automounted */
if (zephcore_fs_is_mounted(extMountPoint())) {
ext_lfs_mounted = true;
LOG_INF("External QSPI LittleFS at %s (automounted, 100 blobs)", extMountPoint());
} else {
#if DT_NODE_EXISTS(DT_NODELABEL(qspi_lfs))
/* Boot-time automount can miss the QSPI on the first boot after a
* factory-erase (blank flash) or an early-boot timing race with QSPI
* init. Retry the mount explicitly (fs_mount auto-formats blank flash,
* and mounts valid data without touching it) so contacts/channels land
* on /ext. Without this the store silently falls back to internal /lfs,
* and the next boot that does mount /ext runs a needless contact
* migration — the "Migrating contacts to external storage" churn. */
#if DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_qspi_nor), zephyr_deferred_init)
/* The flash is deferred-init: probing it at boot raced its own
* power rail and the JEDEC read came back 00 00 00, so the
* driver failed and /ext could never mount. Initialising it here
* instead means the part has had until first use to wake up —
* over a second — rather than us guessing a settling delay. */
{
const struct device *qspi_dev =
DEVICE_DT_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_qspi_nor));
if (!device_is_ready(qspi_dev)) {
int drc = device_init(qspi_dev);
LOG_INF("QSPI flash deferred init: rc=%d ready=%d",
drc, (int)device_is_ready(qspi_dev));
}
}
#endif
FS_FSTAB_DECLARE_ENTRY(DT_NODELABEL(qspi_lfs));
int rc = fs_mount(&FS_FSTAB_ENTRY(DT_NODELABEL(qspi_lfs)));
if (zephcore_fs_is_mounted(extMountPoint())) {
ext_lfs_mounted = true;
LOG_INF("External QSPI LittleFS at %s (mounted on retry, rc=%d)",
extMountPoint(), rc);
} else {
ext_lfs_mounted = false;
LOG_WRN("External QSPI mount retry failed (rc=%d) - using internal only (20 blobs)", rc);
}
#else
ext_lfs_mounted = false;
LOG_INF("External QSPI NOT mounted at %s - using internal only (20 blobs)", extMountPoint());
#endif
/* External QSPI: deferred flash init + explicit mount, never at boot.
* If it fails, contacts/channels fall back to internal /lfs, and the
* next boot that does mount /ext keeps the /ext copy. */
ext_lfs_mounted = zephcore_fs_mount_ext();
if (!ext_lfs_mounted) {
LOG_INF("External QSPI not mounted at %s - using internal only (20 blobs)",
extMountPoint());
}
return true;
+51 -7
View File
@@ -7,6 +7,7 @@
#include "ZephyrFsUtil.h"
#include <zephyr/devicetree.h>
#include <zephyr/device.h>
#include <zephyr/fs/fs.h>
#include <zephyr/storage/flash_map.h>
#include <zephyr/logging/log.h>
@@ -36,6 +37,53 @@ static void flatten(uint8_t id, const char *tag)
flash_area_close(fap);
}
#if FIXED_PARTITION_EXISTS(qspi_storage_partition)
/* qspi-ext.dtsi makes the flash under /ext zephyr,deferred-init, so nothing
* probes it at boot. The flash sits behind a GPIO-switched rail on several
* boards, and the boot-time JEDEC read went out before a cold rail had come
* up: the driver failed, /ext stayed unmounted and contacts silently fell
* back to internal flash. A warm reboot keeps the rail charged; the first
* boot after a UF2 update (seconds in the bootloader, rail off) did not.
* Bringing the part up here, on first use, is long after the rail. Each role
* reaches this on its own path, so it is idempotent: -EALREADY means an
* earlier call already ran the init, whatever the result was. */
static void ext_flash_init(void)
{
const struct device *dev = FIXED_PARTITION_DEVICE(qspi_storage_partition);
if (!device_is_ready(dev)) {
int rc = device_init(dev);
LOG_INF("%s flash init: rc=%d ready=%d", EXT_MNT_POINT, rc,
(int)device_is_ready(dev));
}
}
#endif
bool zephcore_fs_mount_ext(void)
{
#if DT_NODE_EXISTS(DT_NODELABEL(qspi_lfs))
if (zephcore_fs_is_mounted(EXT_MNT_POINT)) {
return true;
}
ext_flash_init();
/* fs_mount() auto-formats blank flash and mounts valid data untouched. */
FS_FSTAB_DECLARE_ENTRY(DT_NODELABEL(qspi_lfs));
int rc = fs_mount(&FS_FSTAB_ENTRY(DT_NODELABEL(qspi_lfs)));
bool mounted = zephcore_fs_is_mounted(EXT_MNT_POINT);
if (mounted) {
LOG_INF("%s mounted (rc=%d)", EXT_MNT_POINT, rc);
} else {
LOG_ERR("%s mount failed (rc=%d)", EXT_MNT_POINT, rc);
}
return mounted;
#else
return false;
#endif
}
bool zephcore_fs_format_all(bool *out_ext_mounted)
{
LOG_INF("zephcore_fs_format_all: starting...");
@@ -71,6 +119,8 @@ bool zephcore_fs_format_all(bool *out_ext_mounted)
#endif
#if FIXED_PARTITION_EXISTS(qspi_storage_partition)
/* A repeater never mounts /ext, so its flash is still uninitialised. */
ext_flash_init();
flatten(PARTITION_ID(qspi_storage_partition), "qspi_storage_partition");
#endif
@@ -93,14 +143,8 @@ bool zephcore_fs_format_all(bool *out_ext_mounted)
* contacts/channels save to /lfs and get needlessly migrated back to
* /ext on the next boot ("Migrating contacts to external storage"). */
{
int ext_rc = fs_mount(&FS_FSTAB_ENTRY(DT_NODELABEL(qspi_lfs)));
bool ext_mounted = zephcore_fs_is_mounted(EXT_MNT_POINT);
bool ext_mounted = zephcore_fs_mount_ext();
if (ext_mounted) {
LOG_INF("format: %s remounted (rc=%d)", EXT_MNT_POINT, ext_rc);
} else {
LOG_ERR("format: %s remount failed (rc=%d)", EXT_MNT_POINT, ext_rc);
}
if (out_ext_mounted) {
*out_ext_mounted = ext_mounted;
}
@@ -34,6 +34,16 @@ extern "C" {
*/
bool zephcore_fs_format_all(bool *out_ext_mounted);
/**
* @brief Mount /ext (QSPI) on first use, initialising its deferred flash.
*
* Idempotent. The flash is zephyr,deferred-init (qspi-ext.dtsi) and /ext is
* not automounted, so this is the only way /ext comes up.
*
* @return true if /ext is mounted; always false on boards with no QSPI.
*/
bool zephcore_fs_mount_ext(void);
#ifdef __cplusplus
}
#endif
+23 -3
View File
@@ -1,6 +1,6 @@
/*
* SPDX-License-Identifier: MIT
* QSPI external flash /ext automount — optional add-on for any platform
* QSPI external flash /ext — optional add-on for any platform
*
* See nrf52_partitions_sdv7.dtsi for full include guide.
*
@@ -8,7 +8,8 @@
* and blobs to QSPI flash (more space, faster writes on native 4KB blocks).
* Prefs and identity stay on internal flash.
*
* Requires board DTS to define: qspi_storage_partition
* Requires board DTS to define: qspi_flash (the flash device node) and
* qspi_storage_partition
*
* Usage in board overlay (in addition to partition or filesystem include):
* #include "../../common/qspi-ext.dtsi"
@@ -16,6 +17,26 @@
* Works with all platforms using /lfs mount.
*/
/*
* The flash is not probed at boot. On boards where it sits behind a switched
* rail, the boot-time JEDEC read could beat a cold rail (first boot after a
* UF2 update, or a power-up): the driver failed, /ext stayed unmounted and
* contacts/channels silently fell back to internal flash. A warm reboot keeps
* the rail charged, which is why only updates showed it. The store initialises
* the flash and mounts /ext on first use (zephcore_fs_mount_ext()), so /ext is
* not automounted either.
*
* t-exit-dpd (nanoseconds, so 50 us) is how long the driver waits after
* Release-from-Deep-Power-Down, which it sends once at init to wake a part a
* previous firmware left asleep. Boards used to carry 30, 35 or 50 us each; 50
* covers all of them, and the extra few us run once per flash init. It
* assumes a powered part, so it does nothing for the cold-rail race above.
*/
&qspi_flash {
zephyr,deferred-init;
t-exit-dpd = <50000>;
};
/ {
fstab {
/* External QSPI for contacts, channels, blobs */
@@ -23,7 +44,6 @@
compatible = "zephyr,fstab,littlefs";
mount-point = "/ext";
partition = <&qspi_storage_partition>;
automount;
read-size = <16>;
prog-size = <16>;
cache-size = <64>;
@@ -88,11 +88,11 @@
enable-gpios = <&gpio0 3 (GPIO_ACTIVE_HIGH | ESP32_GPIO_SLEEP_HOLD_EN)>;
regulator-boot-on;
regulator-always-on;
/* Documents the rail's requirement, but do not rely on it. It is
* inert twice over: regulator-boot-on sends regulator_common_init()
* down the refcount-only branch so regulator_delay() never runs, and
* regulator-always-on makes regulator_enable() return at its first
* line. The real settle time comes from the ST7735R driver's own
/* The rail's requirement. It runs once at boot (Zephyr
* 3b4821f1ba6, pinned from 1.17.5; it was skipped before), and
* never again: regulator-always-on makes regulator_enable() return
* at its first line. Do not rely on it alone: the real settle time
* comes from the ST7735R driver's own
* mipi_dbi_reset() + exit-sleep delay at DISPLAY_INIT_PRIORITY 85;
* the GNSS side is safe because gnss-nmea-generic never probes the
* receiver, it just parses whatever the UART delivers. */
@@ -123,9 +123,9 @@
enable-gpios = <&gpio0 7 (GPIO_ACTIVE_HIGH | ESP32_GPIO_SLEEP_HOLD_EN)>; /* held across light sleep */
regulator-boot-on;
regulator-always-on;
/* Documents the FEM's requirement, but do not rely on it — inert for
* the same two reasons as vext above. Harmless because the FEM is a
* passive gain block with no bus to probe. */
/* The FEM's requirement; runs once at boot, as vext above. Nothing
* depends on it: the FEM is a passive gain block with no bus to
* probe. */
startup-delay-us = <1000>;
};
+1 -1
View File
@@ -571,7 +571,7 @@
* size = <16777216>;
* has-dpd;
* t-enter-dpd = <3000>;
* t-exit-dpd = <30000>;
* (t-exit-dpd comes from qspi-ext.dtsi: 50 us)
* quad-enable-requirements = "S2B1v1";
* };
* };
@@ -104,10 +104,9 @@
regulator-name = "gps-power";
enable-gpios = <&gpio0 6 GPIO_ACTIVE_LOW>;
regulator-boot-on;
/* Documents the module's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. Harmless here because gnss-nmea-generic never
/* The module's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but nothing here
* depends on it: gnss-nmea-generic never
* probes the receiver — it attaches a UART and parses whatever
* arrives, so an unpowered module means no sentences yet, not a
* failed init. ZephyrGPSManager owns the rail at runtime via
@@ -130,11 +129,10 @@
regulator-name = "vfem-enable";
enable-gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the FEM's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. Harmless here because the FEM is a passive
* gain block with no bus to probe — nothing addresses it at init. */
/* The FEM's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but nothing depends
* on it: the FEM is a passive gain block with no bus to probe —
* nothing addresses it at init. */
startup-delay-us = <1000>;
};
@@ -154,10 +152,9 @@
regulator-name = "tft-pwr-enable";
enable-gpios = <&gpio0 26 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the panel's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from the ST7735R
/* The panel's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from the ST7735R
* driver's own mipi_dbi_reset() + exit-sleep delay, at
* DISPLAY_INIT_PRIORITY 85 against this rail's 75. */
startup-delay-us = <10000>;
@@ -168,10 +168,9 @@
regulator-name = "gps-power";
enable-gpios = <&gpio0 4 GPIO_ACTIVE_LOW>;
regulator-boot-on;
/* Documents the module's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. Harmless here because gnss-nmea-generic never
/* The module's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but nothing here
* depends on it: gnss-nmea-generic never
* probes the receiver — it attaches a UART and parses whatever
* arrives, so an unpowered module means no sentences yet, not a
* failed init. ZephyrGPSManager owns the rail at runtime via
@@ -234,10 +233,9 @@
regulator-name = "tft-pwr-enable";
enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
regulator-boot-on;
/* Documents the panel's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from the ST7735R
/* The panel's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from the ST7735R
* driver's own mipi_dbi_reset() + exit-sleep delay, at
* DISPLAY_INIT_PRIORITY 85 against this rail's 75. */
startup-delay-us = <10000>;
@@ -147,10 +147,10 @@
* regulator framework brings them up before the SX1262 driver init —
* REGULATOR_FIXED_INIT_PRIORITY 75 against LORA_INIT_PRIORITY 90.
*
* Ordering is all that guarantee covers. It does NOT mean a rail has
* settled: regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and the
* startup-delay-us below is inert. The SX1262 is safe because its driver
* Ordering is all that guarantee covers. The startup-delay-us below does
* run at boot (Zephyr 3b4821f1ba6, pinned from 1.17.5; it was skipped
* before), but it is a settle floor, not a readiness check. The SX1262 is
* safe because its driver
* resets the part (5 ms NRESET pulse + 5 ms wait + BUSY poll) before any
* SPI traffic. Anything put behind these rails that does NOT
* reset-then-wait needs zephyr,deferred-init instead; see
@@ -385,7 +385,6 @@
size = <16777216>; /* 2MB = 16Mbit */
has-dpd;
t-enter-dpd = <10000>;
t-exit-dpd = <35000>;
};
};
@@ -125,10 +125,9 @@
regulator-name = "pwr-enable";
enable-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the LDO's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. What actually gives this rail time to settle is the
/* The LDO's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone. What also gives this rail time to settle is the
* priority-30 pin above — every consumer here (QSPI 41, OLED 85,
* SX1262 90) has real boot work between it and the rail, and the
* SX1262 and SSD1315 both reset-then-wait besides. The GPS, which
@@ -357,7 +356,6 @@
size = <33554432>; /* size is in BITS: 32Mbit = 4MB */
has-dpd;
t-enter-dpd = <10000>; /* tDP 3us (conservative) */
t-exit-dpd = <35000>; /* tRES1 8us (conservative) */
};
};
@@ -21,8 +21,3 @@ CONFIG_ZEPHCORE_RADIO_LR2021=y
# DRV2605L vibration motor
CONFIG_HAPTICS=y
CONFIG_HAPTICS_DRV2605=y
# The flash sits behind the P0.15 power switch, modelled as a regulator so its
# power-up time is a startup-delay rather than a race. Regulators come up at 75;
# probe the flash after that.
CONFIG_NORDIC_QSPI_NOR_INIT_PRIORITY=80
@@ -113,12 +113,11 @@
regulator-name = "flash-power";
enable-gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the part's requirement, but do not rely on it:
* regulator_fixed_init() drives the enable GPIO active and then
* passes is_enabled=true into regulator_common_init(), which
* takes the refcount-only branch — regulator_delay() sits on the
* other one and never runs. The flash gets its settling time
* from deferred init instead (see the qspi_flash node). */
/* Runs at boot: since Zephyr 3b4821f1ba6 (pinned from 1.17.5)
* regulator_fixed_init() sleeps this long after driving a
* regulator-boot-on rail active. Before that it was skipped. The
* flash's real margin is still its deferred init (qspi-ext.dtsi),
* which runs when the store first mounts /ext. */
startup-delay-us = <100000>;
};
@@ -290,9 +289,10 @@
*
* sensor_power comes up at CONFIG_REGULATOR_FIXED_INIT_PRIORITY (75) and
* both drivers run at 90 — microseconds later, with nothing scheduled in
* between on this board. The node's startup-delay-us does not save them:
* with regulator-boot-on, regulator_common_init() takes the refcount-only
* branch and regulator_delay() never runs. Same trap as flash_power below.
* between on this board. The node's startup-delay-us did not save them:
* until Zephyr 3b4821f1ba6 (pinned from 1.17.5) it was skipped for a
* regulator-boot-on rail. It now runs (5 ms), but the parts were never
* validated against it, so the deferral below stays.
* (vin-supply + regulator_enable() would not help either — that applies
* off-on-delay-us, and only on the 0->1 refcount transition.)
*
@@ -365,14 +365,11 @@
qspi_flash: qspi_flash@0 {
compatible = "nordic,qspi-nor";
reg = <0>;
/* Do not probe this at boot. The rail is switched on by
* flash-power a few init priorities earlier, and the part is not
* driving the bus yet when the driver's JEDEC read goes out —
* it answers 00 00 00 at 0.4 s and a correct c8 40 17 by 5.9 s
* on the very same boot. A fixed settling delay would only be a
* guess at where in between it wakes; instead the driver is
* initialised on demand, when the store first mounts /ext. */
zephyr,deferred-init;
/* Not probed at boot: qspi-ext.dtsi makes it zephyr,deferred-init.
* This board is where that came from — probed a few init
* priorities after flash-power switched the rail on, the part
* answered 00 00 00 at 0.4 s and a correct c8 40 17 by 5.9 s on
* the very same boot. */
/* Single-line, as every other QSPI board here uses.
*
* Quad needs the part's QE bit set, and the binding defaults
@@ -395,7 +392,6 @@
size = <67108864>; /* 64Mbit = 8MB, in bits */
has-dpd;
t-enter-dpd = <3000>;
t-exit-dpd = <30000>;
};
};
@@ -62,10 +62,9 @@
regulator-name = "vcc3v3-enable";
enable-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the module's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from lr20xx_hal_reset(),
/* The module's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from lr20xx_hal_reset(),
* which the driver runs (with two retries, 10 ms apart) before any
* SPI traffic — and at LORA_INIT_PRIORITY 90 against this rail's 75.
* Anything put behind this rail that does NOT reset-then-wait needs
@@ -68,10 +68,9 @@
regulator-name = "vcc3v3-enable";
enable-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the module's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from the SX126x driver's
/* The module's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from the SX126x driver's
* own reset (5 ms NRESET pulse + 5 ms wait + BUSY poll) before any
* SPI traffic, at LORA_INIT_PRIORITY 90 against this rail's 75.
* Anything put behind this rail that does NOT reset-then-wait needs
@@ -93,10 +93,9 @@
regulator-name = "lora-pwr-enable";
enable-gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the FEM's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from the SX126x driver's
/* The FEM's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from the SX126x driver's
* own reset (5 ms NRESET pulse + 5 ms wait + BUSY poll) before any
* SPI traffic, at LORA_INIT_PRIORITY 90 against this rail's 75.
* Anything put behind this rail that does NOT reset-then-wait needs
@@ -120,10 +120,9 @@
regulator-name = "lora-pwr-enable";
enable-gpios = <&gpio1 5 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the module's requirement, but do not rely on it:
* regulator-boot-on sends regulator_common_init() down the
* refcount-only branch, so regulator_delay() never runs and this
* value is inert. The real settle time comes from the SX126x driver's
/* The module's requirement. It runs at boot (Zephyr 3b4821f1ba6,
* pinned from 1.17.5; it was skipped before), but do not rely on
* it alone: the real settle time comes from the SX126x driver's
* own reset (5 ms NRESET pulse + 5 ms wait + BUSY poll) before any
* SPI traffic, at LORA_INIT_PRIORITY 90 against this rail's 75.
* Anything put behind this rail that does NOT reset-then-wait needs
@@ -218,7 +218,6 @@
size = <16777216>;
has-dpd;
t-enter-dpd = <3000>;
t-exit-dpd = <50000>;
};
};
@@ -359,7 +359,6 @@
size = <16777216>; /* 2MB = 16Mbit */
has-dpd;
t-enter-dpd = <10000>;
t-exit-dpd = <35000>;
};
};
@@ -331,7 +331,6 @@ sh1106: &oled {
size = <16777216>; /* 2MB = 16Mbit */
has-dpd;
t-enter-dpd = <3000>;
t-exit-dpd = <50000>; /* 50ms for reliable wakeup */
/* quad-enable not needed for single-bit mode */
partitions {
@@ -152,10 +152,9 @@
regulator-name = "lora-pwr-enable";
enable-gpios = <&gpio0 14 GPIO_ACTIVE_HIGH>;
regulator-boot-on;
/* Documents the LDO + PA settle requirement but does not enforce
* it: regulator-boot-on takes regulator_common_init()'s
* refcount-only branch, so regulator_delay() never runs. The real
* settle time comes from the SX126x driver's own reset (5 ms
/* The LDO + PA settle requirement. It runs at boot (Zephyr
* 3b4821f1ba6, pinned from 1.17.5; it was skipped before), but do
* not rely on it alone: the real settle time comes from the SX126x driver's own reset (5 ms
* NRESET pulse + 5 ms wait + BUSY poll) 15 init priorities later.
* See the identical note on rak3401_1watt. */
startup-delay-us = <10000>;
@@ -392,7 +391,6 @@
size = <16777216>; /* 2MB = 16Mbit */
has-dpd;
t-enter-dpd = <3000>;
t-exit-dpd = <50000>; /* 50ms for reliable wakeup */
/* quad-enable not needed for single-bit mode */
};
};
@@ -230,7 +230,6 @@
size = <16777216>;
has-dpd;
t-enter-dpd = <3000>;
t-exit-dpd = <50000>;
};
};
+9
View File
@@ -67,6 +67,15 @@ static int qspi_rdid_probe(void)
return 0;
}
if (!flash->state->initialized) {
/* qspi-ext.dtsi defers the flash to first use, and a repeater
* never mounts /ext. Stealing the pins now would break the init
* a later factory reset runs, so there is nothing to probe. */
LOG_INF("qspi flash '%s' not initialised yet - RDID probe skipped",
flash->name);
return 0;
}
if (device_is_ready(flash)) {
/* Bit-banging steals the QSPI pins as GPIOs and leaves them
* that way, which would break a flash that just came up fine.