From 88e208a083a516cd4970d250c7e21668974d4516 Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 15 Dec 2022 22:15:02 -0500 Subject: [PATCH] stm32: Add support for flashing stm32h7 boards Signed-off-by: Kevin O'Connor --- src/generic/armcm_canboot.c | 3 + src/generic/armcm_reset.c | 3 + src/stm32/Kconfig | 8 +- src/stm32/Makefile | 8 +- src/stm32/flash.c | 33 ++++- src/stm32/stm32h7.c | 241 ++++++++++++++++++++++++++++++++++++ 6 files changed, 292 insertions(+), 4 deletions(-) create mode 100644 src/stm32/stm32h7.c diff --git a/src/generic/armcm_canboot.c b/src/generic/armcm_canboot.c index 513a500..75d926f 100644 --- a/src/generic/armcm_canboot.c +++ b/src/generic/armcm_canboot.c @@ -36,6 +36,9 @@ set_bootup_code(uint64_t code) uint64_t *req_code = (void*)&_stack_end; *req_code = code; barrier(); +#if __CORTEX_M >= 7 + SCB_CleanDCache_by_Addr((void*)req_code, sizeof(*req_code)); +#endif } #pragma GCC diagnostic pop diff --git a/src/generic/armcm_reset.c b/src/generic/armcm_reset.c index 0fef20e..81a2642 100644 --- a/src/generic/armcm_reset.c +++ b/src/generic/armcm_reset.c @@ -18,5 +18,8 @@ try_request_canboot(void) uint64_t *req_sig = (uint64_t *)bl_vectors[0]; irq_disable(); *req_sig = REQUEST_CANBOOT; +#if __CORTEX_M >= 7 + SCB_CleanDCache_by_Addr((void*)req_sig, sizeof(*req_sig)); +#endif NVIC_SystemReset(); } diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index b85f5e2..d49c8f8 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -76,10 +76,10 @@ choice bool "STM32G431" if 0 select MACH_STM32G4 config MACH_STM32H723 - bool "STM32H723" if 0 + bool "STM32H723" select MACH_STM32H7 config MACH_STM32H743 - bool "STM32H743" if 0 + bool "STM32H743" select MACH_STM32H7 config MACH_STM32H750 bool "STM32H750" if 0 @@ -184,6 +184,7 @@ config FLASH_BOOT_ADDRESS config RAM_START hex + default 0x24000000 if MACH_STM32H7 # Use "AXI SRAM" to persist reboot flag default 0x20000000 config RAM_SIZE @@ -478,6 +479,8 @@ config FLASH_START choice prompt "Application start offset" + config STM32_APP_START_20000 + bool "128KiB offset" if MACH_STM32H7 config STM32_APP_START_8000 bool "32KiB offset" if MACH_STM32F2 || MACH_STM32F4 config STM32_APP_START_4000 @@ -490,6 +493,7 @@ endchoice config LAUNCH_APP_ADDRESS hex + default 0x8020000 if STM32_APP_START_20000 default 0x8008000 if STM32_APP_START_8000 default 0x8004000 if STM32_APP_START_4000 default 0x8002000 if STM32_APP_START_2000 diff --git a/src/stm32/Makefile b/src/stm32/Makefile index cbd7af4..514da93 100644 --- a/src/stm32/Makefile +++ b/src/stm32/Makefile @@ -9,6 +9,7 @@ dirs-$(CONFIG_MACH_STM32F1) += lib/stm32f1 dirs-$(CONFIG_MACH_STM32F2) += lib/stm32f2 dirs-$(CONFIG_MACH_STM32F4) += lib/stm32f4 dirs-$(CONFIG_MACH_STM32G0) += lib/stm32g0 +dirs-$(CONFIG_MACH_STM32H7) += lib/stm32h7 MCU := $(shell echo $(CONFIG_MCU)) MCU_UPPER := $(shell echo $(CONFIG_MCU) | tr a-z A-Z | tr X x) @@ -18,6 +19,7 @@ CFLAGS-$(CONFIG_MACH_STM32F1) += -mcpu=cortex-m3 -Ilib/stm32f1/include CFLAGS-$(CONFIG_MACH_STM32F2) += -mcpu=cortex-m3 -Ilib/stm32f2/include CFLAGS-$(CONFIG_MACH_STM32F4) += -mcpu=cortex-m4 -Ilib/stm32f4/include CFLAGS-$(CONFIG_MACH_STM32G0) += -mcpu=cortex-m0plus -Ilib/stm32g0/include +CFLAGS-$(CONFIG_MACH_STM32H7) += -mcpu=cortex-m7 -Ilib/stm32h7/include CFLAGS += $(CFLAGS-y) -D$(MCU_UPPER) -mthumb -Ilib/cmsis-core -Ilib/fast-hash CFLAGS_canboot.elf += --specs=nano.specs --specs=nosys.specs @@ -45,6 +47,10 @@ mcu-$(CONFIG_MACH_STM32F4) += stm32/gpioperiph.c mcu-$(CONFIG_MACH_STM32G0) += stm32/stm32f0_timer.c mcu-$(CONFIG_MACH_STM32G0) += stm32/stm32g0.c stm32/gpioperiph.c +mcu-$(CONFIG_MACH_STM32H7) += ../lib/stm32h7/system_stm32h7xx.c +mcu-$(CONFIG_MACH_STM32H7) += stm32/stm32h7.c generic/armcm_timer.c +mcu-$(CONFIG_MACH_STM32H7) += stm32/gpioperiph.c + src-y += generic/armcm_canboot.c $(mcu-y) usb-src-$(CONFIG_HAVE_STM32_USBFS) := stm32/usbfs.c usb-src-$(CONFIG_HAVE_STM32_USBOTG) := stm32/usbotg.c @@ -52,7 +58,7 @@ src-$(CONFIG_USBSERIAL) += $(usb-src-y) stm32/chipid.c generic/usb_cdc.c serial-src-y := stm32/serial.c serial-src-$(CONFIG_MACH_STM32F0) := stm32/stm32f0_serial.c serial-src-$(CONFIG_MACH_STM32G0) := stm32/stm32f0_serial.c -serial-src-$(CONFIG_MACH_STM32H7) := stm32/stm32h7_serial.c +serial-src-$(CONFIG_MACH_STM32H7) := stm32/stm32f0_serial.c src-$(CONFIG_SERIAL) += $(serial-src-y) generic/serial_irq.c canbus-src-y := generic/canserial.c ../lib/fast-hash/fasthash.c canbus-src-$(CONFIG_HAVE_STM32_CANBUS) += stm32/can.c diff --git a/src/stm32/flash.c b/src/stm32/flash.c index ea582d5..ee0a711 100644 --- a/src/stm32/flash.c +++ b/src/stm32/flash.c @@ -34,6 +34,8 @@ flash_get_page_size(uint32_t addr) return *flash_size <= 64 ? 1024 : 2 * 1024; } else if (CONFIG_MACH_STM32G0) { return 2 * 1024; + } else if (CONFIG_MACH_STM32H7) { + return 128 * 1024; } } @@ -48,8 +50,13 @@ check_erased(uint32_t addr, uint32_t count) return 1; } -#if CONFIG_MACH_STM32G0 // stm32g0 chip has slightly different bit name +// Some chips have slightly different register names +#if CONFIG_MACH_STM32G0 #define FLASH_SR_BSY (FLASH_SR_BSY1 | FLASH_SR_BSY2) +#elif CONFIG_MACH_STM32H7 +#define CR CR1 +#define SR SR1 +#define KEYR KEYR1 #endif // Wait for flash hardware to report ready @@ -114,6 +121,13 @@ erase_page(uint32_t page_address) } pidx = pidx > 0x3ff ? 0x3ff : pidx; FLASH->CR = FLASH_CR_PER | FLASH_CR_STRT | (pidx << FLASH_CR_PNB_Pos); +#elif CONFIG_MACH_STM32H7 + uint32_t snb = (page_address - 0x08000000) / (128 * 1024); + snb = snb > 7 ? 7 : snb; + FLASH->CR = FLASH_CR_SER | FLASH_CR_START | (snb << FLASH_CR_SNB_Pos); + while (FLASH->SR & FLASH_SR_QW) + ; + SCB_InvalidateDCache_by_Addr((void*)page_address, 128*1024); #endif wait_flash(); } @@ -144,6 +158,23 @@ write_block(uint32_t block_address, uint32_t *data) writel(&page[i*2 + 1], data[i*2 + 1]); wait_flash(); } +#elif CONFIG_MACH_STM32H7 + uint32_t *page = (void*)block_address; + FLASH->CR = FLASH_CR_PG; + for (int i = 0; i < CONFIG_BLOCK_SIZE / 32; i++) { + writel(&page[i*8], data[i*8]); + writel(&page[i*8 + 1], data[i*8 + 1]); + writel(&page[i*8 + 2], data[i*8 + 2]); + writel(&page[i*8 + 3], data[i*8 + 3]); + writel(&page[i*8 + 4], data[i*8 + 4]); + writel(&page[i*8 + 5], data[i*8 + 5]); + writel(&page[i*8 + 6], data[i*8 + 6]); + writel(&page[i*8 + 7], data[i*8 + 7]); + while (FLASH->SR & FLASH_SR_QW) + ; + wait_flash(); + } + SCB_InvalidateDCache_by_Addr((void*)block_address, CONFIG_BLOCK_SIZE); #endif } diff --git a/src/stm32/stm32h7.c b/src/stm32/stm32h7.c new file mode 100644 index 0000000..dc9a29d --- /dev/null +++ b/src/stm32/stm32h7.c @@ -0,0 +1,241 @@ +// Code to setup clocks on stm32h7 +// +// Copyright (C) 2020 Konstantin Vogel +// +// This file may be distributed under the terms of the GNU GPLv3 license. + +#include "autoconf.h" // CONFIG_CLOCK_REF_FREQ +#include "board/armcm_boot.h" // VectorTable +#include "board/armcm_reset.h" // try_request_canboot +#include "board/irq.h" // irq_disable +#include "board/misc.h" // bootloader_request +#include "command.h" // DECL_CONSTANT_STR +#include "internal.h" // get_pclock_frequency +#include "sched.h" // sched_main + + +/**************************************************************** + * Clock setup + ****************************************************************/ + +#define FREQ_PERIPH (CONFIG_CLOCK_FREQ / 4) + +// Map a peripheral address to its enable bits +struct cline +lookup_clock_line(uint32_t periph_base) +{ + if (periph_base >= D3_AHB1PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D3_AHB1PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->AHB4ENR, .rst=&RCC->AHB4RSTR, .bit=bit}; + } else if (periph_base >= D3_APB1PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D3_APB1PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->APB4ENR, .rst=&RCC->APB4RSTR, .bit=bit}; + } else if (periph_base >= D1_AHB1PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D1_AHB1PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->AHB3ENR, .rst=&RCC->AHB3RSTR, .bit=bit}; + } else if (periph_base >= D1_APB1PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D1_APB1PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->APB3ENR, .rst=&RCC->APB3RSTR, .bit=bit}; + } else if (periph_base >= D2_AHB2PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D2_AHB2PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->AHB2ENR, .rst=&RCC->AHB2RSTR, .bit=bit}; + } else if (periph_base >= D2_AHB1PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D2_AHB1PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->AHB1ENR, .rst=&RCC->AHB1RSTR, .bit=bit}; + } else if (periph_base >= D2_APB2PERIPH_BASE) { + uint32_t bit = 1 << ((periph_base - D2_APB2PERIPH_BASE) / 0x400); + return (struct cline){.en=&RCC->APB2ENR, .rst=&RCC->APB2RSTR, .bit=bit}; + } else { + uint32_t offset = ((periph_base - D2_APB1PERIPH_BASE) / 0x400); + if (offset < 32) { + uint32_t bit = 1 << offset; + return (struct cline){ + .en=&RCC->APB1LENR, .rst=&RCC->APB1LRSTR, .bit=bit}; + } else { + uint32_t bit = 1 << (offset - 32); + return (struct cline){ + .en=&RCC->APB1HENR, .rst=&RCC->APB1HRSTR, .bit=bit}; + } + } +} + +// Return the frequency of the given peripheral clock +uint32_t +get_pclock_frequency(uint32_t periph_base) +{ + return FREQ_PERIPH; +} + +// Enable a GPIO peripheral clock +void +gpio_clock_enable(GPIO_TypeDef *regs) +{ + uint32_t pos = ((uint32_t)regs - D3_AHB1PERIPH_BASE) / 0x400; + RCC->AHB4ENR |= (1<AHB4ENR; +} + +#if !CONFIG_STM32_CLOCK_REF_INTERNAL +DECL_CONSTANT_STR("RESERVE_PINS_crystal", "PH0,PH1"); +#endif + +// Main clock and power setup called at chip startup +static void +clock_setup(void) +{ +#if !CONFIG_MACH_STM32H723 + // Ensure USB OTG ULPI is not enabled + CLEAR_BIT(RCC->AHB1ENR, RCC_AHB1ENR_USB2OTGHSULPIEN); + CLEAR_BIT(RCC->AHB1LPENR, RCC_AHB1LPENR_USB2OTGHSULPILPEN); +#endif + // Set this despite correct defaults. + // "The software has to program the supply configuration in PWR control + // register 3" (pg. 259) + // Only a single write is allowed (pg. 304) + PWR->CR3 = (PWR->CR3 | PWR_CR3_LDOEN) & ~(PWR_CR3_BYPASS | PWR_CR3_SCUEN); + while (!(PWR->CSR1 & PWR_CSR1_ACTVOSRDY)) + ; + // (HSE 25mhz) /DIVM1(5) (pll_base 5Mhz) *DIVN1(192) (pll_freq 960Mhz) + // /DIVP1(2) (SYSCLK 480Mhz) + uint32_t pll_base = 5000000; + // Only even dividers (DIVP1) are allowed + uint32_t pll_freq = CONFIG_CLOCK_FREQ * 2; + if (!CONFIG_STM32_CLOCK_REF_INTERNAL) { + // Configure PLL from external crystal (HSE) + RCC->CR |= RCC_CR_HSEON; + while(!(RCC->CR & RCC_CR_HSERDY)) + ; + MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_PLLSRC_Msk, + RCC_PLLCKSELR_PLLSRC_HSE); + MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_DIVM1_Msk, + (CONFIG_CLOCK_REF_FREQ/pll_base) << RCC_PLLCKSELR_DIVM1_Pos); + } else { + // Configure PLL from internal 64Mhz oscillator (HSI) + // HSI frequency of 64Mhz is integer divisible with 4Mhz + pll_base = 4000000; + MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_PLLSRC_Msk, + RCC_PLLCKSELR_PLLSRC_HSI); + MODIFY_REG(RCC->PLLCKSELR, RCC_PLLCKSELR_DIVM1_Msk, + (64000000/pll_base) << RCC_PLLCKSELR_DIVM1_Pos); + } + // Set input frequency range of PLL1 according to pll_base + // 3 = 8-16Mhz, 2 = 4-8Mhz + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_PLL1RGE_Msk, RCC_PLLCFGR_PLL1RGE_2); + // Disable unused PLL1 outputs + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVR1EN_Msk, 0); + // Enable PLL1Q and set to 100MHz for SPI 1,2,3 + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVQ1EN, RCC_PLLCFGR_DIVQ1EN); + MODIFY_REG(RCC->PLL1DIVR, RCC_PLL1DIVR_Q1, + (pll_freq / FREQ_PERIPH - 1) << RCC_PLL1DIVR_Q1_Pos); + // This is necessary, default is not 1! + MODIFY_REG(RCC->PLLCFGR, RCC_PLLCFGR_DIVP1EN_Msk, RCC_PLLCFGR_DIVP1EN); + // Set multiplier DIVN1 and post divider DIVP1 + // 001 = /2, 010 = not allowed, 0011 = /4 ... + MODIFY_REG(RCC->PLL1DIVR, RCC_PLL1DIVR_N1_Msk, + (pll_freq/pll_base - 1) << RCC_PLL1DIVR_N1_Pos); + MODIFY_REG(RCC->PLL1DIVR, RCC_PLL1DIVR_P1_Msk, + (pll_freq/CONFIG_CLOCK_FREQ - 1) << RCC_PLL1DIVR_P1_Pos); + + // Pwr + MODIFY_REG(PWR->D3CR, PWR_D3CR_VOS_Msk, PWR_D3CR_VOS); + while (!(PWR->D3CR & PWR_D3CR_VOSRDY)) + ; + + // Enable VOS0 (overdrive) + if (CONFIG_CLOCK_FREQ > 400000000) { + RCC->APB4ENR |= RCC_APB4ENR_SYSCFGEN; +#if !CONFIG_MACH_STM32H723 + SYSCFG->PWRCR |= SYSCFG_PWRCR_ODEN; +#else + PWR->CR3 |= PWR_CR3_BYPASS; +#endif + while (!(PWR->D3CR & PWR_D3CR_VOSRDY)) + ; + } + + SCB_EnableICache(); + SCB_EnableDCache(); + + // Set flash latency according to clock frequency (pg.159) + uint32_t flash_acr_latency = (CONFIG_CLOCK_FREQ > 450000000) ? + FLASH_ACR_LATENCY_4WS : FLASH_ACR_LATENCY_2WS; + MODIFY_REG(FLASH->ACR, FLASH_ACR_LATENCY_Msk, flash_acr_latency); + MODIFY_REG(FLASH->ACR, FLASH_ACR_WRHIGHFREQ_Msk, FLASH_ACR_WRHIGHFREQ_1); + while (!(FLASH->ACR & flash_acr_latency)) + ; + + // Set HPRE, D1PPRE, D2PPRE, D2PPRE2, D3PPRE dividers + // 480MHz / 2 = 240MHz rcc_hclk3 + MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_HPRE, RCC_D1CFGR_HPRE_3); + // 240MHz / 2 = 120MHz rcc_pclk3 + MODIFY_REG(RCC->D1CFGR, RCC_D1CFGR_D1PPRE, RCC_D1CFGR_D1PPRE_DIV2); + // 240MHz / 2 = 120MHz rcc_pclk1 + MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE1, RCC_D2CFGR_D2PPRE1_DIV2); + // 240MHz / 2 = 120MHz rcc_pclk2 + MODIFY_REG(RCC->D2CFGR, RCC_D2CFGR_D2PPRE2, RCC_D2CFGR_D2PPRE2_DIV2); + // 240MHz / 2 = 120MHz rcc_pclk4 + MODIFY_REG(RCC->D3CFGR, RCC_D3CFGR_D3PPRE, RCC_D3CFGR_D3PPRE_DIV2); + + // Switch on PLL1 + RCC->CR |= RCC_CR_PLL1ON; + while (!(RCC->CR & RCC_CR_PLL1RDY)) + ; + + // Switch system clock source (SYSCLK) to PLL1 + MODIFY_REG(RCC->CFGR, RCC_CFGR_SW_Msk, RCC_CFGR_SW_PLL1); + while ((RCC->CFGR & RCC_CFGR_SWS_Msk) != RCC_CFGR_SWS_PLL1) + ; + + // Set the source of FDCAN clock + MODIFY_REG(RCC->D2CCIP1R, RCC_D2CCIP1R_FDCANSEL, RCC_D2CCIP1R_FDCANSEL_0); + + // Configure HSI48 clock for USB + if (CONFIG_USB) { + SET_BIT(RCC->CR, RCC_CR_HSI48ON); + while((RCC->CR & RCC_CR_HSI48RDY) == 0); + SET_BIT(RCC->APB1HENR, RCC_APB1HENR_CRSEN); + SET_BIT(RCC->APB1HRSTR, RCC_APB1HRSTR_CRSRST); + CLEAR_BIT(RCC->APB1HRSTR, RCC_APB1HRSTR_CRSRST); + CLEAR_BIT(CRS->CFGR, CRS_CFGR_SYNCSRC); + SET_BIT(CRS->CR, CRS_CR_CEN | CRS_CR_AUTOTRIMEN); + CLEAR_BIT(RCC->D2CCIP2R, RCC_D2CCIP2R_USBSEL); + SET_BIT(RCC->D2CCIP2R, RCC_D2CCIP2R_USBSEL); + } +} + + +/**************************************************************** + * Bootloader + ****************************************************************/ + +// Handle reboot requests +void +bootloader_request(void) +{ + try_request_canboot(); + dfu_reboot(); +} + + +/**************************************************************** + * Startup + ****************************************************************/ + +// Main entry point - called from armcm_boot.c:ResetHandler() +void +armcm_main(void) +{ + // Run SystemInit() and then restore VTOR + SystemInit(); + RCC->D1CCIPR = 0x00000000; + RCC->D2CCIP1R = 0x00000000; + RCC->D2CCIP2R = 0x00000000; + RCC->D3CCIPR = 0x00000000; + SCB->VTOR = (uint32_t)VectorTable; + + dfu_reboot_check(); + + clock_setup(); + + sched_main(); +}