aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNick Brassel <nick@tzarc.org>2020-07-16 16:58:14 +1000
committerJames Young <18669334+noroadsleft@users.noreply.github.com>2020-08-29 14:30:02 -0700
commit93e7a8f74cc2c9c7bc50b413654642a0347a55d2 (patch)
tree60bb1683c096dff9f9c1b515f9b570d18e7a0ff3
parentd0abad27abd6d58f88985c1c28463ee51b7a116e (diff)
downloadqmk_firmware-93e7a8f74cc2c9c7bc50b413654642a0347a55d2.tar.gz
qmk_firmware-93e7a8f74cc2c9c7bc50b413654642a0347a55d2.zip
Add dual-bank STM32 bootloader support, given GPIO toggle on BOOT0 to charge RC circuit. (#8778)
-rw-r--r--docs/platformdev_chibios_earlyinit.md19
-rw-r--r--tmk_core/common/chibios/bootloader.c84
-rw-r--r--tmk_core/protocol/chibios/main.c1
3 files changed, 85 insertions, 19 deletions
diff --git a/docs/platformdev_chibios_earlyinit.md b/docs/platformdev_chibios_earlyinit.md
index 699c22377..5fd78bb33 100644
--- a/docs/platformdev_chibios_earlyinit.md
+++ b/docs/platformdev_chibios_earlyinit.md
@@ -4,17 +4,28 @@ This page describes a part of QMK that is a somewhat advanced concept, and is on
4 4
5QMK uses ChibiOS as the underlying layer to support a multitude of Arm-based devices. Each ChibiOS-supported keyboard has a low-level board definition which is responsible for initializing hardware peripherals such as the clocks, and GPIOs. 5QMK uses ChibiOS as the underlying layer to support a multitude of Arm-based devices. Each ChibiOS-supported keyboard has a low-level board definition which is responsible for initializing hardware peripherals such as the clocks, and GPIOs.
6 6
7Older QMK revisions required duplication of these board definitions inside your keyboard's directory in order to override such early initialization points; this is now abstracted into the following APIs, and allows usage of the board definitions supplied with ChibiOS itself. Check `<qmk_firmware>/lib/chibios/os/hal/boards` for the list of official definitions. If your keyboard needs extra initialization at a very early stage, consider providing keyboard-level overrides of the following APIs: 7Older QMK revisions required duplication of these board definitions inside your keyboard's directory in order to override such early initialization points; this is now abstracted into the following APIs, and allows usage of the board definitions supplied with ChibiOS itself. Check `<qmk_firmware>/lib/chibios/os/hal/boards` for the list of official definitions. If your keyboard needs extra initialization at a very early stage, consider providing keyboard-level overrides of the following APIs instead of duplicating the board definitions:
8 8
9## `early_hardware_init_pre()` :id=early-hardware-init-pre 9## `early_hardware_init_pre()` :id=early-hardware-init-pre
10 10
11The function `early_hardware_init_pre` is the earliest possible code that can be executed by a keyboard firmware. This is intended as a replacement for the ChibiOS board definition's `__early_init` function, and is the equivalent of executing at the start of the function. 11The function `early_hardware_init_pre` is the earliest possible code that can be executed by a keyboard firmware. This is intended as a replacement for the ChibiOS board definition's `__early_init` function, and is the equivalent of executing at the start of the function.
12 12
13This is executed before RAM gets cleared, and before clocks or GPIOs are configured; any delays or preparation using GPIOs is not likely to work at this point. After executing this function, RAM on the MCU may be zero'ed. Assigning values to variables during execution of this function may be overwritten. 13This is executed before RAM gets cleared, and before clocks or GPIOs are configured; for example, ChibiOS delays are not likely to work at this point. After executing this function, RAM on the MCU may be zero'ed. Assigning values to variables during execution of this function may be overwritten.
14 14
15As such, if you wish to override this API consider limiting use to writing to low-level registers. The default implementation of this function can be configured to jump to bootloader if a `RESET` key was pressed, by ensuring `#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP TRUE` is in the keyboard's `config.h` file. 15As such, if you wish to override this API consider limiting use to writing to low-level registers. The default implementation of this function can be configured to jump to bootloader if a `RESET` key was pressed:
16 16
17To implement your own version of this function, in your keyboard's source files: 17| `config.h` override | Description | Default |
18|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|
19| `#define EARLY_INIT_PERFORM_BOOTLOADER_JUMP` | Whether or not bootloader is to be executed during the early initialisation code of QMK. | `FALSE` |
20| `#define STM32_BOOTLOADER_ADDRESS` | Relevant for single-bank STM32 MCUs, signifies the memory address to jump to bootloader. Consult [AN2606](https://www.st.com/content/st_com/en/search.html#q=an2606-t=resources-page=1) for the _System Memory_ address for your MCU. This value should be of the format `0x11111111`. | `<none>` |
21| `#define STM32_BOOTLOADER_DUAL_BANK` | Relevant for dual-bank STM32 MCUs, signifies that a GPIO is to be toggled in order to enter bootloader mode. | `FALSE` |
22| `#define STM32_BOOTLOADER_DUAL_BANK_GPIO` | Relevant for dual-bank STM32 MCUs, the pin to toggle when attempting to enter bootloader mode, e.g. `B8` | `<none>` |
23| `#define STM32_BOOTLOADER_DUAL_BANK_POLARITY` | Relevant for dual-bank STM32 MCUs, the value to set the pin to in order to trigger charging of the RC circuit. e.g. `0` or `1`. | `0` |
24| `#define STM32_BOOTLOADER_DUAL_BANK_DELAY` | Relevant for dual-bank STM32 MCUs, an arbitrary measurement of time to delay before resetting the MCU. Increasing number increases the delay. | `100000` |
25
26Kinetis MCUs have no configurable options.
27
28Alternatively, to implement your own version of this function, in your keyboard's source files:
18 29
19```c 30```c
20void early_hardware_init_pre(void) { 31void early_hardware_init_pre(void) {
diff --git a/tmk_core/common/chibios/bootloader.c b/tmk_core/common/chibios/bootloader.c
index 4cf5dae7e..dceeaa6b1 100644
--- a/tmk_core/common/chibios/bootloader.c
+++ b/tmk_core/common/chibios/bootloader.c
@@ -3,28 +3,82 @@
3#include "ch.h" 3#include "ch.h"
4#include "hal.h" 4#include "hal.h"
5 5
6#ifdef STM32_BOOTLOADER_ADDRESS
7/* STM32 */
8
9/* This code should be checked whether it runs correctly on platforms */ 6/* This code should be checked whether it runs correctly on platforms */
10# define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) 7#define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0))
8#define BOOTLOADER_MAGIC 0xDEADBEEF
9#define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)
10
11#ifndef STM32_BOOTLOADER_DUAL_BANK
12# define STM32_BOOTLOADER_DUAL_BANK FALSE
13#endif
14
15#if STM32_BOOTLOADER_DUAL_BANK
16
17// Need pin definitions
18# include "config_common.h"
19
20# ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO
21# error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle"
22# endif
23
24# ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY
25# define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0
26# endif
27
28# ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY
29# define STM32_BOOTLOADER_DUAL_BANK_DELAY 100000
30# endif
31
32extern uint32_t __ram0_end__;
33
34# define bootdelay(loopcount) \
35 do { \
36 for (int i = 0; i < loopcount; ++i) { \
37 __asm__ volatile("nop\n\t" \
38 "nop\n\t" \
39 "nop\n\t"); \
40 } \
41 } while (0)
42
43void bootloader_jump(void) {
44 *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader
45 NVIC_SystemReset();
46}
47
48void enter_bootloader_mode_if_requested(void) {
49 unsigned long *check = MAGIC_ADDR;
50 if (*check == BOOTLOADER_MAGIC) {
51 *check = 0;
52
53 // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash
54 // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do
55 // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to
56 // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord
57 // #hardware channel pins for an example circuit.
58 palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL);
59# if STM32_BOOTLOADER_DUAL_BANK_POLARITY
60 palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
61# else
62 palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO));
63# endif
64
65 // Wait for a while for the capacitor to charge
66 bootdelay(STM32_BOOTLOADER_DUAL_BANK_DELAY);
67
68 // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high
69 NVIC_SystemReset();
70 }
71}
72
73#elif defined(STM32_BOOTLOADER_ADDRESS) // STM32_BOOTLOADER_DUAL_BANK
74
11extern uint32_t __ram0_end__; 75extern uint32_t __ram0_end__;
12# define BOOTLOADER_MAGIC 0xDEADBEEF
13# define MAGIC_ADDR (unsigned long *)(SYMVAL(__ram0_end__) - 4)
14 76
15/** \brief Jump to the bootloader
16 *
17 * FIXME: needs doc
18 */
19void bootloader_jump(void) { 77void bootloader_jump(void) {
20 *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader 78 *MAGIC_ADDR = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader
21 NVIC_SystemReset(); 79 NVIC_SystemReset();
22} 80}
23 81
24/** \brief Enter bootloader mode if requested
25 *
26 * FIXME: needs doc
27 */
28void enter_bootloader_mode_if_requested(void) { 82void enter_bootloader_mode_if_requested(void) {
29 unsigned long *check = MAGIC_ADDR; 83 unsigned long *check = MAGIC_ADDR;
30 if (*check == BOOTLOADER_MAGIC) { 84 if (*check == BOOTLOADER_MAGIC) {
@@ -41,7 +95,7 @@ void enter_bootloader_mode_if_requested(void) {
41 } 95 }
42} 96}
43 97
44#elif defined(KL2x) || defined(K20x) /* STM32_BOOTLOADER_ADDRESS */ 98#elif defined(KL2x) || defined(K20x) // STM32_BOOTLOADER_DUAL_BANK // STM32_BOOTLOADER_ADDRESS
45/* Kinetis */ 99/* Kinetis */
46 100
47# if defined(KIIBOHD_BOOTLOADER) 101# if defined(KIIBOHD_BOOTLOADER)
diff --git a/tmk_core/protocol/chibios/main.c b/tmk_core/protocol/chibios/main.c
index 7d32c16ed..a0d28f9af 100644
--- a/tmk_core/protocol/chibios/main.c
+++ b/tmk_core/protocol/chibios/main.c
@@ -35,6 +35,7 @@
35 35
36#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP 36#ifndef EARLY_INIT_PERFORM_BOOTLOADER_JUMP
37// Change this to be TRUE once we've migrated keyboards to the new init system 37// Change this to be TRUE once we've migrated keyboards to the new init system
38// Remember to change docs/platformdev_chibios_earlyinit.md as well.
38# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE 39# define EARLY_INIT_PERFORM_BOOTLOADER_JUMP FALSE
39#endif 40#endif
40 41