diff --git a/docs/supported_boards.md b/docs/supported_boards.md index a2a4b72..ee26917 100644 --- a/docs/supported_boards.md +++ b/docs/supported_boards.md @@ -158,6 +158,7 @@ xiao_mg24 ``` me25ls02/nrf54l15/cpuapp seeed_lr2021_evk/nrf54l15/cpuapp +seeed_lr2021_evk_lm20a/nrf54lm20a/cpuapp # source-only, no published firmware xiao_nrf54l15/nrf54l15/cpuapp ``` diff --git a/zephcore/adapters/board/ZephyrBoard.cpp b/zephcore/adapters/board/ZephyrBoard.cpp index af59f9d..851398f 100644 --- a/zephcore/adapters/board/ZephyrBoard.cpp +++ b/zephcore/adapters/board/ZephyrBoard.cpp @@ -175,6 +175,17 @@ static const struct device *const fuel_gauge_dev = #define HAS_FUEL_GAUGE 0 #endif +/* nPM1300 charger (XIAO nRF54LM20A): the PMIC measures VBAT with its own ADC + * and Zephyr exposes that through the sensor API, not the fuel-gauge one. + * Voltage only; state of charge comes from the discharge curve. */ +#if !HAS_FUEL_GAUGE && DT_HAS_COMPAT_STATUS_OKAY(nordic_npm1300_charger) +#define HAS_PMIC_CHARGER 1 +static const struct device *const pmic_charger_dev = + DEVICE_DT_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_npm1300_charger)); +#else +#define HAS_PMIC_CHARGER 0 +#endif + /* Initialize activity LED GPIO at boot */ #if HAS_TX_LED /* Width of the receive blink. A transmit holds the LED for its whole airtime, @@ -255,6 +266,21 @@ uint16_t ZephyrBoard::readBattMilliVolts() LOG_WRN("Fuel gauge voltage read failed: %d", ret); } return 0; +#elif HAS_PMIC_CHARGER + if (device_is_ready(pmic_charger_dev)) { + struct sensor_value v; + int ret = sensor_sample_fetch(pmic_charger_dev); + + if (ret == 0) { + ret = sensor_channel_get(pmic_charger_dev, + SENSOR_CHAN_GAUGE_VOLTAGE, &v); + } + if (ret == 0) { + return (uint16_t)(v.val1 * 1000 + v.val2 / 1000); /* V -> mV */ + } + LOG_WRN("PMIC battery voltage read failed: %d", ret); + } + return 0; #elif DT_NODE_EXISTS(DT_PATH(zephyr_user)) && \ DT_NODE_HAS_PROP(DT_PATH(zephyr_user), io_channels) if (!adc_is_ready_dt(&vbat_adc)) { diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.defconfig b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.defconfig new file mode 100644 index 0000000..fec4f92 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.defconfig @@ -0,0 +1,33 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +if BOARD_SEEED_LR2021_EVK_LM20A_NRF54LM20A_CPUAPP + +config HAS_BT_CTLR + default BT + +# nPM1300 init order, copied from upstream Zephyr's xiao_nrf54lm20a +# Kconfig.defconfig. The charger driver raises the PMIC's VBUS current limit +# from its 100 mA power-up default to 500 mA, and it lives under the sensor +# subsystem (priority 90). The regulators must come up after it, or the LDO1 +# soft-start inrush trips over-current protection and the board brown-outs in a +# boot loop. The 500 mA limit matters to us beyond boot: an LR2021 at +22 dBm +# does not fit in 100 mA on a kit running from USB alone. + +config SENSOR + default y + +config REGULATOR + default y + +if REGULATOR_NPM13XX + +config REGULATOR_NPM13XX_COMMON_INIT_PRIORITY + default 91 + +config REGULATOR_NPM13XX_INIT_PRIORITY + default 92 + +endif # REGULATOR_NPM13XX + +endif # BOARD_SEEED_LR2021_EVK_LM20A_NRF54LM20A_CPUAPP diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.seeed_lr2021_evk_lm20a b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.seeed_lr2021_evk_lm20a new file mode 100644 index 0000000..9da1779 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/Kconfig.seeed_lr2021_evk_lm20a @@ -0,0 +1,6 @@ +# Semtech LR2021 LoRa Plus EVK on a XIAO nRF54LM20A — board configuration +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +config BOARD_SEEED_LR2021_EVK_LM20A + select SOC_NRF54LM20A_CPUAPP if BOARD_SEEED_LR2021_EVK_LM20A_NRF54LM20A_CPUAPP diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.cmake b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.cmake new file mode 100644 index 0000000..07a52d6 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.cmake @@ -0,0 +1,20 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT +# +# Same flashing story as seeed_lr2021_evk: the XIAO's USB-C goes to a SAMD11 +# that presents CMSIS-DAP plus a USB CDC for the uart20 console. The nRF54LM20A +# does have a USB peripheral, but this XIAO leaves D+/D- unconnected, so there +# is no UF2 and no DFU. +# +# The openocd config is upstream Zephyr's for the XIAO nRF54LM20A, used in place +# rather than copied: it sources boards/seeed/common/openocd_nrf54l.cfg by a +# path relative to itself. + +board_runner_args(openocd "--config=${ZEPHYR_BASE}/boards/seeed/xiao_nrf54lm20a/support/openocd.cfg" + "--cmd-load=nrf54l_load" "--cmd-erase=nrf54l_mass_erase" + "--cmd-verify=verify_image" -c "targets nrf54lm20a.cpu") +board_runner_args(jlink "--device=nRF54LM20A_M33" "--speed=4000") + +include(${ZEPHYR_BASE}/boards/common/openocd.board.cmake) +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.conf b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.conf new file mode 100644 index 0000000..b52d742 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.conf @@ -0,0 +1,22 @@ +# Semtech LR2021 LoRa Plus EVK (XIAO nRF54LM20A + Wio-LR2021) +# Board-specific configuration + +# Board identification +CONFIG_ZEPHCORE_BOARD_NAME="LR2021 LoRa Plus EVK LM20A" + +# Device Information Service model name +CONFIG_BT_DIS_MODEL_NUMBER_STR="Seeed LR2021 LoRa Plus EVK LM20A" + +# LR2021 radio driver +CONFIG_ZEPHCORE_RADIO_LR2021=y + +# 511 KB of RAM leaves room well past the defaults (350 / 256). 510 contacts is +# the most the companion protocol can report to the app (one byte, doubled); +# each contact costs 188 B of RAM and each queue slot 173 B. +CONFIG_ZEPHCORE_MAX_CONTACTS=510 +CONFIG_ZEPHCORE_OFFLINE_QUEUE_SIZE=512 + +# /ext on the XIAO's SPI NOR: LittleFS takes its block size from the flash page +# layout, and the SPI NOR driver's default page is the 64 KB block, not the +# 4 KB sector (the QSPI driver's equivalent is set in nrf52_common.conf). +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.yml b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.yml new file mode 100644 index 0000000..7ca4faf --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/board.yml @@ -0,0 +1,9 @@ +# Copyright (c) 2026 ZephCore +# SPDX-License-Identifier: MIT + +board: + name: seeed_lr2021_evk_lm20a + full_name: Semtech LR2021 LoRa Plus EVK (XIAO nRF54LM20A + Wio-LR2021) + vendor: seeed + socs: + - name: nrf54lm20a diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/bringup.conf b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/bringup.conf new file mode 100644 index 0000000..5a73625 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/bringup.conf @@ -0,0 +1,19 @@ +# LR2021 LoRa Plus EVK (XIAO nRF54LM20A) bring-up logging. +# +# west build -b seeed_lr2021_evk_lm20a/nrf54lm20a/cpuapp zephcore --pristine --no-sysbuild -- \ +# -DEXTRA_CONF_FILE="boards/common/debug.conf;boards/nrf54l/seeed_lr2021_evk_lm20a/bringup.conf" +# +# Same set, for the same reasons, as boards/nrf54l/seeed_lr2021_evk/bringup.conf. +# Not for daily use. + +CONFIG_LORA_LOG_LEVEL_DBG=y + +CONFIG_I2C_LOG_LEVEL_INF=y +CONFIG_DISPLAY_LOG_LEVEL_DBG=y + +# One line per real input event: K1 reaches the XIAO through a pogo pin, and +# whether it lands on this XIAO's D14 pad is a bench question. +CONFIG_INPUT_LOG_LEVEL_INF=y +CONFIG_INPUT_EVENT_DUMP=y + +CONFIG_FS_LOG_LEVEL_DBG=y diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/pre_dt_board.cmake b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/pre_dt_board.cmake new file mode 100644 index 0000000..3789630 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/pre_dt_board.cmake @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: MIT +# Suppress "unique_unit_address_if_enabled" to handle some overlaps +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a-pinctrl.dtsi b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a-pinctrl.dtsi new file mode 100644 index 0000000..135ef78 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a-pinctrl.dtsi @@ -0,0 +1,86 @@ +/* + * Semtech LR2021 LoRa Plus EVK on a XIAO nRF54LM20A — pin control + * Copyright (c) 2026 ZephCore + * SPDX-License-Identifier: MIT + * + * Pin assignments are the XIAO nRF54LM20A's fixed connector mapping, copied + * from Zephyr's xiao_nrf54lm20a_nrf54lm20a-pinctrl.dtsi. Only the three buses + * this kit uses are declared: + * + * uart20 P1.11 TX / P1.10 RX — console, via the SAMD11 CMSIS-DAP bridge + * i2c22 P1.03 SDA / P1.07 SCL — D4/D5: OLED + the two I2C Grove ports + * spi23 P1.04 SCK / P1.06 MOSI / P1.05 MISO — D8/D10/D9: LR2021 + * spi00 P2.01 SCK / P2.02 MOSI / P2.04 MISO — the XIAO's 8 MB flash (/ext) + * + * uart21 (D6/D7) is deliberately absent — see the DIO7/DIO11 note in the .dts. + */ + +&pinctrl { + /omit-if-no-ref/ uart20_default: uart20_default { + group1 { + psels = ; + }; + + group2 { + psels = ; + bias-pull-up; + }; + }; + + /omit-if-no-ref/ uart20_sleep: uart20_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /omit-if-no-ref/ i2c22_default: i2c22_default { + group1 { + psels = , + ; + }; + }; + + /omit-if-no-ref/ i2c22_sleep: i2c22_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /omit-if-no-ref/ spi00_default: spi00_default { + group1 { + psels = , + , + ; + }; + }; + + /omit-if-no-ref/ spi00_sleep: spi00_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + /omit-if-no-ref/ spi23_default: spi23_default { + group1 { + psels = , + , + ; + }; + }; + + /omit-if-no-ref/ spi23_sleep: spi23_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; +}; diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp.dts b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp.dts new file mode 100644 index 0000000..bbfdce8 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp.dts @@ -0,0 +1,430 @@ +/* + * Semtech LR2021 LoRa Plus Evaluation Kit with a XIAO nRF54LM20A fitted + * Copyright (c) 2026 ZephCore + * SPDX-License-Identifier: MIT + * + * The same three-board sandwich as boards/nrf54l/seeed_lr2021_evk, with the + * MCU module swapped: + * + * Seeed XIAO nRF54LM20A — the MCU (2036 KB RRAM, 511 KB RAM, nPM1300) + * LoRa Plus Expansion Board V2a — OLED, buttons, Grove, SMA, SWD + * Wio-LR2021 module — LR2021WIO1XDCS, 868/915 MHz, switchless + * + * Everything about the carrier and the radio module is unchanged and is + * explained in seeed_lr2021_evk_nrf54l15_cpuapp.dts; this file only records + * what the other XIAO changes. Sources, in devdocs/seeed_lr2021_evk/assets/lm20a: + * the XIAO nRF54LM20A schematic and Zephyr's own xiao_nrf54lm20a board. + * + * XIAO connector map (Zephyr seeed_xiao_connector.dtsi, schematic sheet 6): + * D0 P1.00 LR2021 IRQ (chip DIO8) D8 P1.04 SPI SCK (spi23) + * D1 P1.31 LR2021 BUSY D9 P1.05 SPI MISO (spi23) + * D2 P1.30 LR2021 NRESET D10 P1.06 SPI MOSI (spi23) + * D3 P1.29 LR2021 NSS + * D4 P1.03 I2C SDA (i2c22) — OLED + 2x Grove + * D5 P1.07 I2C SCL (i2c22) — OLED + 2x Grove + * D6 P1.08 LR2021 DIO7 / uart21 TX / Grove UART + * D7 P1.09 LR2021 DIO11 / uart21 RX / Grove UART + * D16 P3.05 K1 user button on the expansion board (back pad TP16; the + * expansion board calls this pogo "D14", which is the L15's map) + * + * What differs from the nRF54L15 XIAO, beyond the pin numbers: + * - The radio is on spi23. spi00 belongs to the XIAO's own 8 MB flash on P2, + * which is mounted as /ext. + * - Every pin used here sits on a port with GPIOTE (P0, P1, P3), so the + * buttons are interrupt-driven and the L15's polling workaround is gone. + * - An nPM1300 PMIC makes the 3.3 V rail and charges the battery. + * - No 2.4 GHz antenna switch: ANT goes through a fixed match to the + * ceramic antenna (sheet 6). Seeed's pin sheet lists RF_SW_PWR/RF_SW_CTL + * on P1.27/P1.28, but no such net exists in the schematic. + * - The SoC has USB, the board does not use it: D+/D- are no-connects and + * the VBUS link R23 is not fitted. Flashing and console stay on the SAMD11. + */ + +/dts-v1/; +#include +#include "seeed_lr2021_evk_lm20a-pinctrl.dtsi" +#include +#include + +/ { + model = "Semtech LR2021 LoRa Plus EVK (XIAO nRF54LM20A)"; + compatible = "seeed,lr2021-evk-lm20a"; + + chosen { + zephyr,sram = &cpuapp_sram_app; + zephyr,flash = &cpuapp_rram; + zephyr,flash-controller = &rram_controller; + zephyr,console = &uart20; + zephyr,shell-uart = &uart20; + zephyr,bt-hci = &bt_hci_controller; + zephyr,display = &ssd1306; + /* No zephyr,code-partition: no bootloader, the image links at + * RRAM base 0x0. Same choice as seeed_lr2021_evk. */ + }; + + aliases { + lora0 = &lora; + led0 = &blue_led; + sw0 = &user_button; + }; + + /* RGB LED on the XIAO core board. */ + leds { + compatible = "gpio-leds"; + + blue_led: led_0 { + gpios = <&gpio1 23 GPIO_ACTIVE_LOW>; + label = "Blue LED"; + }; + + red_led: led_1 { + gpios = <&gpio1 22 GPIO_ACTIVE_LOW>; + label = "Red LED"; + }; + + green_led: led_2 { + gpios = <&gpio1 24 GPIO_ACTIVE_LOW>; + label = "Green LED"; + }; + }; + + buttons: buttons { + compatible = "gpio-keys"; + + /* + * K1 on the expansion board: a bare switch to GND, so the + * internal pull-up is required. + * + * NOT this XIAO's D14. K1 reaches the MCU through a pogo pin + * placed for the nRF54L15 XIAO's D14 back pad, and the two XIAOs + * put different nets on the same eight pad positions (both KiCad + * board files, devdocs/seeed_lr2021_evk/assets). The pad that + * pogo touches here is TP16 = D16 = P3.05; D14 (P3.03, TP14) sits + * in the other pad row. P3 has a GPIOTE instance (gpiote20), so + * no polling as on the L15's P2.09. + * + * Derived from the board files, NOT confirmed on hardware: on the + * one kit tried (2026-10-02) holding K1 pulled none of P3.00..07 + * low with pull-ups on all eight, i.e. the pogo pins made no + * contact at all. The XIAO's own button below is the one that works. + */ + user_button: button_0 { + gpios = <&gpio3 5 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "K1 (expansion board)"; + }; + + /* The XIAO core board's own USR button. Same key code as K1. */ + xiao_button: button_1 { + gpios = <&gpio0 9 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>; + zephyr,code = ; + label = "USR (XIAO core board)"; + }; + }; + + /* Button chain — same mapping as RAK4631 (rak4631/board.overlay). */ + user_btn_longpress { + compatible = "zephyr,input-longpress"; + input = <&buttons>; + input-codes = ; + short-codes = ; + long-codes = ; + long-delay-ms = <1000>; + }; + + page_btn_multitap { + compatible = "zephcore,input-multi-tap"; + input-codes = ; + tap-codes = ; + tap-delay-ms = <400>; + }; + + /* + * nPM1300 on a bit-banged bus (P1.17 SCL / P1.18 SDA). Copied from + * upstream Zephyr's xiao_nrf54lm20a; the init order it depends on is in + * Kconfig.defconfig. + */ + pmic_i2c: pmic-i2c { + compatible = "gpio-i2c"; + #address-cells = <1>; + #size-cells = <0>; + sda-gpios = <&gpio1 18 GPIO_ACTIVE_HIGH>; + scl-gpios = <&gpio1 17 GPIO_ACTIVE_HIGH>; + clock-frequency = <100000>; + status = "okay"; + + pmic: npm1300@6b { + compatible = "nordic,npm1300"; + reg = <0x6b>; + + pmic_charger: charger { + compatible = "nordic,npm1300-charger"; + term-microvolt = <4200000>; + term-warm-microvolt = <4000000>; + current-microamp = <150000>; + dischg-limit-microamp = <1000000>; + vbus-limit-microamp = <500000>; + thermistor-ohms = <10000>; + thermistor-beta = <3380>; + charging-enable; + }; + + regulators { + compatible = "nordic,npm1300-regulator"; + + /* + * BUCK2 is VSYS_3V3: the nRF54, the SAMD11 debugger + * and, through the expansion board, the LR2021. + */ + vsys_3v3: BUCK2 { + regulator-always-on; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + /* IMU and microphone supply. */ + imu_vdd: LDO1 { + regulator-init-microvolt = <3300000>; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-initial-mode = ; + regulator-allowed-modes = ; + regulator-boot-on; + }; + }; + }; + }; +}; + +&cpuapp_sram { + status = "okay"; +}; + +&hfpll { + clock-frequency = ; +}; + +&grtc { + owned-channels = <0 1 2 3 4 5 6 7 8 9 10 11>; + /* Channels 7-11 reserved for Zero Latency IRQs, 3-4 for FLPR */ + child-owned-channels = <3 4 7 8 9 10 11>; + status = "okay"; +}; + +/* Clock set as upstream's xiao_nrf54lm20a. */ +&clock { + status = "okay"; +}; + +&xo { + status = "okay"; +}; + +&lfclk { + status = "okay"; +}; + +&xo24m { + status = "okay"; +}; + +/* Console: P1.11 TX / P1.10 RX -> SAMD11 CMSIS-DAP bridge -> USB CDC */ +&uart20 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart20_default>; + pinctrl-1 = <&uart20_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* D6/D7 are also the LR2021's DIO7/DIO11 — see seeed_lr2021_evk. */ +&uart21 { + status = "disabled"; +}; + +&i2c22 { + status = "okay"; + pinctrl-0 = <&i2c22_default>; + pinctrl-1 = <&i2c22_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = <400000>; + + /* Mandatory for the SSD1306 on nrf-twim: 1 + 1024, rounded up. */ + zephyr,concat-buf-size = <1088>; + + /* ZJY-2864KSWPG01 on the expansion board; settings as seeed_lr2021_evk. */ + ssd1306: ssd1306@3c { + compatible = "solomon,ssd1306"; + reg = <0x3c>; + width = <128>; + height = <64>; + segment-offset = <0>; + page-offset = <0>; + display-offset = <0>; + multiplex-ratio = <63>; + segment-remap; + com-invdir; + inversion-on; + prechargep = <0x22>; + }; +}; + +&spi23 { + status = "okay"; + cs-gpios = <&gpio1 29 GPIO_ACTIVE_LOW>; /* D3 = P1.29 NSS */ + pinctrl-0 = <&spi23_default>; + pinctrl-1 = <&spi23_sleep>; + pinctrl-names = "default", "sleep"; + + /* The bus must idle at the LR2021's NOP, not Nordic's 0xFF. */ + overrun-character = <0x00>; + + lora: lora@0 { + compatible = "semtech,lr2021"; + reg = <0>; + spi-max-frequency = <8000000>; + + reset-gpios = <&gpio1 30 GPIO_ACTIVE_LOW>; /* D2 = P1.30 */ + busy-gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>; /* D1 = P1.31 */ + irq-gpios = <&gpio1 0 (GPIO_PULL_DOWN | GPIO_ACTIVE_HIGH)>; /* D0 = P1.00 */ + irq-dio = <8>; /* chip DIO8 -> module pin 1 -> D0 */ + + /* No TCXO and no RF switch: both are properties of the Wio-LR2021 + * module, settled on hardware — see seeed_lr2021_evk. */ + rx-boosted; + }; +}; + +/* + * The XIAO's own 8 MB SPI NOR (PY25Q64HA) on P2, mounted as /ext by + * qspi-ext.dtsi below: contacts, channels and blobs move there, and contact + * saves become atomic. Flash parameters are upstream Zephyr's xiao_nrf54lm20a + * node, except the clock: upstream gives the part's 133 MHz ceiling, this + * stays at 8 MHz until a faster rate is measured to be worth having. + * + * The label is "qspi_flash" because that is the name qspi-ext.dtsi and + * ZephyrFsFormat.c look for; the part is driven as plain single-line SPI. + */ +&spi00 { + status = "okay"; + cs-gpios = <&gpio2 5 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&spi00_default>; + pinctrl-1 = <&spi00_sleep>; + pinctrl-names = "default", "sleep"; + + qspi_flash: py25q64ha@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <8000000>; + jedec-id = [85 20 17]; + sfdp-bfp = [e5 20 f9 ff ff ff ff 03 44 eb 08 6b 08 3b 80 bb + fe ff ff ff ff ff 00 ff ff ff 44 eb 0c 20 0f 52 + 10 d8 00 81]; + size = <67108864>; + has-dpd; + t-enter-dpd = <3000>; + requires-ulbpr; + hold-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>; + wp-gpios = <&gpio2 3 GPIO_ACTIVE_LOW>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Full 8 MB for LittleFS storage */ + qspi_storage_partition: partition@0 { + label = "qspi_storage"; + reg = <0x00000000 0x00800000>; + }; + }; + }; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&vregmain { + status = "okay"; + regulator-initial-mode = ; +}; + +/* Crystal load capacitances as upstream's xiao_nrf54lm20a. */ +&lfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <17000>; + status = "okay"; +}; + +&hfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <15000>; + status = "okay"; +}; + +&radio { + status = "okay"; +}; + +&bt_hci_controller { + status = "okay"; +}; + +&temp { + status = "okay"; +}; + +/* Default Nordic RRAM map, then re-carved below for ZephCore. */ +#include + +/* + * RRAM layout (2036 KB total, 0x1FD000; the last 96 KB are the FLPR core's). + * + * No bootloader: the application starts at 0x0, so the DFU slots are deleted. + * + * 0x000000 - 0x1C0000 (1792 KB) application (links from 0x0) + * 0x1C0000 - 0x1E0000 (128 KB) LittleFS: identity, prefs, contacts, + * channels, BLE bonds + * 0x1E0000 - 0x1E5000 (20 KB) unused + * 0x1E5000 - 0x1FD000 (96 KB) reserved for FLPR + */ +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; +/delete-node/ &storage_partition; + +&cpuapp_rram { + partitions { + lfs_partition: partition@1c0000 { + compatible = "zephyr,mapped-partition"; + label = "lfs"; + reg = <0x1C0000 0x20000>; + }; + }; +}; + +/* LittleFS auto-mount — standard /lfs mount point */ +#include "../../common/filesystem.dtsi" + +/* /ext on the 8 MB SPI NOR: deferred init, mounted by the store on first use */ +#include "../../common/qspi-ext.dtsi" diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp_defconfig b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp_defconfig new file mode 100644 index 0000000..604dc33 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/seeed_lr2021_evk_lm20a_nrf54lm20a_cpuapp_defconfig @@ -0,0 +1,29 @@ +# Semtech LR2021 LoRa Plus EVK (XIAO nRF54LM20A) default configuration +# SPDX-License-Identifier: MIT + +CONFIG_ARM_MPU=y +CONFIG_HW_STACK_PROTECTION=y +CONFIG_NULL_POINTER_EXCEPTION_DETECTION_NONE=y + +CONFIG_CACHE_MANAGEMENT=y +CONFIG_EXTERNAL_CACHE=y + +# Console on uart20 -> SAMD11 CMSIS-DAP bridge -> USB CDC +# (the SoC's own USB is not routed to the connector on this XIAO) +CONFIG_SERIAL=y +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +CONFIG_GPIO=y +CONFIG_PINCTRL=y +CONFIG_SPI=y +CONFIG_I2C=y + +# RRAM + LittleFS +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +CONFIG_NRF_GRTC_START_SYSCOUNTER=y + +# nPM1300 regulators; init order is in Kconfig.defconfig. +CONFIG_REGULATOR=y diff --git a/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/zephcore.yml b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/zephcore.yml new file mode 100644 index 0000000..5031461 --- /dev/null +++ b/zephcore/boards/nrf54l/seeed_lr2021_evk_lm20a/zephcore.yml @@ -0,0 +1,3 @@ +# ZephCore board manifest — schema in zephcore/scripts/board_manifest.py +# No release: until validated on hardware. +target: seeed_lr2021_evk_lm20a/nrf54lm20a/cpuapp diff --git a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c index c1a7f28..e7d1529 100644 --- a/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c +++ b/zephcore/patches/zephyr-new/drivers/lora/lr20xx/lr20xx_lora.c @@ -1154,7 +1154,10 @@ static bool lr20xx_apply_rx_duty_cycle(struct lr20xx_data *data) * As on the LR11xx (DS §6.3.8 is word for word UM §7.2.6): any NSS edge in * the sleep phase ends the loop, and BUSY is high in sleep and in Rx alike. * So suspend = SetStandby (never an NSS poke: that caused the X1's CMD_PERR - * storm), resume = re-arm. Caller holds spi_mutex. LLD 05 §13. */ + * storm), resume = re-arm. Resume expects the standby suspend left: the chip + * answers SetRxDutyCycle with CMD_FAIL while in Rx (measured; DS §6.3.5 says + * it of SetRx), so a caller that enters Rx in between leaves it again first. + * Caller holds spi_mutex. LLD 05 §13. */ static bool lr20xx_dc_suspend(struct lr20xx_data *data) { if (!data->rx_duty_cycle_enabled || !data->in_rx_mode) { @@ -2440,6 +2443,12 @@ int16_t lr20xx_get_rssi_inst(const struct device *dev) out = rssi; } + if (armed) { + /* Leave the Rx entered above; dc_resume() re-arms from standby. */ + lr20xx_system_set_standby_mode(&data->hal_ctx, + LR20XX_SYSTEM_STANDBY_MODE_RC); + } + lr20xx_dc_resume(data, armed); k_mutex_unlock(&data->spi_mutex); @@ -2529,6 +2538,10 @@ int lr20xx_get_rssi_burst(const struct device *dev, int16_t *out, int n, LR20XX_SYSTEM_IRQ_SYNC_WORD_HEADER_VALID))) { got = -EAGAIN; } + + /* Leave the Rx entered above; dc_resume() re-arms from standby. */ + lr20xx_system_set_standby_mode(&data->hal_ctx, + LR20XX_SYSTEM_STANDBY_MODE_RC); } lr20xx_dc_resume(data, armed);