diff --git a/boards/framework/framework_wireless_dongle/Kconfig.defconfig b/boards/framework/framework_wireless_dongle/Kconfig.defconfig new file mode 100644 index 000000000000..21498230fbce --- /dev/null +++ b/boards/framework/framework_wireless_dongle/Kconfig.defconfig @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_FRAMEWORK_WIRELESS_DONGLE_NRF54LM20A_CPUAPP + +config HW_STACK_PROTECTION + default ARCH_HAS_STACK_PROTECTION + +endif # BOARD_FRAMEWORK_WIRELESS_DONGLE_NRF54LM20A_CPUAPP diff --git a/boards/framework/framework_wireless_dongle/Kconfig.framework_wireless_dongle b/boards/framework/framework_wireless_dongle/Kconfig.framework_wireless_dongle new file mode 100644 index 000000000000..84ad2a7b71bc --- /dev/null +++ b/boards/framework/framework_wireless_dongle/Kconfig.framework_wireless_dongle @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FRAMEWORK_WIRELESS_DONGLE + select SOC_NRF54LM20A_CPUAPP if BOARD_FRAMEWORK_WIRELESS_DONGLE_NRF54LM20A_CPUAPP diff --git a/boards/framework/framework_wireless_dongle/Kconfig.sysbuild b/boards/framework/framework_wireless_dongle/Kconfig.sysbuild new file mode 100644 index 000000000000..bcc6cbe8cd3c --- /dev/null +++ b/boards/framework/framework_wireless_dongle/Kconfig.sysbuild @@ -0,0 +1,17 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Match the factory bootloader: the flash layout has a single application +# slot, so MCUboot can only run in single application mode, and images are +# verified with ED25519 signatures. +if BOOTLOADER_MCUBOOT + +choice MCUBOOT_MODE + default MCUBOOT_MODE_SINGLE_APP +endchoice + +choice BOOT_SIGNATURE_TYPE + default BOOT_SIGNATURE_TYPE_ED25519 +endchoice + +endif # BOOTLOADER_MCUBOOT diff --git a/boards/framework/framework_wireless_dongle/board.cmake b/boards/framework/framework_wireless_dongle/board.cmake new file mode 100644 index 000000000000..c15f7a359e3a --- /dev/null +++ b/boards/framework/framework_wireless_dongle/board.cmake @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_SOC_NRF54LM20A_CPUAPP) + board_runner_args(jlink "--device=nRF54LM20A_M33" "--speed=4000") +endif() + +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/boards/framework/framework_wireless_dongle/board.yml b/boards/framework/framework_wireless_dongle/board.yml new file mode 100644 index 000000000000..aaa8ad5c20bc --- /dev/null +++ b/boards/framework/framework_wireless_dongle/board.yml @@ -0,0 +1,34 @@ +board: + name: framework_wireless_dongle + full_name: Framework Wireless Dongle + vendor: framework + socs: + - name: nrf54lm20a +runners: + run_once: + '--recover': + - runners: + - nrfjprog + - nrfutil + run: first + groups: + - boards: + - framework_wireless_dongle/nrf54lm20a/cpuapp + '--erase': + - runners: + - nrfjprog + - jlink + - nrfutil + run: first + groups: + - boards: + - framework_wireless_dongle/nrf54lm20a/cpuapp + '--reset': + - runners: + - nrfjprog + - jlink + - nrfutil + run: last + groups: + - boards: + - framework_wireless_dongle/nrf54lm20a/cpuapp diff --git a/boards/framework/framework_wireless_dongle/doc/index.rst b/boards/framework/framework_wireless_dongle/doc/index.rst new file mode 100644 index 000000000000..3a917dadaf86 --- /dev/null +++ b/boards/framework/framework_wireless_dongle/doc/index.rst @@ -0,0 +1,123 @@ +.. zephyr:board:: framework_wireless_dongle + +Overview +******** + +The Framework Wireless Dongle is the USB-A receiver that ships with the +:zephyr:board:`framework_wireless_tp_kb`. It connects to Bluetooth Low Energy input devices and +presents them to the host as a USB HID device, without needing Bluetooth on the host. It is built +around the Nordic nRF54LM20A SoC and has no user interface of its own: no buttons and no LEDs. + +The dongle ships with an MCUboot bootloader that supports serial recovery over USB. + +Hardware +******** + +* nRF54LM20A SoC: 128 MHz Arm Cortex-M33, RISC-V FLPR coprocessor, 2 MB RRAM, 512 kB RAM, + multiprotocol 2.4 GHz radio (Bluetooth LE, IEEE 802.15.4, proprietary) +* USB high-speed device on the USB-A plug, which also powers the dongle +* 32 MHz crystal, no 32.768 kHz crystal (the low frequency clock runs from the internal RC + oscillator) +* Debug UART (UARTE20) on test pads + +Supported Features +================== + +.. zephyr:board-supported-hw:: + +Connections and IOs +=================== + ++-------+-------------+----------------------+ +| Pin | Function | Usage | ++=======+=============+======================+ +| P2.02 | UARTE20 TX | Debug UART, test pad | ++-------+-------------+----------------------+ +| P2.00 | UARTE20 RX | Debug UART, test pad | ++-------+-------------+----------------------+ + +Flash Layout +============ + +The board's flash layout matches the factory bootloader: + ++-------------------+----------+----------+---------------------------------+ +| Partition | Offset | Size | Usage | ++===================+==========+==========+=================================+ +| ``mcuboot`` | 0x000000 | 96 kB | MCUboot with USB serial recovery| ++-------------------+----------+----------+---------------------------------+ +| ``image-0`` | 0x018000 | 1828 kB | Application | ++-------------------+----------+----------+---------------------------------+ +| ``storage`` | 0x1e1000 | 16 kB | Settings storage | ++-------------------+----------+----------+---------------------------------+ + +There is only one application slot, so when building MCUboot with sysbuild the board defaults to +``SB_CONFIG_MCUBOOT_MODE_SINGLE_APP``. The factory bootloader verifies ED25519 signatures, so the +board also defaults to ``SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519``. Sysbuild then signs images with +MCUboot's ``root-ed25519.pem``; set ``SB_CONFIG_BOOT_SIGNATURE_KEY_FILE`` to use a different key. + +Programming and Debugging +************************* + +.. zephyr:board-supported-runners:: + +Flashing with a debugger +======================== + +SWD is available on test pads. Flashing with a J-Link or an nRF Util compatible probe uses the +standard flow, for example for the :zephyr:code-sample:`usb-cdc-acm` sample: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/usb/cdc_acm + :board: framework_wireless_dongle/nrf54lm20a/cpuapp + :goals: build flash + +Flashing a plain application this way overwrites the factory bootloader. To keep it, build the +application for the ``image-0`` slot with sysbuild and MCUboot enabled, and flash only the +application domain. A plain ``west flash`` of a sysbuild build also writes the MCUboot image built +by sysbuild over the factory bootloader. + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/usb/cdc_acm + :board: framework_wireless_dongle/nrf54lm20a/cpuapp + :goals: build flash + :west-args: --sysbuild + :gen-args: -DSB_CONFIG_BOOTLOADER_MCUBOOT=y + :flash-args: --domain cdc_acm + +Flashing over USB +================= + +The factory MCUboot bootloader exposes a USB CDC ACM serial port for :ref:`mcumgr ` +serial recovery. As the dongle has no button, recovery mode is requested from the running +application through the :ref:`retention boot mode ` (for example with the mcumgr +``os reset`` command with the boot mode argument, if the application enables it). Build the +application with MCUboot support so that the image is signed and linked for the application slot: + +.. zephyr-app-commands:: + :zephyr-app: samples/subsys/usb/cdc_acm + :board: framework_wireless_dongle/nrf54lm20a/cpuapp + :goals: build + :west-args: --sysbuild + :gen-args: -DSB_CONFIG_BOOTLOADER_MCUBOOT=y + +The image must be signed with a key the installed bootloader trusts. Upload the signed +application image with mcumgr and reset the device: + +.. code-block:: console + + mcumgr --conntype serial --connstring dev=/dev/ttyACM0 image upload build/cdc_acm/zephyr/zephyr.signed.bin + mcumgr --conntype serial --connstring dev=/dev/ttyACM0 reset + +Debugging +========= + +The debug UART (115200 baud) on the test pads is the default console. The SoC can be debugged +over SWD with J-Link or nRF Util using ``west debug``. + +References +********** + +* `Framework Computer`_ + +.. _Framework Computer: https://frame.work diff --git a/boards/framework/framework_wireless_dongle/framework_wireless_dongle-pinctrl.dtsi b/boards/framework/framework_wireless_dongle/framework_wireless_dongle-pinctrl.dtsi new file mode 100644 index 000000000000..6335ef471f8b --- /dev/null +++ b/boards/framework/framework_wireless_dongle/framework_wireless_dongle-pinctrl.dtsi @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2026 Framework Computer Inc + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart20_default: uart20_default { + group1 { + psels = ; + }; + + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart20_sleep: uart20_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; diff --git a/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.dts b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.dts new file mode 100644 index 000000000000..c59f32fa1933 --- /dev/null +++ b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.dts @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2026 Framework Computer Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include "framework_wireless_dongle-pinctrl.dtsi" + +/ { + compatible = "framework,wireless_dongle_nrf54lm20a-cpuapp"; + model = "Framework Wireless Dongle"; + + chosen { + zephyr,console = &uart20; + zephyr,shell-uart = &uart20; + zephyr,uart-mcumgr = &uart20; + zephyr,bt-mon-uart = &uart20; + zephyr,bt-c2h-uart = &uart20; + zephyr,flash-controller = &rram_controller; + zephyr,flash = &cpuapp_rram; + zephyr,code-partition = &slot0_partition; + zephyr,sram = &cpuapp_sram_app; + zephyr,ieee802154 = &ieee802154; + }; + + aliases { + watchdog0 = &wdt31; + }; +}; + +&cpuapp_sram { + status = "okay"; +}; + +&hfpll { + clock-frequency = ; +}; + +&hfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <15000>; +}; + +/* No DCDC inductor is fitted, VREGMAIN runs in LDO mode. */ +&vregmain { + status = "okay"; + regulator-initial-mode = ; +}; + +&vregusb { + status = "okay"; +}; + +&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"; +}; + +/* Debug UART on test pads */ +&uart20 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart20_default>; + pinctrl-1 = <&uart20_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&radio { + status = "okay"; +}; + +&ieee802154 { + status = "okay"; +}; + +&temp { + status = "okay"; +}; + +&clock { + status = "okay"; +}; + +&xo { + status = "okay"; +}; + +/* No 32.768 kHz crystal is fitted, the LFCLK runs from the internal RC oscillator. */ +&lfclk { + status = "okay"; + k32src = "rc"; + k32src-accuracy-ppm = <250>; +}; + +&xo24m { + status = "okay"; +}; + +&usbhs_wrapper { + status = "okay"; +}; + +zephyr_udc0: &usbhs { + status = "okay"; +}; + +/* + * Flash layout matching the factory MCUboot bootloader: 96 kB bootloader + * with USB serial recovery, a single application slot and 16 kB of settings + * storage. The last 96 kB of RRAM are reserved for the FLPR core. + */ +&cpuapp_rram { + partitions { + ranges; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "mcuboot"; + reg = <0x0 DT_SIZE_K(96)>; + }; + + slot0_partition: partition@18000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x18000 DT_SIZE_K(1828)>; + }; + + storage_partition: partition@1e1000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x1e1000 DT_SIZE_K(16)>; + }; + }; +}; diff --git a/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.yaml b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.yaml new file mode 100644 index 000000000000..25f1871f109b --- /dev/null +++ b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp.yaml @@ -0,0 +1,19 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +identifier: framework_wireless_dongle/nrf54lm20a/cpuapp +name: Framework Wireless Dongle nRF54LM20A Application +type: mcu +arch: arm +toolchain: + - gnuarmemb + - zephyr +sysbuild: true +ram: 511 +flash: 1828 +supported: + - counter + - gpio + - usbd + - watchdog +vendor: framework diff --git a/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp_defconfig b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp_defconfig new file mode 100644 index 000000000000..60894607e1f2 --- /dev/null +++ b/boards/framework/framework_wireless_dongle/framework_wireless_dongle_nrf54lm20a_cpuapp_defconfig @@ -0,0 +1,26 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable GPIO +CONFIG_GPIO=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# MPU-based null-pointer dereferencing detection cannot +# be applied as the (0x0 - 0x400) is unmapped for this target. +CONFIG_NULL_POINTER_EXCEPTION_DETECTION_NONE=y + +# Enable Cache +CONFIG_CACHE_MANAGEMENT=y +CONFIG_EXTERNAL_CACHE=y + +# Start SYSCOUNTER on driver init +CONFIG_NRF_GRTC_START_SYSCOUNTER=y diff --git a/boards/framework/framework_wireless_dongle/pre_dt_board.cmake b/boards/framework/framework_wireless_dongle/pre_dt_board.cmake new file mode 100644 index 000000000000..061cced81f72 --- /dev/null +++ b/boards/framework/framework_wireless_dongle/pre_dt_board.cmake @@ -0,0 +1,6 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - power@X010e000 & clock@X010e000 & lfclk@X010e000 & xo@X010e000 & xo24m@X010e000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/boards/framework/framework_wireless_tp_kb/Kconfig.defconfig b/boards/framework/framework_wireless_tp_kb/Kconfig.defconfig new file mode 100644 index 000000000000..039f03082b2a --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/Kconfig.defconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +if BOARD_FRAMEWORK_WIRELESS_TP_KB_NRF54LM20A_CPUAPP + +config HW_STACK_PROTECTION + default ARCH_HAS_STACK_PROTECTION + +# The nPM1300 PMIC supplies the SoC (BUCK2) and the keyboard backlight +# (BUCK1), and a fixed regulator switches the touchpad power rail. The +# bootloader does not need them. +if !MCUBOOT + +config REGULATOR + default y + +endif # !MCUBOOT + +endif # BOARD_FRAMEWORK_WIRELESS_TP_KB_NRF54LM20A_CPUAPP diff --git a/boards/framework/framework_wireless_tp_kb/Kconfig.framework_wireless_tp_kb b/boards/framework/framework_wireless_tp_kb/Kconfig.framework_wireless_tp_kb new file mode 100644 index 000000000000..98f94b680a2d --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/Kconfig.framework_wireless_tp_kb @@ -0,0 +1,5 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +config BOARD_FRAMEWORK_WIRELESS_TP_KB + select SOC_NRF54LM20A_CPUAPP if BOARD_FRAMEWORK_WIRELESS_TP_KB_NRF54LM20A_CPUAPP diff --git a/boards/framework/framework_wireless_tp_kb/Kconfig.sysbuild b/boards/framework/framework_wireless_tp_kb/Kconfig.sysbuild new file mode 100644 index 000000000000..bcc6cbe8cd3c --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/Kconfig.sysbuild @@ -0,0 +1,17 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Match the factory bootloader: the flash layout has a single application +# slot, so MCUboot can only run in single application mode, and images are +# verified with ED25519 signatures. +if BOOTLOADER_MCUBOOT + +choice MCUBOOT_MODE + default MCUBOOT_MODE_SINGLE_APP +endchoice + +choice BOOT_SIGNATURE_TYPE + default BOOT_SIGNATURE_TYPE_ED25519 +endchoice + +endif # BOOTLOADER_MCUBOOT diff --git a/boards/framework/framework_wireless_tp_kb/board.cmake b/boards/framework/framework_wireless_tp_kb/board.cmake new file mode 100644 index 000000000000..c15f7a359e3a --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/board.cmake @@ -0,0 +1,9 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +if(CONFIG_SOC_NRF54LM20A_CPUAPP) + board_runner_args(jlink "--device=nRF54LM20A_M33" "--speed=4000") +endif() + +include(${ZEPHYR_BASE}/boards/common/nrfutil.board.cmake) +include(${ZEPHYR_BASE}/boards/common/jlink.board.cmake) diff --git a/boards/framework/framework_wireless_tp_kb/board.yml b/boards/framework/framework_wireless_tp_kb/board.yml new file mode 100644 index 000000000000..1a3a95875cca --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/board.yml @@ -0,0 +1,34 @@ +board: + name: framework_wireless_tp_kb + full_name: Framework Wireless Touchpad Keyboard + vendor: framework + socs: + - name: nrf54lm20a +runners: + run_once: + '--recover': + - runners: + - nrfjprog + - nrfutil + run: first + groups: + - boards: + - framework_wireless_tp_kb/nrf54lm20a/cpuapp + '--erase': + - runners: + - nrfjprog + - jlink + - nrfutil + run: first + groups: + - boards: + - framework_wireless_tp_kb/nrf54lm20a/cpuapp + '--reset': + - runners: + - nrfjprog + - jlink + - nrfutil + run: last + groups: + - boards: + - framework_wireless_tp_kb/nrf54lm20a/cpuapp diff --git a/boards/framework/framework_wireless_tp_kb/doc/index.rst b/boards/framework/framework_wireless_tp_kb/doc/index.rst new file mode 100644 index 000000000000..d7fd9a4d1312 --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/doc/index.rst @@ -0,0 +1,185 @@ +.. zephyr:board:: framework_wireless_tp_kb + +Overview +******** + +The Framework Wireless Touchpad Keyboard (development codename "Daisy") is a battery powered +keyboard with an integrated touchpad. It connects over Bluetooth Low Energy or over USB 2.0. +It is built around the Nordic nRF54LM20A SoC with a Nordic nPM1300 PMIC for battery charging and +power management. + +The keyboard ships with an MCUboot bootloader that supports serial recovery over USB, so +applications can be updated without opening the device. + +The board definition in Zephyr focuses on the board support, to allow users to build their own +projects on top of the standalone PCB. Provide your own definitions for battery charging, touchpad +and keyboard matrix. + +Hardware +******** + +* nRF54LM20A SoC: 128 MHz Arm Cortex-M33, RISC-V FLPR coprocessor, 2 MB RRAM, 512 kB RAM, + multiprotocol 2.4 GHz radio (Bluetooth LE, IEEE 802.15.4, proprietary) +* USB high-speed device (USB-C connector, also used for charging) +* nPM1300 PMIC on I2C (TWIM21) + + * BUCK2 supplies the 3.3 V system rail + * BUCK1 supplies the keyboard backlight + * Charger for the 1200 mAh Li-ion battery with NTC temperature monitoring + +* PTP compatible Touchpad connected over I2C +* 8x18 key matrix, same as Framework Laptop 12 +* PWM Keyboard backlight (Unused on official product) +* Four white status LEDs (PWM capable) for the Bluetooth profile/pairing state +* One RGB status LED (PWM capable) +* Caps Lock LED +* Pairing button +* Wired/Wireless mode slide switch +* Debug pin header with UART, I2C, power rails, and key matrix pins + +Supported Features +================== + +.. zephyr:board-supported-hw:: + +Connections and IOs +=================== + ++---------------+---------------+-------------------------------------------+ +| Pin | Function | Usage | ++===============+===============+===========================================+ +| P2.02 | UARTE00 TX | Debug UART, pin header | ++---------------+---------------+-------------------------------------------+ +| P2.00 | UARTE00 RX | Debug UART, pin header | ++---------------+---------------+-------------------------------------------+ +| P3.03 | TWIM21 SCL | nPM1300 PMIC | ++---------------+---------------+-------------------------------------------+ +| P3.09 | TWIM21 SDA | nPM1300 PMIC | ++---------------+---------------+-------------------------------------------+ +| P3.04 | TWIM22 SCL | Touchpad | ++---------------+---------------+-------------------------------------------+ +| P3.08 | TWIM22 SDA | Touchpad | ++---------------+---------------+-------------------------------------------+ +| P3.06 | GPIO | Touchpad interrupt (active low) | ++---------------+---------------+-------------------------------------------+ +| P3.12 | GPIO | Touchpad power enable | ++---------------+---------------+-------------------------------------------+ +| P1.03 | TWIM23 SCL | I2C, pin header | ++---------------+---------------+-------------------------------------------+ +| P1.02 | TWIM23 SDA | I2C, pin header | ++---------------+---------------+-------------------------------------------+ +| P1.22 - P1.25 | GPIO / PWM20 | White status LEDs 1-4 (active low) | ++---------------+---------------+-------------------------------------------+ +| P1.29 | GPIO / PWM21 | Green status LED | ++---------------+---------------+-------------------------------------------+ +| P1.30 | GPIO / PWM21 | Blue status LED | ++---------------+---------------+-------------------------------------------+ +| P1.31 | GPIO / PWM21 | Red status LED | ++---------------+---------------+-------------------------------------------+ +| P3.07 | GPIO | Caps Lock LED | ++---------------+---------------+-------------------------------------------+ +| P3.11 | PWM22 | Keyboard backlight | ++---------------+---------------+-------------------------------------------+ +| P3.10 | GPIO | Pairing button (active low) | ++---------------+---------------+-------------------------------------------+ +| P3.05 | GPIO | USB/Bluetooth mode switch | ++---------------+---------------+-------------------------------------------+ +| P0.00 - P0.07 | GPIO | Key matrix sense lines KSI0-7 | ++---------------+---------------+-------------------------------------------+ +| P1.00, P1.01, | GPIO | Key matrix drive lines KSO0-17 | +| P1.18, P1.19, | | | +| P1.04 - P1.17 | | | ++---------------+---------------+-------------------------------------------+ +| P3.00 | GPIO | Layout strap: ISO | ++---------------+---------------+-------------------------------------------+ +| P3.01 | GPIO | Layout strap: ANSI | ++---------------+---------------+-------------------------------------------+ + +P1.01 and P1.02 are NFC pins on the nRF54LM20A. The board sets ``nfct-pins-as-gpios`` on the NFCT +node, so the startup code configures them as GPIOs. + +Flash Layout +============ + +The board's flash layout matches the factory bootloader: + ++-------------------+----------+----------+---------------------------------+ +| Partition | Offset | Size | Usage | ++===================+==========+==========+=================================+ +| ``mcuboot`` | 0x000000 | 96 kB | MCUboot with USB serial recovery| ++-------------------+----------+----------+---------------------------------+ +| ``image-0`` | 0x018000 | 1828 kB | Application | ++-------------------+----------+----------+---------------------------------+ +| ``storage`` | 0x1e1000 | 16 kB | Settings storage | ++-------------------+----------+----------+---------------------------------+ + +There is only one application slot, so when building MCUboot with sysbuild the board defaults to +``SB_CONFIG_MCUBOOT_MODE_SINGLE_APP``. The factory bootloader verifies ED25519 signatures, so the +board also defaults to ``SB_CONFIG_BOOT_SIGNATURE_TYPE_ED25519``. Sysbuild then signs images with +MCUboot's ``root-ed25519.pem``; set ``SB_CONFIG_BOOT_SIGNATURE_KEY_FILE`` to use a different key. + +Programming and Debugging +************************* + +.. zephyr:board-supported-runners:: + +Flashing with a debugger +======================== + +The SWD interface is available on the internal pin header together with the debug UART. +Flashing with a J-Link or an nRF Util compatible probe uses the standard flow, for example for +the :zephyr:code-sample:`blinky` sample: + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: framework_wireless_tp_kb/nrf54lm20a/cpuapp + :goals: build flash + +Flashing a plain application this way overwrites the factory bootloader. To keep it, build the +application for the ``image-0`` slot with sysbuild and MCUboot enabled, and flash only the +application domain. A plain ``west flash`` of a sysbuild build also writes the MCUboot image built +by sysbuild over the factory bootloader. + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: framework_wireless_tp_kb/nrf54lm20a/cpuapp + :goals: build flash + :west-args: --sysbuild + :gen-args: -DSB_CONFIG_BOOTLOADER_MCUBOOT=y + :flash-args: --domain blinky + +Flashing over USB +================= + +The factory MCUboot bootloader exposes a USB CDC ACM serial port for :ref:`mcumgr ` +serial recovery. Hold the pairing button while connecting the keyboard to USB to start the +bootloader in recovery mode. Build the application with MCUboot support so that the image is +signed and linked for the application slot: + +.. zephyr-app-commands:: + :zephyr-app: samples/basic/blinky + :board: framework_wireless_tp_kb/nrf54lm20a/cpuapp + :goals: build + :west-args: --sysbuild + :gen-args: -DSB_CONFIG_BOOTLOADER_MCUBOOT=y + +The image must be signed with a key the installed bootloader trusts. Upload the signed +application image with mcumgr and reset the device: + +.. code-block:: console + + mcumgr --conntype serial --connstring dev=/dev/ttyACM0 image upload build/blinky/zephyr/zephyr.signed.bin + mcumgr --conntype serial --connstring dev=/dev/ttyACM0 reset + +Debugging +========= + +The debug UART (115200 baud) on the pin header is the default console. The SoC can be debugged +over SWD with J-Link or nRF Util using ``west debug``. + +References +********** + +* `Framework Computer`_ + +.. _Framework Computer: https://frame.work diff --git a/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb-pinctrl.dtsi b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb-pinctrl.dtsi new file mode 100644 index 000000000000..92a6fae52302 --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb-pinctrl.dtsi @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2026 Framework Computer Inc + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + uart00_default: uart00_default { + group1 { + psels = ; + }; + + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart00_sleep: uart00_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* nPM1300 PMIC, external pull-ups */ + i2c21_default: i2c21_default { + group1 { + psels = , + ; + }; + }; + + i2c21_sleep: i2c21_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* Touchpad connector, external pull-ups */ + i2c22_default: i2c22_default { + group1 { + psels = , + ; + }; + }; + + i2c22_sleep: i2c22_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* Pin header */ + i2c23_default: i2c23_default { + group1 { + psels = , + ; + }; + }; + + i2c23_sleep: i2c23_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + /* White pairing LEDs, active low */ + pwm20_default: pwm20_default { + group1 { + psels = , + , + , + ; + nordic,invert; + }; + }; + + pwm20_sleep: pwm20_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; + + /* RGB status LED */ + pwm21_default: pwm21_default { + group1 { + psels = , + , + ; + }; + }; + + pwm21_sleep: pwm21_sleep { + group1 { + psels = , + , + ; + low-power-enable; + }; + }; + + /* Keyboard backlight */ + pwm22_default: pwm22_default { + group1 { + psels = ; + }; + }; + + pwm22_sleep: pwm22_sleep { + group1 { + psels = ; + low-power-enable; + }; + }; +}; diff --git a/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.dts b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.dts new file mode 100644 index 000000000000..21ad4c9645af --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.dts @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2026 Framework Computer Inc + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/dts-v1/; + +#include +#include +#include "framework_wireless_tp_kb-pinctrl.dtsi" + +/ { + compatible = "framework,wireless_tp_kb_nrf54lm20a-cpuapp"; + model = "Framework Wireless Touchpad Keyboard"; + + chosen { + zephyr,console = &uart00; + zephyr,shell-uart = &uart00; + zephyr,uart-mcumgr = &uart00; + zephyr,bt-mon-uart = &uart00; + zephyr,bt-c2h-uart = &uart00; + zephyr,flash-controller = &rram_controller; + zephyr,flash = &cpuapp_rram; + zephyr,code-partition = &slot0_partition; + zephyr,sram = &cpuapp_sram_app; + zephyr,ieee802154 = &ieee802154; + }; + + leds { + compatible = "gpio-leds"; + + /* + * The pull-ups keep the active-low white LEDs off while the pins + * are not driven, for example while &pwm20 owns them. + */ + led_w1: led-w1 { + gpios = <&gpio1 22 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + label = "White LED 1"; + }; + + led_w2: led-w2 { + gpios = <&gpio1 23 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + label = "White LED 2"; + }; + + led_w3: led-w3 { + gpios = <&gpio1 24 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + label = "White LED 3"; + }; + + led_w4: led-w4 { + gpios = <&gpio1 25 (GPIO_ACTIVE_LOW | GPIO_PULL_UP)>; + label = "White LED 4"; + }; + + led_g: led-g { + gpios = <&gpio1 29 GPIO_ACTIVE_HIGH>; + label = "Green LED"; + }; + + led_b: led-b { + gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>; + label = "Blue LED"; + }; + + led_r: led-r { + gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>; + label = "Red LED"; + }; + + caps_led: caps-led { + gpios = <&gpio3 7 GPIO_ACTIVE_HIGH>; + label = "Caps Lock LED"; + }; + }; + + /* + * The four white LEDs indicate the Bluetooth pairing/profile state. + * They share pins with &led_w1 .. &led_w4. + */ + pairing_leds: pairing-leds { + compatible = "pwm-leds"; + + pwm_led_w1: pwm-led-w1 { + pwms = <&pwm20 0 PWM_HZ(4096) PWM_POLARITY_INVERTED>; + }; + + pwm_led_w2: pwm-led-w2 { + pwms = <&pwm20 1 PWM_HZ(4096) PWM_POLARITY_INVERTED>; + }; + + pwm_led_w3: pwm-led-w3 { + pwms = <&pwm20 2 PWM_HZ(4096) PWM_POLARITY_INVERTED>; + }; + + pwm_led_w4: pwm-led-w4 { + pwms = <&pwm20 3 PWM_HZ(4096) PWM_POLARITY_INVERTED>; + }; + }; + + /* RGB status LED, shares pins with &led_r / &led_g / &led_b. */ + rgb_pwmleds: rgb-pwmleds { + compatible = "pwm-leds"; + + pwm_led_g: pwm-led-g { + pwms = <&pwm21 0 PWM_HZ(4096) PWM_POLARITY_NORMAL>; + }; + + pwm_led_b: pwm-led-b { + pwms = <&pwm21 1 PWM_HZ(4096) PWM_POLARITY_NORMAL>; + }; + + pwm_led_r: pwm-led-r { + pwms = <&pwm21 2 PWM_HZ(4096) PWM_POLARITY_NORMAL>; + }; + }; + + /* Keyboard backlight, powered from the PMIC's BUCK1 (&npm1300_buck1). */ + backlight_pwm: backlight-pwm { + compatible = "pwm-leds"; + + backlight_led: backlight-led { + pwms = <&pwm22 0 PWM_HZ(4096) PWM_POLARITY_NORMAL>; + }; + }; + + buttons { + compatible = "gpio-keys"; + + /* External pull-up */ + pairing_button: pairing-button { + gpios = <&gpio3 10 GPIO_ACTIVE_LOW>; + label = "Pairing button"; + zephyr,code = ; + }; + + /* Slide switch selecting USB (wired) or Bluetooth operation */ + protocol_switch: protocol-switch { + gpios = <&gpio3 5 GPIO_ACTIVE_HIGH>; + label = "USB/Bluetooth mode switch"; + zephyr,code = ; + }; + }; + + /* + * Touchpad power rail. Enabled at boot so that a touchpad driver on + * &i2c22 finds the device during its initialization. + */ + tp_power: tp-power { + compatible = "regulator-fixed"; + regulator-name = "tp-power"; + enable-gpios = <&gpio3 12 GPIO_ACTIVE_HIGH>; + regulator-boot-on; + }; + + aliases { + led0 = &led_r; + led1 = &led_g; + led2 = &led_b; + pwm-led0 = &pwm_led_w1; + sw0 = &pairing_button; + watchdog0 = &wdt31; + mcuboot-button0 = &pairing_button; + mcuboot-led0 = &led_g; + }; +}; + +&cpuapp_sram { + status = "okay"; +}; + +&hfpll { + clock-frequency = ; +}; + +&lfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <17000>; +}; + +&hfxo { + load-capacitors = "internal"; + load-capacitance-femtofarad = <15000>; +}; + +&vregmain { + status = "okay"; + regulator-initial-mode = ; +}; + +&vregusb { + status = "okay"; +}; + +&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"; +}; + +/* Debug UART on the pin header */ +&uart00 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart00_default>; + pinctrl-1 = <&uart00_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* + * nPM1300 PMIC without charger definition. + * Add npm1300_charger based on the connected battery. + */ +&i2c21 { + status = "okay"; + pinctrl-0 = <&i2c21_default>; + pinctrl-1 = <&i2c21_sleep>; + pinctrl-names = "default", "sleep"; + + npm1300: pmic@6b { + compatible = "nordic,npm1300"; + reg = <0x6b>; + + npm1300_regulators: regulators { + compatible = "nordic,npm1300-regulator"; + + /* + * Keyboard backlight rail. The PMIC enables it at power-on + * (VSET1 resistor); it is switched off at boot to save the + * buck's quiescent current until an application turns the + * backlight on. + */ + npm1300_buck1: BUCK1 { + regulator-min-microvolt = <3300000>; + regulator-init-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-off; + }; + + /* 3.3 V rail supplying the nRF54LM20A */ + npm1300_buck2: BUCK2 { + regulator-min-microvolt = <3300000>; + regulator-init-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + }; + }; + }; +}; + +/* + * Touchpad connector, power rail controlled by &tp_power. + */ +&i2c22 { + status = "okay"; + pinctrl-0 = <&i2c22_default>; + pinctrl-1 = <&i2c22_sleep>; + pinctrl-names = "default", "sleep"; + clock-frequency = ; +}; + +/* I2C on the pin header */ +&i2c23 { + status = "disabled"; + pinctrl-0 = <&i2c23_default>; + pinctrl-1 = <&i2c23_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* White Pairing LEDs */ +&pwm20 { + status = "okay"; + pinctrl-0 = <&pwm20_default>; + pinctrl-1 = <&pwm20_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* RGB status LED */ +&pwm21 { + status = "okay"; + pinctrl-0 = <&pwm21_default>; + pinctrl-1 = <&pwm21_sleep>; + pinctrl-names = "default", "sleep"; +}; + +/* Backlight PWM */ +&pwm22 { + status = "okay"; + pinctrl-0 = <&pwm22_default>; + pinctrl-1 = <&pwm22_sleep>; + pinctrl-names = "default", "sleep"; +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio2 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&gpiote20 { + status = "okay"; +}; + +&gpiote30 { + status = "okay"; +}; + +&radio { + status = "okay"; +}; + +&ieee802154 { + status = "okay"; +}; + +&temp { + status = "okay"; +}; + +&clock { + status = "okay"; +}; + +&xo { + status = "okay"; +}; + +&lfclk { + status = "okay"; +}; + +&xo24m { + status = "okay"; +}; + +&usbhs_wrapper { + status = "okay"; +}; + +zephyr_udc0: &usbhs { + status = "okay"; +}; + +/* + * P1.01/P1.02 power up as NFC1/NFC2. The startup code reconfigures them as + * plain GPIOs so that the key matrix and the header I2C can use them. + */ +&nfct { + nfct-pins-as-gpios; +}; + +/* + * Flash layout matching the factory MCUboot bootloader: 96 kB bootloader + * with USB serial recovery, a single application slot and 16 kB of settings + * storage. The last 96 kB of RRAM are reserved for the FLPR core. + */ +&cpuapp_rram { + partitions { + ranges; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + compatible = "zephyr,mapped-partition"; + label = "mcuboot"; + reg = <0x0 DT_SIZE_K(96)>; + }; + + slot0_partition: partition@18000 { + compatible = "zephyr,mapped-partition"; + label = "image-0"; + reg = <0x18000 DT_SIZE_K(1828)>; + }; + + storage_partition: partition@1e1000 { + compatible = "zephyr,mapped-partition"; + label = "storage"; + reg = <0x1e1000 DT_SIZE_K(16)>; + }; + }; +}; diff --git a/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.yaml b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.yaml new file mode 100644 index 000000000000..0dc1f4debbc3 --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp.yaml @@ -0,0 +1,22 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +identifier: framework_wireless_tp_kb/nrf54lm20a/cpuapp +name: Framework Wireless Touchpad Keyboard nRF54LM20A Application +type: mcu +arch: arm +toolchain: + - gnuarmemb + - zephyr +sysbuild: true +ram: 511 +flash: 1828 +supported: + - counter + - gpio + - i2c + - pwm + - regulator + - usbd + - watchdog +vendor: framework diff --git a/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp_defconfig b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp_defconfig new file mode 100644 index 000000000000..2bb4091e4730 --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/framework_wireless_tp_kb_nrf54lm20a_cpuapp_defconfig @@ -0,0 +1,29 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Enable UART driver +CONFIG_SERIAL=y + +# Enable console +CONFIG_CONSOLE=y +CONFIG_UART_CONSOLE=y + +# Enable GPIO +CONFIG_GPIO=y + +# Enable I2C (nPM1300 PMIC, touchpad) +CONFIG_I2C=y + +# Enable MPU +CONFIG_ARM_MPU=y + +# MPU-based null-pointer dereferencing detection cannot +# be applied as the (0x0 - 0x400) is unmapped for this target. +CONFIG_NULL_POINTER_EXCEPTION_DETECTION_NONE=y + +# Enable Cache +CONFIG_CACHE_MANAGEMENT=y +CONFIG_EXTERNAL_CACHE=y + +# Start SYSCOUNTER on driver init +CONFIG_NRF_GRTC_START_SYSCOUNTER=y diff --git a/boards/framework/framework_wireless_tp_kb/pre_dt_board.cmake b/boards/framework/framework_wireless_tp_kb/pre_dt_board.cmake new file mode 100644 index 000000000000..061cced81f72 --- /dev/null +++ b/boards/framework/framework_wireless_tp_kb/pre_dt_board.cmake @@ -0,0 +1,6 @@ +# Copyright (c) 2026 Framework Computer Inc +# SPDX-License-Identifier: Apache-2.0 + +# Suppress "unique_unit_address_if_enabled" to handle the following overlaps: +# - power@X010e000 & clock@X010e000 & lfclk@X010e000 & xo@X010e000 & xo24m@X010e000 +list(APPEND EXTRA_DTC_FLAGS "-Wno-unique_unit_address_if_enabled") diff --git a/drivers/usb/common/nordic/Kconfig b/drivers/usb/common/nordic/Kconfig index c954a6a6ea7b..cd816dd5c356 100644 --- a/drivers/usb/common/nordic/Kconfig +++ b/drivers/usb/common/nordic/Kconfig @@ -6,5 +6,6 @@ config NRF_USBHS_WRAPPER default y depends on DT_HAS_NORDIC_NRF_USBHS_WRAPPER_ENABLED depends on REGULATOR + select EVENTS help Driver for Nordic USBHS wrapper.