From b0919e8401d0bd3caf80f352bb8edae890858249 Mon Sep 17 00:00:00 2001 From: Nicolas Munnich Date: Sat, 6 Sep 2025 22:30:43 +0000 Subject: [PATCH] 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. --- docs/blog/2025-xx-xx-zephyr-4-1.md | 2 +- docs/docs/config/system.md | 7 ++ .../bootloader/_base-config.md | 13 +++ .../bootloader/adafruit-nrf52.mdx | 84 +++++++++++++++ .../hardware-integration/bootloader/index.mdx | 31 ++++++ .../hardware-integration/bootloader/rp2.mdx | 70 ++++++++++++ .../bootloader/samd21-uf2.mdx | 102 ++++++++++++++++++ .../hardware-integration/bootloader/stm32.mdx | 71 ++++++++++++ .../bootloader/tinyuf2.mdx | 102 ++++++++++++++++++ docs/sidebars.js | 16 +++ 10 files changed, 497 insertions(+), 1 deletion(-) create mode 100644 docs/docs/development/hardware-integration/bootloader/_base-config.md create mode 100644 docs/docs/development/hardware-integration/bootloader/adafruit-nrf52.mdx create mode 100644 docs/docs/development/hardware-integration/bootloader/index.mdx create mode 100644 docs/docs/development/hardware-integration/bootloader/rp2.mdx create mode 100644 docs/docs/development/hardware-integration/bootloader/samd21-uf2.mdx create mode 100644 docs/docs/development/hardware-integration/bootloader/stm32.mdx create mode 100644 docs/docs/development/hardware-integration/bootloader/tinyuf2.mdx diff --git a/docs/blog/2025-xx-xx-zephyr-4-1.md b/docs/blog/2025-xx-xx-zephyr-4-1.md index 8fb37ce97..bb165462e 100644 --- a/docs/blog/2025-xx-xx-zephyr-4-1.md +++ b/docs/blog/2025-xx-xx-zephyr-4-1.md @@ -265,7 +265,7 @@ A few other changes, unrelated to the HWMv2 move, may impact out-of-tree boards/ ### 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 diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 4575a50d1..ec5ac224f 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -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_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 :::danger diff --git a/docs/docs/development/hardware-integration/bootloader/_base-config.md b/docs/docs/development/hardware-integration/bootloader/_base-config.md new file mode 100644 index 000000000..3172ec9f2 --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/_base-config.md @@ -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.`, 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. diff --git a/docs/docs/development/hardware-integration/bootloader/adafruit-nrf52.mdx b/docs/docs/development/hardware-integration/bootloader/adafruit-nrf52.mdx new file mode 100644 index 000000000..0e06ea7ee --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/adafruit-nrf52.mdx @@ -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. + + + +## 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 +#include +``` + +## 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; + }; +``` diff --git a/docs/docs/development/hardware-integration/bootloader/index.mdx b/docs/docs/development/hardware-integration/bootloader/index.mdx new file mode 100644 index 000000000..5190c04b0 --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/index.mdx @@ -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) diff --git a/docs/docs/development/hardware-integration/bootloader/rp2.mdx b/docs/docs/development/hardware-integration/bootloader/rp2.mdx new file mode 100644 index 000000000..fce59b032 --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/rp2.mdx @@ -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. + + + +## 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 +#include +``` + +## 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; + }; +}; +``` diff --git a/docs/docs/development/hardware-integration/bootloader/samd21-uf2.mdx b/docs/docs/development/hardware-integration/bootloader/samd21-uf2.mdx new file mode 100644 index 000000000..1894246ac --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/samd21-uf2.mdx @@ -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. + + + +## 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 +``` diff --git a/docs/docs/development/hardware-integration/bootloader/stm32.mdx b/docs/docs/development/hardware-integration/bootloader/stm32.mdx new file mode 100644 index 000000000..6d7a45dd9 --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/stm32.mdx @@ -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. + + + +## 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; + }; +}; +``` diff --git a/docs/docs/development/hardware-integration/bootloader/tinyuf2.mdx b/docs/docs/development/hardware-integration/bootloader/tinyuf2.mdx new file mode 100644 index 000000000..85386b4dc --- /dev/null +++ b/docs/docs/development/hardware-integration/bootloader/tinyuf2.mdx @@ -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. + + + +## 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 +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index 0a20a29e8..06bf9fb12 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -157,6 +157,22 @@ module.exports = { "development/hardware-integration/soft-off-setup", "development/hardware-integration/pointing", "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", label: "Lighting",