From f6d637c48416f9890b0fbb41c0637297721bc10b Mon Sep 17 00:00:00 2001 From: liquidraver <504870+liquidraver@users.noreply.github.com> Date: Sat, 19 Sep 2026 19:50:18 +0200 Subject: [PATCH] sysworkq 4K platform-wide, c3 heap 44K, native-linux mapped-partition, sx126x 0003 step B reduction --- zephcore/CMakeLists.txt | 10 +- zephcore/boards/common/esp32c3s3_common.conf | 29 ++ zephcore/boards/common/esp32s3_common.conf | 28 -- zephcore/boards/common/nrf52_common.conf | 8 - zephcore/boards/common/stm32wl_common.conf | 6 +- zephcore/boards/common/zephcore_common.conf | 12 +- zephcore/boards/example_board/board.overlay | 6 + .../boards/linux_native/linux_common.overlay | 1 + .../zephyr/0003-lora-sx126x-native.patch | 352 +++++++----------- 9 files changed, 185 insertions(+), 267 deletions(-) create mode 100644 zephcore/boards/common/esp32c3s3_common.conf delete mode 100644 zephcore/boards/common/esp32s3_common.conf diff --git a/zephcore/CMakeLists.txt b/zephcore/CMakeLists.txt index 1a6e7a6..5a6dd7a 100644 --- a/zephcore/CMakeLists.txt +++ b/zephcore/CMakeLists.txt @@ -417,10 +417,12 @@ if(ZEPHCORE_PLATFORM_CONF AND EXISTS ${ZEPHCORE_PLATFORM_CONF}) list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_PLATFORM_CONF}) zephcore_auto_pair_overlay("${ZEPHCORE_PLATFORM_CONF}") endif() -# ESP32-S3 only: overrides esp32_common.conf, stays below board.conf. Every S3 -# board target carries the /esp32s3/ qualifier (see esp32s3_common.conf). -if(BOARD MATCHES "/esp32s3/") - list(APPEND ZEPHCORE_CONF_FILES "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32s3_common.conf") +# ESP32-S3 and ESP32-C3 (shared BLE controller): overrides esp32_common.conf, +# stays below board.conf. Every S3 board target carries the /esp32s3/ +# qualifier; the C3 boards are single-SoC and are named without one +# (xiao_esp32c3), so match the chip in the name (see esp32c3s3_common.conf). +if(BOARD MATCHES "/esp32s3/" OR BOARD MATCHES "esp32c3") + list(APPEND ZEPHCORE_CONF_FILES "${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32c3s3_common.conf") endif() if(ZEPHCORE_BOARD_CONF AND EXISTS ${ZEPHCORE_BOARD_CONF}) list(APPEND ZEPHCORE_CONF_FILES ${ZEPHCORE_BOARD_CONF}) diff --git a/zephcore/boards/common/esp32c3s3_common.conf b/zephcore/boards/common/esp32c3s3_common.conf new file mode 100644 index 0000000..5f93e9b --- /dev/null +++ b/zephcore/boards/common/esp32c3s3_common.conf @@ -0,0 +1,29 @@ +# ESP32-S3 and ESP32-C3 only. Loaded after esp32_common.conf and before +# board.conf (CMakeLists.txt, matched on the /esp32s3/ qualifier or an esp32c3 +# board name). The two chips share one BLE controller: the S3 build compiles +# hal_espressif's components/bt/controller/esp32c3 sources and links the same +# btdm controller library family, so one heap figure covers both. The C6 runs +# a different controller and keeps esp32_common.conf's 64 KB. +# +# ========== System heap ========== +# esp32_common.conf sizes the heap at 64 KB for the Espressif BLE controller, +# which allocates through k_malloc (CONFIG_ESP_BT_HEAP_SYSTEM=y). On Zephyr 4.5 +# that no longer fits the shared IRAM/DRAM SRAM of these chips: the new +# hal_espressif controller blob places ~15-19 KB of link-layer patch code +# (r_lld_*_hack / *_eco) in IRAM, and dram0 shrinks by the same amount. On the +# S3, Heltec V4/V43, Wireless Tracker V2 and Meshnology W12 overflowed in the +# sysbuild (MCUboot) layout; the XIAO ESP32-C3 companion went from 92.83% to +# 99.02% dram0. +# +# Measured on a XIAO ESP32-S3 companion (2026-09-19, Zephyr main 3e8f38faf93, +# CONFIG_SYS_HEAP_RUNTIME_STATS): 26.8 KB allocated after BLE init, peak +# 28.8 KB across boot, pairing, contact/channel sync and a live session. +# 44 KB leaves ~16 KB above that for boards that allocate more before +# bt_enable() -- the ThinkNode M9 (display, keypad, LR1110) is the reason the +# pool was once raised from 32 KB (see esp32_common.conf). Applied to the C3 +# 2026-09-19 on the strength of the shared controller; not yet measured on C3 +# hardware (`get mem` with CONFIG_ZEPHCORE_MEM_STATS=y is the check). +# +# Repeater builds do not use this value: wifi_ota.conf, appended later, sets +# the heap back to 64 KB for the WiFi driver. +CONFIG_HEAP_MEM_POOL_SIZE=45056 diff --git a/zephcore/boards/common/esp32s3_common.conf b/zephcore/boards/common/esp32s3_common.conf deleted file mode 100644 index 87136d4..0000000 --- a/zephcore/boards/common/esp32s3_common.conf +++ /dev/null @@ -1,28 +0,0 @@ -# ESP32-S3 only. Loaded after esp32_common.conf and before board.conf -# (CMakeLists.txt, matched on the /esp32s3/ board qualifier). -# -# ========== System heap ========== -# esp32_common.conf sizes the heap at 64 KB for the Espressif BLE controller, -# which allocates through k_malloc (CONFIG_ESP_BT_HEAP_SYSTEM=y). On Zephyr 4.5 -# that no longer fits the S3's shared SRAM: the new hal_espressif controller -# blob (libbtdm_app.a) places ~15 KB of link-layer patch code (r_lld_*_hack / -# *_eco) in IRAM, and IRAM and DRAM share the same physical SRAM, so dram0 -# grew by ~21 KB and Heltec V4/V43, Wireless Tracker V2 and Meshnology W12 -# overflowed in the sysbuild (MCUboot) layout. -# -# Measured on a XIAO ESP32-S3 companion (2026-09-19, Zephyr main 3e8f38faf93, -# CONFIG_SYS_HEAP_RUNTIME_STATS): 26.8 KB allocated after BLE init, peak -# 28.8 KB across boot, pairing, contact/channel sync and a live session. -# 44 KB leaves ~16 KB above that for boards that allocate more before -# bt_enable() -- the ThinkNode M9 (display, keypad, LR1110) is the reason the -# pool was once raised from 32 KB (see esp32_common.conf). -# -# C3/C6 keep 64 KB: different controller blobs, not measured, and they fit. -CONFIG_HEAP_MEM_POOL_SIZE=45056 - -# ========== System workqueue stack ========== -# 8192 in zephcore_common.conf. Measured on the XIAO ESP32-S3 (same session as -# the heap figure above, plus a repeater build with flood forwarding and the -# WiFi-OTA AP/HTTP server started and stopped): peak 1092 B (companion). -# 4096 = 3.75x. -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/zephcore/boards/common/nrf52_common.conf b/zephcore/boards/common/nrf52_common.conf index 3feab66..4a4ea51 100644 --- a/zephcore/boards/common/nrf52_common.conf +++ b/zephcore/boards/common/nrf52_common.conf @@ -64,11 +64,3 @@ CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 # BT_CTLR_ASSERT_HANDLER is harmless when CONFIG_ASSERT=n (no RAM/flash cost, # the handler is simply never invoked) so it stays on for both prod and debug. CONFIG_BT_CTLR_ASSERT_HANDLER=y - -# System workqueue stack: 8192 in zephcore_common.conf is ~10x what it uses. -# Measured 2026-09-19 (CONFIG_ZEPHCORE_MEM_STATS, Zephyr main 3e8f38faf93), -# Wio L1 (SX1262) + T1000-E (LR1110), companion and repeater, under a phone -# BLE session with contact sync, radio TX/RX + flood forwarding, GPS on/off and -# prefs writes: peak 808 B. 4096 = 5x, and the floor healthcheck.conf already -# names for DBG-level logging from ISR context. -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/zephcore/boards/common/stm32wl_common.conf b/zephcore/boards/common/stm32wl_common.conf index e18691c..d819a28 100644 --- a/zephcore/boards/common/stm32wl_common.conf +++ b/zephcore/boards/common/stm32wl_common.conf @@ -49,7 +49,7 @@ CONFIG_MBEDTLS_AES_ROM_TABLES=y # juggles a couple of ephemeral AES keys at once; 8 is ample. CONFIG_MBEDTLS_PSA_KEY_SLOT_COUNT=8 -# Trim the common 8KB stacks to fit 64KB SRAM. Crypto (Ed25519/PSA) and the -# RNG jitter sampler run on the main thread, so keep main comfortable. +# Trim the common 8KB main stack to fit 64KB SRAM. Crypto (Ed25519/PSA) and the +# RNG jitter sampler run on the main thread, so keep main comfortable. (The +# system workqueue is already 4 KB in zephcore_common.conf.) CONFIG_MAIN_STACK_SIZE=6144 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 diff --git a/zephcore/boards/common/zephcore_common.conf b/zephcore/boards/common/zephcore_common.conf index 3fdf4ea..a3c21c3 100644 --- a/zephcore/boards/common/zephcore_common.conf +++ b/zephcore/boards/common/zephcore_common.conf @@ -7,7 +7,17 @@ # ========== Core Settings ========== CONFIG_MAIN_STACK_SIZE=8192 -CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=8192 +# System workqueue: measured 2026-09-19 (CONFIG_ZEPHCORE_MEM_STATS, Zephyr main +# 3e8f38faf93) at peak 808 B on nRF52 (Wio L1 SX1262 + T1000-E LR1110, +# companion and repeater, phone BLE session with contact sync, TX/RX + flood +# forwarding, GPS on/off, prefs writes) and 1092 B on a XIAO ESP32-S3 +# (companion, plus a repeater with the WiFi-OTA AP/HTTP server up). Its load +# is ZephCore's own work items on every platform: no BLE/WiFi platform driver +# we build (nRF, ESP32, MG24, STM32WL) submits to it, and main moved the BT +# host's LE work to the BT workqueue (22896cb8d6f). 4096 = 3.75x the worst +# peak, and the floor healthcheck.conf names for DBG logging from ISR context. +# Was 8192 until 2026-09-19. +CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=4096 CONFIG_EVENTS=y CONFIG_CPP=y diff --git a/zephcore/boards/example_board/board.overlay b/zephcore/boards/example_board/board.overlay index 82cef01..c6d23f6 100644 --- a/zephcore/boards/example_board/board.overlay +++ b/zephcore/boards/example_board/board.overlay @@ -332,6 +332,12 @@ * │ 0xED000 - 0x100000 (76KB) UF2 bootloader (reserved) │ * └─────────────────────────────────────────────────────────────────────┘ * + * NOTE (Zephyr main, since 2026-09-19): every partition node in the examples + * below also needs compatible = "zephyr,mapped-partition"; (omitted from + * each one for brevity). Without it, code that opens the partition fails to + * compile with "..._PARTITION_ID undeclared" — see any + * boards/esp32//partitions.overlay for the full form. + * * /delete-node/ &boot_partition; * /delete-node/ &slot0_partition; * /delete-node/ &slot1_partition; diff --git a/zephcore/boards/linux_native/linux_common.overlay b/zephcore/boards/linux_native/linux_common.overlay index 308f372..5c77a02 100644 --- a/zephcore/boards/linux_native/linux_common.overlay +++ b/zephcore/boards/linux_native/linux_common.overlay @@ -37,6 +37,7 @@ &flash0 { partitions { lfs_partition: partition@100000 { + compatible = "zephyr,mapped-partition"; label = "lfs"; reg = <0x00100000 0x00080000>; /* 512 KB */ }; diff --git a/zephcore/patches/zephyr/0003-lora-sx126x-native.patch b/zephcore/patches/zephyr/0003-lora-sx126x-native.patch index 6f1c0d8..a2278dd 100644 --- a/zephcore/patches/zephyr/0003-lora-sx126x-native.patch +++ b/zephcore/patches/zephyr/0003-lora-sx126x-native.patch @@ -576,9 +576,39 @@ rebase is deliberately behaviour-preserving ("step A"): * The DIO1 work queue is named through k_work_queue_config.name. Main deprecates writing k_work_q.thread directly. -Step B, still open: shrink this patch toward upstream's own duty-cycle -structure one piece at a time, with a bench PDR run per piece, until what -remains is only what upstream does not do. Validated so far, on a Wio Tracker +Step B (2026-09-19), each piece benched on a Wio L1 <-> T1000-E PDR run +with duty cycle on, 50 packets each way, all 50/50 (pieces 1-2 at 2 s and +800 ms gaps, piece 3 at 800 ms): + + 1. Upstream's sx126x_set_stop_timer_on_preamble() and + sx126x_set_rx_duty_cycle() restored verbatim and used by both of our + duty-cycle entry points instead of open-coded command writes. Same two + commands in the same order; at the entry site the RX-gain register write + now precedes StopTimerOnPreamble (all in STDBY). The duplicate + SX126X_REG_RETENTION_LIST / SX126X_MAX_RETENTION_REGS defines this patch + added to sx126x_regs.h were dropped -- upstream defines both. + 2. Our dc_rx_time / dc_sleep_time fields dropped in favour of upstream's + data->duty_cycle.rx_period / .sleep_period (same 15.625 us units). + 3. Upstream's sx126x_duty_cycle_stop() and its preamble-length explanation + restored, and used by the duty-cycle entry's cancel branch (identical + wake-if-BUSY -> STDBY_RC -> sleep sequence). + +Net: +2509/-364 -> +2478/-287 lines against upstream. Deliberately not +done: replacing our STATE_RX + rx_duty_cycle_enabled model with upstream's +SX126X_STATE_RX_DUTY_CYCLE. Upstream's enum value already sits in the base +file unused, so the switch would shrink nothing, while rewriting the RX->TX +CAS, busy gate, watchdog and restart paths -- the riskiest code in the +driver. What this patch still deletes is upstream code ours replaces on +purpose: its duty-cycle driver (ours re-arms RX before running the +callback and has the parked-RX watchdog) and its RX-done/timeout handlers. + +Open follow-up, not a reduction: sx126x_lora_recv_async()'s cancel branch +(ZephCore's hwCancelReceive()) issues SetStandby without a raw-NSS wake, +so under duty cycle it can block in wait_busy() until the chip's next RX +window (bounded by one sleep period). sx126x_duty_cycle_stop() would avoid +that, but it is a behaviour change on paths a PDR run does not reach +(reconfigure, CAD-timeout recovery, runtime rxduty toggle), so it waits for +a bench that exercises them. Validated so far, on a Wio Tracker L1 and a T1000-E: 50/50 packet parity with the v4.4 build in both directions, with RX duty cycle off (2 s gap) and on (2 s and 800 ms gaps). @@ -624,7 +654,7 @@ Regenerate with: appended to this preamble. diff --git a/drivers/lora/native/sx126x/sx126x.c b/drivers/lora/native/sx126x/sx126x.c -index c8e682ee84c..399b84e8086 100644 +index c8e682ee8..b5ab10c4e 100644 --- a/drivers/lora/native/sx126x/sx126x.c +++ b/drivers/lora/native/sx126x/sx126x.c @@ -6,14 +6,52 @@ @@ -787,13 +817,11 @@ index c8e682ee84c..399b84e8086 100644 static bool should_enable_ldro(enum lora_datarate sf, enum lora_signal_bandwidth bw, const struct sx126x_hal_config *config) { -@@ -336,46 +474,138 @@ static int sx126x_set_sync_word(const struct device *dev, const struct lora_mode +@@ -336,10 +474,128 @@ static int sx126x_set_sync_word(const struct device *dev, const struct lora_mode return sx126x_hal_write_regs(dev, SX126X_REG_LORA_SYNC_WORD_MSB, buf, 2); } -static int sx126x_set_rx_gain(const struct device *dev, bool boosted) --{ -- uint8_t val = boosted ? SX126X_RX_GAIN_BOOSTED : SX126X_RX_GAIN_POWER_SAVING; +/* RSSI / AGC band calibration (DS Sec 6.1.6, Table 6-4). + * + * Semtech: "The power seen by the SX1261/2 analog front-end is affected by @@ -823,9 +851,7 @@ index c8e682ee84c..399b84e8086 100644 + uint8_t sensi_adjust; /* 0x08AC, bits 7:2 */ + uint8_t gain_tune[SX126X_AGC_GAIN_TUNE_LEN]; /* 0x08F5 .. 0x08FB */ +}; - -- return sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &val, 1); --} ++ +static const struct sx126x_agc_band_cal sx126x_agc_cal_high = { + .rssi_meas_cal_h = 0x01, + .rssi_meas_cal_l = 0x53, @@ -833,11 +859,7 @@ index c8e682ee84c..399b84e8086 100644 + .sensi_adjust = SX126X_AGC_SENSI_ADJUST_HIGH_BAND, + .gain_tune = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, +}; - --static int sx126x_set_tx(const struct device *dev, uint32_t timeout_ms) --{ -- uint32_t timeout = SX126X_MS_TO_TIMEOUT(timeout_ms); -- uint8_t buf[3]; ++ +static const struct sx126x_agc_band_cal sx126x_agc_cal_low = { + .rssi_meas_cal_h = 0x01, + .rssi_meas_cal_l = 0x27, @@ -846,44 +868,30 @@ index c8e682ee84c..399b84e8086 100644 + /* GainTune_1_2, _3_4, _5_6, _7_8, _9_10, _11_12, _13 */ + .gain_tune = { 0xDE, 0xE2, 0x32, 0x44, 0x33, 0x34, 0x04 }, +}; - -- sys_put_be24(timeout, buf); -- return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_TX, buf, 3); --} ++ +/* Band split. The datasheet only characterises 470-490 and 868-915, so any + * threshold between them is a judgement call; 600 MHz sits clear of both the + * 433/470 group and the 779/868/915 group that sx126x_calibrate_image() + * already distinguishes. */ +#define SX126X_AGC_LOW_BAND_MAX_HZ 600000000 - --static int sx126x_set_stop_timer_on_preamble(const struct device *dev, -- bool enable) ++ +static const struct sx126x_agc_band_cal *sx126x_agc_cal_for(uint32_t freq) - { -- uint8_t val = enable; -- -- return sx126x_hal_write_cmd(dev, SX126X_CMD_STOP_TIMER_ON_PREAMBLE, -- &val, 1); ++{ + return (freq < SX126X_AGC_LOW_BAND_MAX_HZ) ? &sx126x_agc_cal_low + : &sx126x_agc_cal_high; - } - --static int sx126x_set_rx_duty_cycle(const struct device *dev, -- uint32_t rx_period, uint32_t sleep_period) ++} ++ +/* Apply the band's RSSI/AGC calibration. Caches AgcSensiAdjust for the RX + * gain writes, which share register 0x08AC, but does NOT touch 0x08AC itself + * -- sx126x_set_rx_gain() owns that register and runs later in + * sx126x_config(). */ +static int sx126x_apply_agc_band_cal(const struct device *dev, uint32_t freq) - { -- uint8_t buf[6]; ++{ + struct sx126x_data *data = dev->data; + const struct sx126x_agc_band_cal *cal = sx126x_agc_cal_for(freq); + uint8_t val; - int ret; - -- sys_put_be24(rx_period, &buf[0]); -- sys_put_be24(sleep_period, &buf[3]); ++ int ret; ++ + data->agc_sensi_adjust = cal->sensi_adjust; + + /* Bits 4:0 only -- preserve the rest of 0x089C. */ @@ -902,15 +910,13 @@ index c8e682ee84c..399b84e8086 100644 + if (ret < 0) { + return ret; + } - -- ret = sx126x_set_stop_timer_on_preamble(dev, true); ++ + val = cal->gforst_powthr; + ret = sx126x_hal_write_regs(dev, SX126X_REG_AGC_GFORST_POWTHR, &val, 1); - if (ret < 0) { - return ret; - } - -- return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE, buf, 6); ++ if (ret < 0) { ++ return ret; ++ } ++ + return sx126x_hal_write_regs(dev, SX126X_REG_AGC_GAIN_TUNE_1_2, + cal->gain_tune, + SX126X_AGC_GAIN_TUNE_LEN); @@ -918,12 +924,13 @@ index c8e682ee84c..399b84e8086 100644 + +/* Byte to write to 0x08AC: band AgcSensiAdjust in bits 7:2, gain mode in 1:0. */ +static uint8_t sx126x_rx_gain_byte(const struct device *dev, bool boosted) -+{ + { +- uint8_t val = boosted ? SX126X_RX_GAIN_BOOSTED : SX126X_RX_GAIN_POWER_SAVING; + struct sx126x_data *data = dev->data; + + return SX126X_RX_GAIN_VAL(data->agc_sensi_adjust, boosted); +} -+ + +static int sx126x_set_rx_gain(const struct device *dev, bool boosted) +{ + uint8_t val = sx126x_rx_gain_byte(dev, boosted); @@ -938,20 +945,10 @@ index c8e682ee84c..399b84e8086 100644 + * wakes are chip-internal warm starts the host never sees, which is + * why 0x08AC is in the retention list installed by + * sx126x_install_retention_list() at chip init. */ -+ return sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &val, 1); -+} -+ -+static int sx126x_set_tx(const struct device *dev, uint32_t timeout_ms) -+{ -+ uint32_t timeout = SX126X_MS_TO_TIMEOUT(timeout_ms); -+ uint8_t buf[3]; -+ -+ sys_put_be24(timeout, buf); -+ return sx126x_hal_write_cmd(dev, SX126X_CMD_SET_TX, buf, 3); + return sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &val, 1); } - static int sx126x_set_rx(const struct device *dev, uint32_t timeout_ms) -@@ -415,7 +645,7 @@ static int sx126x_get_packet_status(const struct device *dev, +@@ -415,7 +671,7 @@ static int sx126x_get_packet_status(const struct device *dev, uint8_t buf[3]; int ret; @@ -960,7 +957,7 @@ index c8e682ee84c..399b84e8086 100644 if (ret == 0) { /* RSSI is -value/2 dBm */ *rssi = -((int16_t)buf[0] >> 1); -@@ -443,6 +673,12 @@ static int sx126x_add_reg_to_retention(const struct device *dev, uint16_t addr) +@@ -443,6 +699,12 @@ static int sx126x_add_reg_to_retention(const struct device *dev, uint16_t addr) n = buf[0]; @@ -973,7 +970,7 @@ index c8e682ee84c..399b84e8086 100644 for (uint8_t i = 0; i < n; i++) { if (sys_get_be16(&buf[1 + 2 * i]) == addr) { return 0; -@@ -460,6 +696,92 @@ static int sx126x_add_reg_to_retention(const struct device *dev, uint16_t addr) +@@ -460,6 +722,92 @@ static int sx126x_add_reg_to_retention(const struct device *dev, uint16_t addr) return sx126x_hal_write_regs(dev, SX126X_REG_RETENTION_LIST, buf, sizeof(buf)); } @@ -1066,7 +1063,7 @@ index c8e682ee84c..399b84e8086 100644 static int sx126x_chip_init(const struct device *dev) { const struct sx126x_hal_config *config = dev->config; -@@ -520,6 +842,26 @@ static int sx126x_chip_init(const struct device *dev) +@@ -520,6 +868,26 @@ static int sx126x_chip_init(const struct device *dev) return ret; } @@ -1093,7 +1090,7 @@ index c8e682ee84c..399b84e8086 100644 /* Set packet type to LoRa */ ret = sx126x_set_packet_type(dev, SX126X_PACKET_TYPE_LORA); if (ret < 0) { -@@ -527,10 +869,23 @@ static int sx126x_chip_init(const struct device *dev) +@@ -527,10 +895,23 @@ static int sx126x_chip_init(const struct device *dev) return ret; } @@ -1120,7 +1117,7 @@ index c8e682ee84c..399b84e8086 100644 if (ret < 0) { LOG_ERR("Set IRQ params failed: %d", ret); return ret; -@@ -543,21 +898,9 @@ static int sx126x_chip_init(const struct device *dev) +@@ -543,21 +924,9 @@ static int sx126x_chip_init(const struct device *dev) return ret; } @@ -1145,7 +1142,7 @@ index c8e682ee84c..399b84e8086 100644 LOG_INF("SX126x initialized"); return 0; -@@ -567,15 +910,44 @@ static void sx126x_dio1_callback(const struct device *dev) +@@ -567,15 +936,44 @@ static void sx126x_dio1_callback(const struct device *dev) { struct sx126x_data *data = dev->data; @@ -1192,7 +1189,7 @@ index c8e682ee84c..399b84e8086 100644 sx126x_hal_set_rf_switch(dev, enable, tx); } } -@@ -595,6 +967,7 @@ static void sx126x_disconnect_rf_gpios(const struct device *dev) +@@ -595,6 +993,7 @@ static void sx126x_disconnect_rf_gpios(const struct device *dev) sx126x_disconnect_gpio(&config->antenna_enable); sx126x_disconnect_gpio(&config->tx_enable); sx126x_disconnect_gpio(&config->rx_enable); @@ -1200,7 +1197,7 @@ index c8e682ee84c..399b84e8086 100644 } static int sx126x_reconnect_rf_gpios(const struct device *dev) -@@ -620,10 +993,238 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev) +@@ -620,10 +1019,232 @@ static int sx126x_reconnect_rf_gpios(const struct device *dev) return ret; } @@ -1303,10 +1300,6 @@ index c8e682ee84c..399b84e8086 100644 + * preamble arriving at the tail of the RX window can get + * cut off when the sleep phase begins. Kept as insurance -- + * see the note above. */ -+ uint8_t stop_on_preamble = 1; -+ sx126x_hal_write_cmd(dev, SX126X_CMD_STOP_TIMER_ON_PREAMBLE, -+ &stop_on_preamble, 1); -+ + /* No retention-list write here. Keeping boosted RX gain alive + * across the chip-internal sleep->RX wakes is still MANDATORY + * (DS sec 9.6: the Rx gain configuration "is not saved when @@ -1320,12 +1313,10 @@ index c8e682ee84c..399b84e8086 100644 + * needs the list at all -- the LR2021 retains the gain + * (DS sec 6.3.8) and the LR11xx documents no caveat either way. */ + -+ /* Re-apply duty cycle with stored timing */ -+ uint8_t dc_buf[6]; -+ sys_put_be24(data->dc_rx_time, &dc_buf[0]); -+ sys_put_be24(data->dc_sleep_time, &dc_buf[3]); -+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE, -+ dc_buf, 6); ++ /* Re-apply duty cycle with stored timing. Upstream's helper ++ * issues StopTimerOnPreamble (see above) and then SetRxDutyCycle. */ ++ (void)sx126x_set_rx_duty_cycle(dev, data->duty_cycle.rx_period, ++ data->duty_cycle.sleep_period); + + sx126x_dc_watchdog_kick(data); + } else { @@ -1439,7 +1430,7 @@ index c8e682ee84c..399b84e8086 100644 static int sx126x_set_sleep(const struct device *dev) { struct sx126x_data *data = dev->data; -@@ -697,94 +1298,214 @@ static void sx126x_handle_irq_tx_done(const struct device *dev) +@@ -697,94 +1318,214 @@ static void sx126x_handle_irq_tx_done(const struct device *dev) k_msgq_put(&data->tx_msgq, &result, K_NO_WAIT); } @@ -1714,7 +1705,7 @@ index c8e682ee84c..399b84e8086 100644 return; } -@@ -827,13 +1548,97 @@ static void sx126x_irq_work_handler(struct k_work *work) +@@ -827,13 +1568,97 @@ static void sx126x_irq_work_handler(struct k_work *work) } if (irq_status & SX126X_IRQ_RX_DONE) { @@ -1812,7 +1803,7 @@ index c8e682ee84c..399b84e8086 100644 /* Re-enable the DIO1 interrupt for the next event (unless sleeping) */ if (atomic_get(&data->state) != SX126X_REST_STATE) { sx126x_hal_dio1_irq_enable(dev); -@@ -894,10 +1699,19 @@ static int sx126x_lora_config(const struct device *dev, +@@ -894,10 +1719,19 @@ static int sx126x_lora_config(const struct device *dev, goto out; } @@ -1833,30 +1824,20 @@ index c8e682ee84c..399b84e8086 100644 if (ret < 0) { goto out; } -@@ -915,13 +1729,19 @@ static int sx126x_lora_config(const struct device *dev, +@@ -915,6 +1749,12 @@ static int sx126x_lora_config(const struct device *dev, goto out; } -- /* Set sync word */ -- ret = sx126x_set_sync_word(dev, config); + /* Sec 15.1 TX Modulation workaround (datasheet errata). */ + ret = sx126x_apply_tx_modulation_workaround(dev, config->bandwidth); - if (ret < 0) { - goto out; - } - -- /* Set RX gain */ -+ /* Set sync word */ -+ ret = sx126x_set_sync_word(dev, config); + if (ret < 0) { + goto out; + } + -+ /* Set RX gain */ - ret = sx126x_set_rx_gain(dev, hal_config->rx_boosted); + /* Set sync word */ + ret = sx126x_set_sync_word(dev, config); if (ret < 0) { - goto out; -@@ -938,6 +1758,10 @@ out: +@@ -938,6 +1778,10 @@ out: return ret; } @@ -1867,7 +1848,7 @@ index c8e682ee84c..399b84e8086 100644 static int sx126x_lora_send_async(const struct device *dev, uint8_t *data_buf, uint32_t data_len, struct k_poll_signal *async) -@@ -955,11 +1779,27 @@ static int sx126x_lora_send_async(const struct device *dev, +@@ -955,11 +1799,27 @@ static int sx126x_lora_send_async(const struct device *dev, return -EINVAL; } @@ -1897,7 +1878,7 @@ index c8e682ee84c..399b84e8086 100644 k_mutex_lock(&data->lock, K_FOREVER); ret = sx126x_ensure_ready(dev); -@@ -969,6 +1809,59 @@ static int sx126x_lora_send_async(const struct device *dev, +@@ -969,6 +1829,59 @@ static int sx126x_lora_send_async(const struct device *dev, return ret; } @@ -1957,7 +1938,7 @@ index c8e682ee84c..399b84e8086 100644 data->tx_async_signal = async; k_msgq_purge(&data->tx_msgq); -@@ -999,8 +1892,90 @@ static int sx126x_lora_send_async(const struct device *dev, +@@ -999,8 +1912,90 @@ static int sx126x_lora_send_async(const struct device *dev, /* Enable antenna and set TX path */ sx126x_set_rf_path(dev, true, true); @@ -2050,7 +2031,7 @@ index c8e682ee84c..399b84e8086 100644 if (ret < 0) { goto out_error; } -@@ -1069,6 +2044,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf, +@@ -1069,6 +2064,8 @@ static int sx126x_lora_recv(const struct device *dev, uint8_t *data_buf, } data->rx_cb = NULL; @@ -2059,7 +2040,7 @@ index c8e682ee84c..399b84e8086 100644 k_msgq_purge(&data->rx_msgq); /* Set packet parameters for variable length reception */ -@@ -1140,6 +2117,8 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -1140,6 +2137,8 @@ static int sx126x_lora_recv_async(const struct device *dev, /* Stop async reception */ data->rx_cb = NULL; data->rx_cb_user_data = NULL; @@ -2068,7 +2049,7 @@ index c8e682ee84c..399b84e8086 100644 if (atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) { sx126x_set_standby(dev, SX126X_STANDBY_RC); sx126x_set_sleep(dev); -@@ -1154,6 +2133,19 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -1154,6 +2153,19 @@ static int sx126x_lora_recv_async(const struct device *dev, return -EINVAL; } @@ -2088,7 +2069,7 @@ index c8e682ee84c..399b84e8086 100644 if (!atomic_cas(&data->state, SX126X_REST_STATE, SX126X_STATE_RX)) { LOG_ERR("Busy"); k_mutex_unlock(&data->lock); -@@ -1167,8 +2159,18 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -1167,8 +2179,18 @@ static int sx126x_lora_recv_async(const struct device *dev, return ret; } @@ -2107,7 +2088,7 @@ index c8e682ee84c..399b84e8086 100644 /* Set packet parameters */ ret = sx126x_set_packet_params(dev, -@@ -1181,6 +2183,7 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -1181,6 +2203,7 @@ static int sx126x_lora_recv_async(const struct device *dev, SX126X_LORA_IQ_INVERTED : SX126X_LORA_IQ_STANDARD); if (ret < 0) { data->rx_cb = NULL; @@ -2115,7 +2096,7 @@ index c8e682ee84c..399b84e8086 100644 sx126x_set_sleep(dev); k_mutex_unlock(&data->lock); return ret; -@@ -1193,221 +2196,25 @@ static int sx126x_lora_recv_async(const struct device *dev, +@@ -1193,11 +2216,21 @@ static int sx126x_lora_recv_async(const struct device *dev, ret = sx126x_set_rx(dev, 0); if (ret < 0) { data->rx_cb = NULL; @@ -2126,63 +2107,21 @@ index c8e682ee84c..399b84e8086 100644 return ret; } -- k_mutex_unlock(&data->lock); -- return 0; --} -- --/* -- * Read this if you have trouble making the duty cycle working reliably. -- * I have wasted many hours on this so I'm passing this along. -- * -- * The driver sets StopTimerOnPreamble = true before entering duty -- * cycle mode. This makes the radio stay in RX once a preamble is -- * detected, instead of going back to sleep when the RX window -- * expires. The radio then searches for the sync word within a -- * window sized by the receiver's preamble_len. This value must -- * match the sender's: a mismatch causes the demodulator to stop -- * searching before the sync word arrives. -- * -- * Matching preamble_len — OK (works even if RX joins mid-preamble) -- * -- * TX |#########< 100 preamble symbols >###########|SW|HDR| payload | -- * ^ ^ -- * RX ..sleep..|==RX==|<<< stays in RX >>>>>>>>>>>>| -- * ^ | -- * preamble detected | -- * timer stops (StopTimerOnPreamble)| -- * search window = 100 symbols | -- * sync word found -----------------+ -- * --> full packet received, RX_DONE -- * -- * Mismatched preamble_len (RX = 8, TX = 100) — FAIL -- * -- * TX |#########< 100 preamble symbols >###########|SW|HDR| payload | -- * ^ ^ -- * RX ..sleep..|==RX==|<<< stays in RX | -- * ^ but gives up here | -- * | |<-8->| | -- * preamble detected | -- * search window = only 8 symbols | -- * sync word NOT found within window -- * --> demodulator fails, packet lost -- * -- * See samples/drivers/lora/duty_cycle/ -- */ --static void sx126x_duty_cycle_stop(const struct device *dev) --{ -- /* -- * During duty cycle the BUSY pin stays asserted even in the -- * sleep phase. Wake the radio via a raw NSS edge (bypasses -- * BUSY check) so the standby command can go through -- * immediately. -- */ -- if (sx126x_hal_is_busy(dev)) { -- sx126x_hal_wakeup(dev); -- } -- sx126x_set_standby(dev, SX126X_STANDBY_RC); -- sx126x_set_sleep(dev); --} -- ++ /* Re-apply RX gain after SetRx -- register 0x08AC is reset on every ++ * SetRx without retention, and lora_config sets gain before going to ++ * sleep, so without this the first packet would be received in ++ * power-saving gain regardless of DTS rx-boosted. */ ++ uint8_t gain = sx126x_rx_gain_byte(dev, data->rx_boost_enabled); ++ ++ sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1); ++ + k_mutex_unlock(&data->lock); + return 0; + } +@@ -1255,159 +2288,6 @@ static void sx126x_duty_cycle_stop(const struct device *dev) + sx126x_set_sleep(dev); + } + -static int sx126x_duty_cycle_start(const struct device *dev, - k_timeout_t rx_period, - k_timeout_t sleep_period) @@ -2215,21 +2154,15 @@ index c8e682ee84c..399b84e8086 100644 - if (ret < 0) { - goto out_error; - } -+ /* Re-apply RX gain after SetRx -- register 0x08AC is reset on every -+ * SetRx without retention, and lora_config sets gain before going to -+ * sleep, so without this the first packet would be received in -+ * power-saving gain regardless of DTS rx-boosted. */ -+ uint8_t gain = sx126x_rx_gain_byte(dev, data->rx_boost_enabled); - +- - k_mutex_unlock(&data->lock); - return 0; -+ sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1); - +- -out_error: - data->rx_cb = NULL; - atomic_set(&data->state, SX126X_REST_STATE); - sx126x_set_sleep(dev); - k_mutex_unlock(&data->lock); +- k_mutex_unlock(&data->lock); - return ret; -} - @@ -2262,9 +2195,9 @@ index c8e682ee84c..399b84e8086 100644 - data->rx_cb = cb; - data->rx_cb_user_data = user_data; - - return 0; - } - +- return 0; +-} +- -static int sx126x_lora_recv_duty_cycle(const struct device *dev, - k_timeout_t rx_period, - k_timeout_t sleep_period, @@ -2345,7 +2278,7 @@ index c8e682ee84c..399b84e8086 100644 static uint32_t sx126x_lora_airtime(const struct device *dev, uint32_t data_len) { struct sx126x_data *data = dev->data; -@@ -1491,7 +2298,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, +@@ -1491,7 +2371,7 @@ static int sx126x_lora_test_cw(const struct device *dev, uint32_t frequency, /* Set PA config and TX power */ ret = sx126x_hal_configure_tx_params(dev, tx_power, frequency, @@ -2354,7 +2287,7 @@ index c8e682ee84c..399b84e8086 100644 if (ret < 0) { goto out_sleep; } -@@ -1523,50 +2330,1135 @@ out_unlock: +@@ -1523,50 +2403,1122 @@ out_unlock: return ret; } @@ -3232,16 +3165,10 @@ index c8e682ee84c..399b84e8086 100644 + data->rx_duty_cycle_enabled = false; + sx126x_reset_rx_busy_signals(data); + if (atomic_cas(&data->state, SX126X_STATE_RX, SX126X_STATE_IDLE)) { -+ /* If we're called during the duty-cycle sleep phase the -+ * chip is actually asleep and BUSY is asserted -- issuing -+ * SetStandby straight away stalls the HAL in -+ * wait_busy(). Poke CS via sx126x_hal_wakeup() first so -+ * BUSY drops before we try to talk to the chip. */ -+ if (sx126x_hal_is_busy(dev)) { -+ sx126x_hal_wakeup(dev); -+ } -+ sx126x_set_standby(dev, SX126X_STANDBY_RC); -+ sx126x_set_sleep(dev); ++ /* May be called in the duty-cycle sleep phase with BUSY ++ * asserted; upstream's stop wakes via a raw NSS edge ++ * before STDBY_RC + sleep. */ ++ sx126x_duty_cycle_stop(dev); + } + k_mutex_unlock(&data->lock); + return 0; @@ -3293,8 +3220,8 @@ index c8e682ee84c..399b84e8086 100644 + * dropped RX window. */ + if (atomic_get(&data->state) == SX126X_STATE_RX) { + const bool rearm = !data->rx_duty_cycle_enabled || -+ data->dc_rx_time != rx_time || -+ data->dc_sleep_time != sleep_time; ++ data->duty_cycle.rx_period != rx_time || ++ data->duty_cycle.sleep_period != sleep_time; + + data->rx_cb = cb; + data->rx_cb_user_data = user_data; @@ -3305,8 +3232,8 @@ index c8e682ee84c..399b84e8086 100644 + return 0; + } + -+ data->dc_rx_time = rx_time; -+ data->dc_sleep_time = sleep_time; ++ data->duty_cycle.rx_period = rx_time; ++ data->duty_cycle.sleep_period = sleep_time; + data->rx_duty_cycle_enabled = true; + + /* Mid-cycle the chip may be in its duty-cycle sleep phase with @@ -3417,18 +3344,14 @@ index c8e682ee84c..399b84e8086 100644 + + /* Store for restart_rx to re-use (periods validated and converted at + * function entry, before the state was claimed). */ -+ data->dc_rx_time = rx_time; -+ data->dc_sleep_time = sleep_time; ++ data->duty_cycle.rx_period = rx_time; ++ data->duty_cycle.sleep_period = sleep_time; + data->rx_duty_cycle_enabled = true; + + /* StopTimerOnPreamble: keep the RX timer from expiring mid-packet + * when a preamble is detected late in the RX window. Without it, + * a packet whose preamble lands near the tail of the RX window + * gets cut off by the sleep transition. */ -+ uint8_t stop_on_preamble = 1; -+ sx126x_hal_write_cmd(dev, SX126X_CMD_STOP_TIMER_ON_PREAMBLE, -+ &stop_on_preamble, 1); -+ + /* Apply RX gain. The warm-start retention list that makes the + * chip-internal sleep->RX wakes restore it (DS sec 9.6: mandatory for + * SetRxDutyCycle -- without it every window after the first sleep @@ -3438,11 +3361,8 @@ index c8e682ee84c..399b84e8086 100644 + + sx126x_hal_write_regs(dev, SX126X_REG_RX_GAIN, &gain, 1); + -+ uint8_t buf[6]; -+ -+ sys_put_be24(rx_time, &buf[0]); -+ sys_put_be24(sleep_time, &buf[3]); -+ sx126x_hal_write_cmd(dev, SX126X_CMD_SET_RX_DUTY_CYCLE, buf, 6); ++ /* StopTimerOnPreamble (comment above) + SetRxDutyCycle. */ ++ (void)sx126x_set_rx_duty_cycle(dev, rx_time, sleep_time); + + sx126x_dc_watchdog_kick(data); + @@ -3526,7 +3446,7 @@ index c8e682ee84c..399b84e8086 100644 /* Initialize HAL */ ret = sx126x_hal_init(dev); -@@ -1628,6 +3520,8 @@ static int sx126x_init(const struct device *dev) +@@ -1628,6 +3580,8 @@ static int sx126x_init(const struct device *dev) {0}), \ .rx_enable = GPIO_DT_SPEC_INST_GET_OR(inst, rx_enable_gpios, \ {0}), \ @@ -3535,7 +3455,7 @@ index c8e682ee84c..399b84e8086 100644 .dio2_tx_enable = DT_INST_PROP(inst, dio2_tx_enable), \ .dio3_tcxo_enable = DT_INST_NODE_HAS_PROP(inst, dio3_tcxo_voltage), \ .dio3_tcxo_voltage = DT_INST_PROP_OR(inst, dio3_tcxo_voltage, 0), \ -@@ -1682,6 +3576,8 @@ DT_INST_FOREACH_STATUS_OKAY_VARGS(SX126X_INIT, true) +@@ -1682,6 +3636,8 @@ DT_INST_FOREACH_STATUS_OKAY_VARGS(SX126X_INIT, true) {0}), \ .rx_enable = GPIO_DT_SPEC_INST_GET_OR(inst, rx_enable_gpios, \ {0}), \ @@ -3545,7 +3465,7 @@ index c8e682ee84c..399b84e8086 100644 .dio3_tcxo_enable = DT_INST_NODE_HAS_PROP(inst, dio3_tcxo_voltage), \ .dio3_tcxo_voltage = DT_INST_PROP_OR(inst, dio3_tcxo_voltage, 0), \ diff --git a/drivers/lora/native/sx126x/sx126x.h b/drivers/lora/native/sx126x/sx126x.h -index 0122958c32a..bac80103ff7 100644 +index 0122958c3..9d39100a7 100644 --- a/drivers/lora/native/sx126x/sx126x.h +++ b/drivers/lora/native/sx126x/sx126x.h @@ -57,6 +57,7 @@ struct sx126x_data { @@ -3556,7 +3476,7 @@ index 0122958c32a..bac80103ff7 100644 /* RX duty cycle parameters (in SX126x timeout ticks) */ struct { -@@ -69,7 +70,136 @@ struct sx126x_data { +@@ -69,7 +70,133 @@ struct sx126x_data { /* Deferred work for interrupt handling */ struct k_work irq_work; @@ -3628,9 +3548,6 @@ index 0122958c32a..bac80103ff7 100644 + * Reset together with the other RX-busy signals. */ + atomic_t raw_header_seen_at_ms; + -+ uint32_t dc_rx_time; /* stored duty cycle rx period (15.625us steps) */ -+ uint32_t dc_sleep_time; /* stored duty cycle sleep period (15.625us steps) */ -+ + /* Parked-RX watchdog. StopTimerOnPreamble=1 means a false preamble + * detect parks the chip in full RX "until the next received frame" + * (AN1200.36 sec 3) with no IRQ fired -- nothing is lost (parked RX @@ -3694,7 +3611,7 @@ index 0122958c32a..bac80103ff7 100644 #endif /* ZEPHYR_DRIVERS_LORA_SX126X_SX126X_INTERNAL_H_ */ diff --git a/drivers/lora/native/sx126x/sx126x_hal.c b/drivers/lora/native/sx126x/sx126x_hal.c -index 34f7089584f..394578956de 100644 +index 34f708958..394578956 100644 --- a/drivers/lora/native/sx126x/sx126x_hal.c +++ b/drivers/lora/native/sx126x/sx126x_hal.c @@ -150,7 +150,13 @@ int sx126x_hal_init(const struct device *dev) @@ -3787,7 +3704,7 @@ index 34f7089584f..394578956de 100644 uint8_t buf[2] = { (uint8_t)tx_power, ramp_time }; diff --git a/drivers/lora/native/sx126x/sx126x_hal.h b/drivers/lora/native/sx126x/sx126x_hal.h -index e4b26ba60bd..558890ad54c 100644 +index e4b26ba60..558890ad5 100644 --- a/drivers/lora/native/sx126x/sx126x_hal.h +++ b/drivers/lora/native/sx126x/sx126x_hal.h @@ -29,6 +29,10 @@ struct sx126x_hal_config { @@ -3802,7 +3719,7 @@ index e4b26ba60bd..558890ad54c 100644 uint8_t dio3_tcxo_voltage; bool dio2_tx_enable; diff --git a/drivers/lora/native/sx126x/sx126x_hal_common.c b/drivers/lora/native/sx126x/sx126x_hal_common.c -index 19c71594193..5f7eece3930 100644 +index 19c715941..5f7eece39 100644 --- a/drivers/lora/native/sx126x/sx126x_hal_common.c +++ b/drivers/lora/native/sx126x/sx126x_hal_common.c @@ -41,11 +41,29 @@ static int spi_transfer(const struct spi_dt_spec *spi, @@ -3839,7 +3756,7 @@ index 19c71594193..5f7eece3930 100644 return 0; diff --git a/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c b/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c -index b7ffbac7a6e..382acfc50b9 100644 +index b7ffbac7a..382acfc50 100644 --- a/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c +++ b/drivers/lora/native/sx126x/sx126x_hal_stm32wl.c @@ -254,6 +254,15 @@ int sx126x_hal_init(const struct device *dev) @@ -3859,10 +3776,10 @@ index b7ffbac7a6e..382acfc50b9 100644 return 0; } diff --git a/drivers/lora/native/sx126x/sx126x_regs.h b/drivers/lora/native/sx126x/sx126x_regs.h -index 1e6146f751f..5992e99969b 100644 +index 1e6146f75..03e72ff90 100644 --- a/drivers/lora/native/sx126x/sx126x_regs.h +++ b/drivers/lora/native/sx126x/sx126x_regs.h -@@ -175,10 +175,45 @@ +@@ -175,10 +175,34 @@ #define SX126X_LORA_SYNC_WORD_PUBLIC 0x3444 #define SX126X_LORA_SYNC_WORD_PRIVATE 0x1424 @@ -3895,22 +3812,11 @@ index 1e6146f751f..5992e99969b 100644 +/* AgcSensiAdjust (0x08AC bits 7:2) per band */ +#define SX126X_AGC_SENSI_ADJUST_HIGH_BAND 0x25 /* 868-915 MHz (default) */ +#define SX126X_AGC_SENSI_ADJUST_LOW_BAND 0x22 /* 470-490 MHz */ -+ -+/* Register retention list (DS Sec 9.6). Registers listed here are restored -+ * by the chip on a warm-start wake -- which is what every RxDutyCycle -+ * sleep->RX transition is. Layout: entry count at 0x029F, then one -+ * big-endian 16-bit register address per entry, four entries max. -+ * -+ * The datasheet scopes the loss to sleep/warm-start. It does NOT say SetRx -+ * resets the Rx gain: in continuous RX, with no sleep in the loop, 0x08AC -+ * simply persists. */ -+#define SX126X_REG_RETENTION_LIST 0x029F -+#define SX126X_MAX_RETENTION_REGS 4 /* TX Clamp Config (workaround for SX1262) */ #define SX126X_REG_TX_CLAMP_CFG 0x08D8 diff --git a/dts/bindings/lora/semtech,sx126x-base.yaml b/dts/bindings/lora/semtech,sx126x-base.yaml -index 2d13d758695..04390be84e5 100644 +index 2d13d7586..04390be84 100644 --- a/dts/bindings/lora/semtech,sx126x-base.yaml +++ b/dts/bindings/lora/semtech,sx126x-base.yaml @@ -35,6 +35,24 @@ properties: