stm32_dfu.c (4878B)
1 /* Copyright 2021-2023 QMK 2 * 3 * This program is free software: you can redistribute it and/or modify 4 * it under the terms of the GNU General Public License as published by 5 * the Free Software Foundation, either version 3 of the License, or 6 * (at your option) any later version. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 */ 16 17 #include "bootloader.h" 18 #include "util.h" 19 20 #include <ch.h> 21 #include <hal.h> 22 #include "wait.h" 23 24 #ifndef STM32_BOOTLOADER_RAM_SYMBOL 25 # define STM32_BOOTLOADER_RAM_SYMBOL __ram0_end__ 26 #endif 27 28 extern uint32_t STM32_BOOTLOADER_RAM_SYMBOL; 29 30 #ifndef STM32_BOOTLOADER_DUAL_BANK 31 # define STM32_BOOTLOADER_DUAL_BANK FALSE 32 #endif 33 34 #if STM32_BOOTLOADER_DUAL_BANK 35 # include "gpio.h" 36 37 # ifndef STM32_BOOTLOADER_DUAL_BANK_GPIO 38 # error "No STM32_BOOTLOADER_DUAL_BANK_GPIO defined, don't know which pin to toggle" 39 # endif 40 41 # ifndef STM32_BOOTLOADER_DUAL_BANK_POLARITY 42 # define STM32_BOOTLOADER_DUAL_BANK_POLARITY 0 43 # endif 44 45 # ifndef STM32_BOOTLOADER_DUAL_BANK_DELAY 46 # define STM32_BOOTLOADER_DUAL_BANK_DELAY 100 47 # endif 48 49 __attribute__((weak)) void bootloader_jump(void) { 50 // For STM32 MCUs with dual-bank flash, and we're incapable of jumping to the bootloader. The first valid flash 51 // bank is executed unconditionally after a reset, so it doesn't enter DFU unless BOOT0 is high. Instead, we do 52 // it with hardware...in this case, we pull a GPIO high/low depending on the configuration, connects 3.3V to 53 // BOOT0's RC charging circuit, lets it charge the capacitor, and issue a system reset. See the QMK discord 54 // #hardware channel pins for an example circuit. 55 palSetPadMode(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_MODE_OUTPUT_PUSHPULL); 56 # if STM32_BOOTLOADER_DUAL_BANK_POLARITY 57 palSetPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO)); 58 # else 59 palClearPad(PAL_PORT(STM32_BOOTLOADER_DUAL_BANK_GPIO), PAL_PAD(STM32_BOOTLOADER_DUAL_BANK_GPIO)); 60 # endif 61 62 // Wait for a while for the capacitor to charge 63 wait_ms(STM32_BOOTLOADER_DUAL_BANK_DELAY); 64 65 // Issue a system reset to get the ROM bootloader to execute, with BOOT0 high 66 NVIC_SystemReset(); 67 } 68 69 __attribute__((weak)) void mcu_reset(void) { 70 NVIC_SystemReset(); 71 } 72 // not needed at all, but if anybody attempts to invoke it.... 73 void enter_bootloader_mode_if_requested(void) {} 74 75 #else 76 77 /* This code should be checked whether it runs correctly on platforms */ 78 # define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) 79 # define BOOTLOADER_MAGIC 0xDEADBEEF 80 # define MAGIC_ADDR (unsigned long *)(SYMVAL(STM32_BOOTLOADER_RAM_SYMBOL) - 4) 81 82 __attribute__((weak)) void bootloader_marker_enable(void) { 83 uint32_t *marker = (uint32_t *)MAGIC_ADDR; 84 *marker = BOOTLOADER_MAGIC; // set magic flag => reset handler will jump into boot loader 85 } 86 87 __attribute__((weak)) bool bootloader_marker_active(void) { 88 const uint32_t *marker = (const uint32_t *)MAGIC_ADDR; 89 return (*marker == BOOTLOADER_MAGIC) ? true : false; 90 } 91 92 __attribute__((weak)) void bootloader_marker_disable(void) { 93 uint32_t *marker = (uint32_t *)MAGIC_ADDR; 94 *marker = 0; 95 } 96 97 __attribute__((weak)) void bootloader_jump(void) { 98 bootloader_marker_enable(); 99 NVIC_SystemReset(); 100 } 101 102 __attribute__((weak)) void mcu_reset(void) { 103 NVIC_SystemReset(); 104 } 105 106 void enter_bootloader_mode_if_requested(void) { 107 if (bootloader_marker_active()) { 108 bootloader_marker_disable(); 109 110 struct system_memory_vector_t { 111 uint32_t stack_top; 112 void (*entrypoint)(void); 113 }; 114 const struct system_memory_vector_t *bootloader = (const struct system_memory_vector_t *)(STM32_BOOTLOADER_ADDRESS); 115 116 __disable_irq(); 117 118 # if defined(QMK_MCU_ARCH_CORTEX_M7) 119 SCB_DisableDCache(); 120 SCB_DisableICache(); 121 # endif 122 123 # if defined(__MPU_PRESENT) && (__MPU_PRESENT == 1U) 124 ARM_MPU_Disable(); 125 # endif 126 127 SysTick->CTRL = 0; 128 SysTick->VAL = 0; 129 SysTick->LOAD = 0; 130 131 // Clear interrupt enable and interrupt pending registers 132 for (int i = 0; i < ARRAY_SIZE(NVIC->ICER); i++) { 133 NVIC->ICER[i] = 0xFFFFFFFF; 134 NVIC->ICPR[i] = 0xFFFFFFFF; 135 } 136 137 __set_CONTROL(0); 138 __set_MSP(bootloader->stack_top); 139 __enable_irq(); 140 141 // Jump to bootloader 142 bootloader->entrypoint(); 143 while (true) { 144 } 145 } 146 } 147 #endif