From c4d3cf21685649de316c98a02cd4cf6dab2c9d21 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Fri, 26 Dec 2025 23:40:49 -0700 Subject: [PATCH 1/3] deps: Use hal_stm32 with stm32c0 USB device fixes. Pull in ZMK fork of hal_stm32 with USB device driver fixes for stm32c0. --- app/west.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/west.yml b/app/west.yml index d177f2bd1c0..0d9d1e024c8 100644 --- a/app/west.yml +++ b/app/west.yml @@ -28,6 +28,12 @@ manifest: - openthread - edtt - trusted-firmware-m + - name: hal_stm32 + revision: 4fcc3a3f32abe1c4cb76d9d1cef967728dd03908 + path: modules/hal/stm32 + remote: zmkfirmware + groups: + - hal - name: lvgl revision: f1db87ee98f1810328a8419572fa42a3b5f352ae path: modules/lib/gui/lvgl From 742c493462a1531daaefc3aabd091a1ab619b2f6 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Sat, 27 Dec 2025 22:35:10 -0700 Subject: [PATCH 2/3] feat: Add nBOOT_SEL bit setup for STM32 G0/C0 targets Newer STM32 C0 and G0 series SoCs do not by default allow use of the BOOT0 pin/button to enter the bootloader once something has been flash to the device, which is a change from previous series, and usually not what's wanted for keyboards running ZMK. To address this, add some optional, but default initialization code to check the nBOOT_SEL bit and override it if necessary to ensure BOOT pin/button functionality. --- app/src/boot/CMakeLists.txt | 3 +- app/src/boot/Kconfig | 8 +++++ app/src/boot/Kconfig.defaults | 1 + app/src/boot/stm32_enforce_nboot_sel.c | 46 ++++++++++++++++++++++++++ docs/docs/config/system.md | 10 ++++++ 5 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 app/src/boot/stm32_enforce_nboot_sel.c diff --git a/app/src/boot/CMakeLists.txt b/app/src/boot/CMakeLists.txt index 6e75945a7e9..264c1aa7d25 100644 --- a/app/src/boot/CMakeLists.txt +++ b/app/src/boot/CMakeLists.txt @@ -1,3 +1,4 @@ target_sources_ifdef(CONFIG_ZMK_BOOTMODE_TO_MAGIC_VALUE_MAPPER app PRIVATE bootmode_to_magic_mapper.c) -target_sources_ifdef(CONFIG_ZMK_DBL_TAP_BOOTLOADER app PRIVATE dbl_tap_bootloader.c) \ No newline at end of file +target_sources_ifdef(CONFIG_ZMK_DBL_TAP_BOOTLOADER app PRIVATE dbl_tap_bootloader.c) +target_sources_ifdef(CONFIG_ZMK_BOOT_STM32_ENFORCE_NBOOT_SEL app PRIVATE stm32_enforce_nboot_sel.c) diff --git a/app/src/boot/Kconfig b/app/src/boot/Kconfig index f98fbba5761..8cd550c224e 100644 --- a/app/src/boot/Kconfig +++ b/app/src/boot/Kconfig @@ -41,3 +41,11 @@ config ZMK_BOOTMODE_BOOTLOADER_MAGIC_VALUE hex endif + +config ZMK_BOOT_STM32_ENFORCE_NBOOT_SEL + bool + default y + depends on FLASH && (SOC_STM32C071XX || SOC_STM32G0B1XX) + select FLASH_EX_OP_ENABLED + select FLASH_STM32_OPTION_BYTES + diff --git a/app/src/boot/Kconfig.defaults b/app/src/boot/Kconfig.defaults index 41816bd865a..9fb225759ca 100644 --- a/app/src/boot/Kconfig.defaults +++ b/app/src/boot/Kconfig.defaults @@ -25,3 +25,4 @@ config ZMK_BOOTMODE_BOOTLOADER_MAGIC_VALUE default 0x57 if ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_ADAFRUIT_NRF52 endif + diff --git a/app/src/boot/stm32_enforce_nboot_sel.c b/app/src/boot/stm32_enforce_nboot_sel.c new file mode 100644 index 00000000000..f7e79c12ebf --- /dev/null +++ b/app/src/boot/stm32_enforce_nboot_sel.c @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include +#include + +#include + +LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL); + +const struct device *flash_dev = FIXED_PARTITION_DEVICE(storage_partition); + +static int stm32_enforce_nboot_sel_init(void) { + uint32_t opts = 0; + int ret = 0; + + if (!device_is_ready(flash_dev)) { + LOG_ERR("flash dev not ready"); + return 0; + } + + ret = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_OPTB_READ, (uintptr_t)NULL, &opts); + + if (ret < 0) { + LOG_ERR("Failed to read option bytes with flash ext op (%d)\n", ret); + return ret; + } + + LOG_DBG("Current option bytes: %02X\n", opts); + + if (opts & FLASH_OPTR_nBOOT_SEL) { + WRITE_BIT(opts, FLASH_OPTR_nBOOT_SEL_Pos, false); + + LOG_DBG("Writing new option bytes %02X\n", opts); + ret = flash_ex_op(flash_dev, FLASH_STM32_EX_OP_OPTB_WRITE, opts, NULL); + if (ret < 0) { + LOG_ERR("Failed to write new option bytes (%d)", ret); + return ret; + } + } + + return 0; +} + +SYS_INIT(stm32_enforce_nboot_sel_init, APPLICATION, 10); diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 36b29da7252..05f519dd89f 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -127,6 +127,16 @@ Note that `CONFIG_BT_MAX_CONN` and `CONFIG_BT_MAX_PAIRED` should be set to the s | `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 | +### STM32 nBOOT_SEL Option Byte Setup + +Some newer STM32 series SoCs, in particular stm32c0 and stm32g0, enable the `nBOOT_SEL` bit of the option bytes by default. This bit prevents entering the system ROM bootloader by holding the BOOT0 pin/button during a reset/startup. + +To ensure the BOOT button on keyboard and controllers using these SoCs works as expected after being flashed with ZMK, we check the `nBOOT_SEL` bit on startup and clear it if it is set. Should you _not_ want that functionality, for some reason, this can be disabled. + +| Config | Type | Description | Default | +| ----------------------------------------- | ---- | ------------------------------------- | ----------------------- | +| `CONFIG_ZMK_BOOT_STM32_ENFORCE_NBOOT_SEL` | bool | Ensure the `nBOOT_SEL` bit is not set | y if STM32CO or STM32G0 | + ## Snippets :::danger From 439f6374d22675da352c380c37eef41304d0c3c7 Mon Sep 17 00:00:00 2001 From: Peter Johanson Date: Tue, 30 Dec 2025 15:27:32 -0700 Subject: [PATCH 3/3] docs: Extract bootloader config into a dedicated page. Pull the bootloader intergration options out of the system configuration page into their own dedicated one. Co-authored-by: Nicolas Munnich <98408764+nmunnich@users.noreply.github.com> --- docs/docs/config/bootloader.md | 42 ++++++++++++++++++++++++++++++++++ docs/docs/config/system.md | 17 -------------- docs/sidebars.js | 1 + 3 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 docs/docs/config/bootloader.md diff --git a/docs/docs/config/bootloader.md b/docs/docs/config/bootloader.md new file mode 100644 index 00000000000..7b4d22527dc --- /dev/null +++ b/docs/docs/config/bootloader.md @@ -0,0 +1,42 @@ +--- +title: Bootloader Integration Configuration +sidebar_label: Bootloader Integration +--- + +These are general settings that control the various bootloader integration features of ZMK. + +See [Configuration Overview](index.md) for instructions on how to change these settings. + +## Kconfig + +Definition file: [zmk/app/src/boot/Kconfig](https://github.com/zmkfirmware/zmk/blob/main/app/src/boot/Kconfig) + +### Double Tap To Bootloader + +Some SoCs, like RP2040 or the various STM32 offerings, require holding a certain "boot button" down to enter the bootloader, on a hardware level. To make it easier to enter the bootloader on those platforms, ZMK integrates an optional feature to allow entering the bootloader by simply double tapping reset within the configured timeout period. + +| 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 | + +### STM32 nBOOT_SEL Option Byte Setup + +Some newer STM32 series SoCs, in particular stm32c0 and stm32g0, enable the `nBOOT_SEL` bit of the option bytes by default. This bit prevents entering the system ROM bootloader by holding the BOOT0 pin/button during a reset/startup. + +To ensure the BOOT button on keyboard and controllers using these SoCs works as expected after being flashed with ZMK, we check the `nBOOT_SEL` bit on startup and clear it if it is set. Should you _not_ want that functionality, for some reason, this can be disabled. + +| Config | Type | Description | Default | +| ----------------------------------------- | ---- | ------------------------------------- | ----------------------- | +| `CONFIG_ZMK_BOOT_STM32_ENFORCE_NBOOT_SEL` | bool | Ensure the `nBOOT_SEL` bit is not set | y if STM32CO or STM32G0 | + +### Bootmode Magic Value Mapper + +Some target SoCs may use the bootmode magic value mapper for [bootloader integration](docs/development/hardware-integration/bootloader/index.mdx). When doing so, the following configurations are used: + +| Config | Type | Description | Default | +| ---------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------- | ------- | +| `CONFIG_ZMK_BOOTMODE_BOOTLOADER_MAGIC_VALUE` | hex | The magic value to place into retained memory when the bootloader boot mode is set | none | +| `CONFIG_ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_TINYUF2` | bool | Used to default the bootloader magic value for the tinyuf2 bootloader | false | +| `CONFIG_ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_ADAFRUIT_BOSSA` | bool | Used to default the bootloader magic value for the Adafruit BOSSA (SAMD21) bootloader | false | +| `CONFIG_ZMK_BOOTMODE_MAGIC_VALUE_BOOTLOADER_TYPE_ADAFRUIT_NRF52` | bool | Used to default the bootloader magic value for the Adafruit nRF52 bootloader | false | diff --git a/docs/docs/config/system.md b/docs/docs/config/system.md index 05f519dd89f..e00dfd7a0b2 100644 --- a/docs/docs/config/system.md +++ b/docs/docs/config/system.md @@ -120,23 +120,6 @@ 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 | - -### STM32 nBOOT_SEL Option Byte Setup - -Some newer STM32 series SoCs, in particular stm32c0 and stm32g0, enable the `nBOOT_SEL` bit of the option bytes by default. This bit prevents entering the system ROM bootloader by holding the BOOT0 pin/button during a reset/startup. - -To ensure the BOOT button on keyboard and controllers using these SoCs works as expected after being flashed with ZMK, we check the `nBOOT_SEL` bit on startup and clear it if it is set. Should you _not_ want that functionality, for some reason, this can be disabled. - -| Config | Type | Description | Default | -| ----------------------------------------- | ---- | ------------------------------------- | ----------------------- | -| `CONFIG_ZMK_BOOT_STM32_ENFORCE_NBOOT_SEL` | bool | Ensure the `nBOOT_SEL` bit is not set | y if STM32CO or STM32G0 | - ## Snippets :::danger diff --git a/docs/sidebars.js b/docs/sidebars.js index af818d04f76..0b81c9c0b07 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -122,6 +122,7 @@ module.exports = { "config/battery", "config/behaviors", "config/bluetooth", + "config/bootloader", "config/combos", "config/displays", "config/encoders",