docs: Add bootloader integrtion page

Add a dediceated page to outline steps to set up bootloader integration
using the boot retention mechanism in newer Zephyr versions.
This commit is contained in:
Nicolas Munnich 2025-09-06 22:30:43 +00:00 committed by Peter Johanson
parent 8e886973e6
commit b0919e8401
10 changed files with 497 additions and 1 deletions

View File

@ -265,7 +265,7 @@ A few other changes, unrelated to the HWMv2 move, may impact out-of-tree boards/
### Bootloader Setup ### Bootloader Setup
With the version bump, the previous method to enable `&bootloader` has been disabled. Instead, ZMK is introducing _boot retention_, which as a side effect also enables `&bootloader` for SoCs which previously didn't work with said behavior, such as the STM32F072. To set up boot retention for your board, please read through [the dedicated page](/docs/development/hardware-integration/bootloader). No changes are needed for shields. With the version bump, the previous method to enable `&bootloader` has been disabled. Instead, ZMK is introducing _boot retention_, which as a side effect also enables `&bootloader` for SoCs which previously didn't work with said behavior, such as the STM32F072. To set up boot retention for your board, please read through [the dedicated page](/docs/development/hardware-integration/bootloader).
### nRF52840 NFC Pins as GPIO ### nRF52840 NFC Pins as GPIO

View File

@ -120,6 +120,13 @@ Note that `CONFIG_BT_MAX_CONN` and `CONFIG_BT_MAX_PAIRED` should be set to the s
| `CONFIG_ZMK_USB_LOGGING` | bool | Enable USB CDC ACM logging for debugging | n | | `CONFIG_ZMK_USB_LOGGING` | bool | Enable USB CDC ACM logging for debugging | n |
| `CONFIG_ZMK_LOG_LEVEL` | int | Log level for ZMK debug messages | 4 | | `CONFIG_ZMK_LOG_LEVEL` | int | Log level for ZMK debug messages | 4 |
### Double Tap To Bootloader
| Config | Type | Description | Default |
| ------------------------------------------ | ---- | ------------------------------------------------------------------- | --------------------------- |
| `CONFIG_ZMK_DBL_TAP_BOOTLOADER` | bool | Enable the double-tap to enter bootloader functionality | y if STM32 or RP2040/RP2350 |
| `CONFIG_ZMK_DBL_TAP_BOOTLOADER_TIMEOUT_MS` | int | Duration (in ms) to wait for a second reset to enter the bootloader | 500 |
## Snippets ## Snippets
:::danger :::danger

View File

@ -0,0 +1,13 @@
## Kconfig Symbol Enablement
Three Kconfig symbols need to be enabled for this feature to work, namely `RETAINED_MEM`, `RETENTION`, and `RETENTION_BOOT_MODE`. Typically, this is done by `imply`ing the symbols for the board symbol in the `Kconfig.<board>`, file, e.g.:
```dts
config BOARD_TOFU65
select SOC_RP2040
imply RETAINED_MEM
imply RETENTION
imply RETENTION_BOOT_MODE
```
By using `imply` at the board level, users of the board can choose to override the setting and disable the feature if they so choose.

View File

@ -0,0 +1,84 @@
---
title: Adafuit nRF52 Bootloader
sidebar_label: Adafuit nRF52
---
import BaseConfig from "./_base-config.md";
The [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader/) is a [magic value type bootloader](./index.mdx#magic-value-bootloaders), with some extra setup used to integrate with it.
<BaseConfig />
## Magic Value Type Kconfig
In addition to the core Kconfig symbols already set up, one additional Kconfig choice needs to be set, e.g. in `Kconfig.defaults` for the board:
```
choice ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE
default ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_ADAFRUIT_NRF52
endchoice
```
This ensures the correct magic value is stored in the retained memory.
## Devicetree Simple Include
A simple shared file can be included in your board's devicetree to set up the retained memory and retention nodes. Near the top of your file, add an include for `common/nordic/nrf52840_uf2_boot_mode.dtsi`, e.g.:
```dts
/dts-v1/;
#include <nordic/nrf52840_qiaa.dtsi>
#include <common/nordic/nrf52840_uf2_boot_mode.dtsi>
```
## Devicetree Manual Changes
### GPREGRET Setup
Nordic nRF52840 has a dedicated register that can be used to store data to persist across resets. Zephyr has a retained mem driver over this register, so we'll add the boot mode retention to that existing node:
```dts
&gpregret1 {
adafruit_boot_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
```
### Magic Value Mapper
Next, we'll set up our mapping retained mem driver, which will map from the Zephyr boot mode values to the values the bootloader is looking for:
```dts
/ {
magic_mapper {
compatible = "zmk,bootmode-to-magic-mapper";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
boot_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
};
```
### Chosen Node Properties
Finally, we'll assign two `chosen` properties for the two nodes that have been defined:
```dts
/ {
chosen {
zephyr,boot-mode = &boot_retention;
zmk,magic-boot-mode = &adafruit_boot_retention;
};
```

View File

@ -0,0 +1,31 @@
---
title: Bootloader Integration
sidebar_label: Bootloader
---
:::info
The information on this page is only relevant for **boards**, not **shields**.
:::
The `&bootloader` behavior requires properly set up [boot mode](https://docs.zephyrproject.org/4.1.0/services/retention/index.html#boot-mode) support to function properly. The behavior operates by setting the boot mode, resetting, and then relies on an SoC/bootloader specific early init hook to enter the bootloader when the boot mode is found to have been set.
Most of the SoCs actively supported by ZMK rely on a generic retained memory driver to store the boot mode between restarts, and additional configuration is required when using a second stage bootloader like the [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader/) or [tinyuf2](https://github.com/adafruit/tinyuf2).
## Magic Value Bootloaders
Most "second stage" bootloaders will enter bootloader mode on startup when a specific magic value is found in a specific reserved location in memory. For those bootloaders, an extra mapping layer is used to map the Zephyr "bootloader mode" retained value to the magic value expected by the bootloader.
The following bootloaders of this type are supported, see those pages for details on the additional configuration needed:
- [Adafruit nRF52](./adafruit-nrf52.mdx)
- [TinyUF2](./tinyuf2.mdx)
- [SAMD21 UF2](./samd21-uf2.mdx)
## Jump-To Bootloaders
Several SoCs use bootloaders that can be directly jumped to from early init code in the firmware. For these situations, the only setup required is a a [retained mem](https://docs.zephyrproject.org/4.1.0/hardware/peripherals/retained_mem.html) instance that can retain the set boot mode after the reset, in order for the early initailization code to check the value and then jump to the bootloader.
The following bootloaders of this type are supported, see those pages for details on the additional configuration needed:
- [RP2040/RP2350](./rp2.mdx)
- [STM32](./stm32.mdx)

View File

@ -0,0 +1,70 @@
---
title: RP2040/RP2350 Bootloader
sidebar_label: RP2040/RP2350
---
import BaseConfig from "./_base-config.md";
The RP2040/RP2350 Bootloader is a [jump-to type bootloader](./index.mdx#jump-to-bootloaders), with some extra setup used to integrate with it.
By default, when integrating this bootloader, a ["double tap reset to enter the bootloader"](../../../config/system.md#double-tap-to-bootloader) feature will be enabled, to help with designs that do not easily expose a BOOTSEL pin.
<BaseConfig />
## Simple Include
A simple shared file can be included in your board's devicetree to set up the necessary retained memory and retention nodes. Near the top of your file, add an include for `arm/raspberrypi/rp2040-boot-mode-retention.dtsi`, e.g.:
```dts
/dts-v1/;
#include <raspberrypi/rpi_pico/rp2040.dtsi>
#include <arm/raspberrypi/rp2040-boot-mode-retention.dtsi>
```
## Manual Changes
The include mentioned above can be performed manually, if you so choose.
First, we'll shrink the SRAM node, from the front, to create a 4-byte area that won't have anything placed in it by the normal Zephyr linking process:
```dts
&sram0 {
reg = <0x20000004 ((DT_SIZE_K(264)) - 4)>;
};
```
Next, we'll define a _new_ 4-byte RAM region for that reserved space, and within that region, set up a Zephyr retained RAM node. Within that node, we'll create the actual retention _value_ that is used:
```dts
/ {
sram@20000000 {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20000000 0x4>;
zephyr,memory-region = "RetainedMem";
status = "okay";
retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
boot_mode: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
};
};
```
Finally, we'll assign that new retention value node to the `zephyr,boot-mode` chosen property:
```dts
/ {
chosen {
zephyr,boot-mode = &boot_mode;
};
};
```

View File

@ -0,0 +1,102 @@
---
title: SAMD21 UF2 Bootloader
sidebar_label: SAMD21
---
import BaseConfig from "./_base-config.md";
The [SAMD21 UF2 Bootloader](https://github.com/adafruit/uf2-samdx1) is a [magic value type bootloader](./index.mdx#magic-value-bootloaders), with some extra setup used to integrate with it.
<BaseConfig />
## Adjust The Existing RAM node
We'll first adjust the SRAM to ensure Zephyr does not overwrite the memory location the bootloader inspect to determine if it should enter bootloader mode:
```dts
&sram0 {
reg = <0x20000000 0x7FFC>;
};
```
Note:
- The `0x20000000` address is the address of the RAM for the target. This is nearly always `0x20000000`.
- The exact value of `0x7FFC` will depend on the total RAM on the target. The value should be the total RAM, minus 4-bytes, in hex.
## Add a new memory region node with retainer RAM
```dts
/ {
sram@20007FFC {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20007FFC 0x4>;
zephyr,memory-region = "RetainedMem";
status = "okay";
retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
magic_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x4>;
};
};
};
};
```
Note:
- The node `sram@20007FFC` and the corresponding `reg` property values are obtained by adding the base RAM address (.e.g. `0x20000000`) to the shrunk RAM size (e.g. `0x7FFC`) to get the new start address for the area of reserved RAM.
- The magic values are 32-bits (4 bytes), so the second `reg` size value is `0x4`.
## Magic Mapper node
Next, we'll set up our mapping retained mem driver, which will map from the Zephyr boot mode values to the values the bootloader is looking for:
```dts
/ {
magic_mapper {
compatible = "zmk,bootmode-to-magic-mapper";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
boot_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
};
```
## Assign Chosen Properties
Finally, we'll assign two `chosen` properties for the two nodes that have been defined:
```dts
/ {
chosen {
zephyr,boot-mode = &boot_retention;
zmk,magic-boot-mode = &magic_retention;
};
};
```
## Magic Value Type Kconfig
Lastly, one Kconfig choice needs to be set, e.g. in `Kconfig.defaults` for the board:
```
choice ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE
default ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_ADAFRUIT_BOSSA
endchoice
```

View File

@ -0,0 +1,71 @@
---
title: STM32 ROM Bootloader
sidebar_label: STM32 ROM
toc_max_heading_level: 3
---
import BaseConfig from "./_base-config.md";
The [STM32 ROM Bootloader](https://www.st.com/resource/en/application_note/an2606-stm32-microcontroller-system-memory-boot-mode-stmicroelectronics.pdf) is a [jump-to type bootloader](./index.mdx#jump-to-bootloaders), with some extra setup used to integrate with it.
By default, when integrating this bootloader, a ["double tap reset to enter the bootloader"](../../../config/system.md#double-tap-to-bootloader) feature will be enabled, to help with designs that do not easily expose a BOOT pin.
<BaseConfig />
## Adjust Existing SRAM Node
First, we'll adjust the existing SRAM node to shrink it by one byte so Zephyr will not interfere with the retained mem:
```dts
/* Reduce SRAM0 usage by 1 byte to account for non-init area */
&sram0 {
reg = <0x20000000 0x3FFF>;
};
```
Note:
- The `0x20000000` address is the address of the RAM for the target. This is nearly always `0x20000000`
- The exact value of `0x3FFF` will depend on the total RAM on the target. The value should be the total RAM, minus 1-bytes, in hex
## New Memory Region & Nested Retained Mem
```dts
/ {
sram@20003FFF {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x20003FFF 0x1>;
zephyr,memory-region = "RetainedMem";
status = "okay";
retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
retention0: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
};
};
```
Note:
- The node `sram@20003FFF` and the corresponding `reg` property values are obtained by adding the base RAM address (.e.g. `0x20000000`) to the shrunk RAM size (e.g. `0x3FFF`) to get the new start address for the area of reserved RAM.
## Chosen Boot Mode Node
Finally, we'll set a chosen property to select the created retention node:
```dts
/ {
chosen {
zephyr,boot-mode = &retention0;
};
};
```

View File

@ -0,0 +1,102 @@
---
title: TinyUF2 Bootloader
sidebar_label: TinyUF2
---
import BaseConfig from "./_base-config.md";
The [TinyUF2 Bootloader](https://github.com/adafruit/tinyuf2) is a [magic value type bootloader](./index.mdx#magic-value-bootloaders), with some extra setup used to integrate with it.
<BaseConfig />
## Adjust The Existing RAM node
For TinyUF2, we'll first adjust the SRAM to ensure Zephyr does not overwrite the memory location the bootloader inspect to determine if it should enter bootloader mode:
```dts
&sram0 {
reg = <0x20000000 0x1FFFC>;
};
```
Note:
- The `0x20000000` address is the address of the RAM for the target. This is nearly always `0x20000000`
- The exact value of `0x1FFFC` will depend on the total RAM on the target. The value should be the total RAM, minus 4-bytes, in hex
## Add a New Memory Region Node with Retainer RAM
```dts
/ {
sram@2001FFFC {
compatible = "zephyr,memory-region", "mmio-sram";
reg = <0x2001FFFC 0x4>;
zephyr,memory-region = "RetainedMem";
status = "okay";
retainedmem {
compatible = "zephyr,retained-ram";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
magic_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x4>;
};
};
};
};
```
Note:
- The node `sram@2001FFFC` and the corresponding `reg` property values are obtained by adding the base RAM address (.e.g. `0x20000000`) to the shrunk RAM size (e.g. `0x1FFFC`) to get the new start address for the area of reserved RAM.
- The magic values in TinyUF2 are 32-bits (4 bytes), so the second `reg` size value is `0x4`.
## Magic Mapper node
Next, we'll set up our mapping retained mem driver, which will map from the Zephyr boot mode values to the values the bootloader is looking for:
```dts
/ {
magic_mapper {
compatible = "zmk,bootmode-to-magic-mapper";
status = "okay";
#address-cells = <1>;
#size-cells = <1>;
boot_retention: retention@0 {
compatible = "zephyr,retention";
status = "okay";
reg = <0x0 0x1>;
};
};
};
```
## Assign Chosen Properties
Finally, we'll assign two `chosen` properties for the two nodes that have been defined:
```dts
/ {
chosen {
zephyr,boot-mode = &boot_retention;
zmk,magic-boot-mode = &magic_retention;
};
};
```
## Magic Value Type Kconfig
Lastly, one Kconfig choice needs to be set, e.g. in `Kconfig.defaults` for the board:
```
choice ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE
default ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_TINYUF2
endchoice
```

View File

@ -157,6 +157,22 @@ module.exports = {
"development/hardware-integration/soft-off-setup", "development/hardware-integration/soft-off-setup",
"development/hardware-integration/pointing", "development/hardware-integration/pointing",
"development/hardware-integration/battery", "development/hardware-integration/battery",
{
type: "category",
label: "Bootloader",
link: {
type: "doc",
id: "development/hardware-integration/bootloader/index",
},
collapsed: true,
items: [
"development/hardware-integration/bootloader/adafruit-nrf52",
"development/hardware-integration/bootloader/tinyuf2",
"development/hardware-integration/bootloader/samd21-uf2",
"development/hardware-integration/bootloader/rp2",
"development/hardware-integration/bootloader/stm32",
],
},
{ {
type: "category", type: "category",
label: "Lighting", label: "Lighting",