mirror of https://github.com/zmkfirmware/zmk.git
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:
parent
8e886973e6
commit
b0919e8401
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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;
|
||||
};
|
||||
```
|
||||
|
|
@ -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)
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
|
@ -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
|
||||
```
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue