/* stm32u5.c * * Copyright (C) 2026 wolfSSL Inc. * * This file is part of wolfBoot. * * wolfBoot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include #include #include #include "hal/stm32u5.h" #include "hal.h" #include "uart_drv.h" static void RAMFUNCTION flash_set_waitstates(unsigned int waitstates) { uint32_t reg = FLASH_ACR; if ((reg & FLASH_ACR_LATENCY_MASK) != waitstates) FLASH_ACR = (reg & ~FLASH_ACR_LATENCY_MASK) | waitstates; } void RAMFUNCTION hal_flash_wait_complete(uint8_t bank) { while ((FLASH_NS_SR & (FLASH_SR_BSY | FLASH_SR_WDW)) != 0) ; #if (TZ_SECURE()) while ((FLASH_SR & (FLASH_SR_BSY | FLASH_SR_WDW)) != 0) ; #endif } void RAMFUNCTION hal_flash_clear_errors(uint8_t bank) { FLASH_NS_SR |= (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | FLASH_SR_PGSERR #if !(TZ_SECURE()) | FLASH_SR_OPTWERR #endif ); #if (TZ_SECURE()) FLASH_SR |= (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR | FLASH_SR_PGAERR | FLASH_SR_SIZERR | FLASH_SR_PGSERR | FLASH_SR_OPTWERR); #endif } int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { int i = 0; uint32_t *dst; uint32_t qword[4]; uint8_t *qword_bytes = (uint8_t *)qword; volatile uint32_t *sr, *cr; hal_flash_clear_errors(0); dst = (uint32_t*)address; #if (TZ_SECURE()) if ((((FLASH_OPTR & FLASH_OPTR_DBANK) == 0) && (address <= FLASH_TOP)) || (address < FLASH_BANK2_BASE)) { cr = &FLASH_CR; sr = &FLASH_SR; /* Convert into secure address space */ dst = (uint32_t *)((address & (~FLASHMEM_ADDRESS_SPACE)) | FLASH_SECURE_MMAP_BASE); } else if (address >= (FLASH_BANK2_BASE) && (address <= (FLASH_TOP) )) { cr = &FLASH_NS_CR; sr = &FLASH_NS_SR; } else { return 0; /* Address out of range */ } #else cr = &FLASH_NS_CR; sr = &FLASH_NS_SR; #endif while (i < len) { int j; uintptr_t cur_addr = (uintptr_t)dst + i; uint32_t *unit = (uint32_t *)(cur_addr & (~0x0FUL)); int off = (int)(cur_addr & 0x0FUL); int i_aligned = i - off; /* Read-modify-write the whole 128-bit unit (as stm32h5.c): * the program only starts on the 4th word, and a partial * quad-word leaves FLASH_SR_WDW set, hanging the wait. The * unit is aligned down from the next byte, so an unaligned * start keeps the bytes before the request. */ for (j = 0; j < 16; j++) { if ((j >= off) && (i_aligned + j < len)) qword_bytes[j] = data[i_aligned + j]; else qword_bytes[j] = ((const uint8_t *)unit)[j]; } *cr |= FLASH_CR_PG; for (j = 0; j < 4; j++) { unit[j] = qword[j]; ISB(); } hal_flash_wait_complete(0); if ((*sr & FLASH_SR_EOP) != 0) *sr |= FLASH_SR_EOP; *cr &= ~FLASH_CR_PG; i = i_aligned + 16; } return 0; } void RAMFUNCTION hal_flash_unlock(void) { hal_flash_wait_complete(0); #if (TZ_SECURE()) if ((FLASH_CR & FLASH_CR_LOCK) != 0) { FLASH_KEYR = FLASH_KEY1; DMB(); FLASH_KEYR = FLASH_KEY2; DMB(); while ((FLASH_CR & FLASH_CR_LOCK) != 0) ; } #endif if ((FLASH_NS_CR & FLASH_CR_LOCK) != 0) { FLASH_NS_KEYR = FLASH_KEY1; DMB(); FLASH_NS_KEYR = FLASH_KEY2; DMB(); while ((FLASH_NS_CR & FLASH_CR_LOCK) != 0) ; } } void RAMFUNCTION hal_flash_lock(void) { hal_flash_wait_complete(0); #if (TZ_SECURE()) if ((FLASH_CR & FLASH_CR_LOCK) == 0) FLASH_CR |= FLASH_CR_LOCK; #endif if ((FLASH_NS_CR & FLASH_CR_LOCK) == 0) FLASH_NS_CR |= FLASH_CR_LOCK; /* Drop the flash read cache at the end of the batch rather than in * hal_flash_write()/hal_flash_erase(): every write/erase sequence * ends with a lock, so one invalidate per batch replaces one per * operation (and per error return), and every consumer is covered, * not just the ones that remember to ask. */ hal_cache_invalidate(); } void RAMFUNCTION hal_flash_opt_unlock(void) { hal_flash_wait_complete(0); if ((FLASH_NS_CR & FLASH_CR_OPTLOCK) != 0) { FLASH_NS_OPTKEYR = FLASH_OPTKEY1; DMB(); FLASH_NS_OPTKEYR = FLASH_OPTKEY2; DMB(); while ((FLASH_NS_CR & FLASH_CR_OPTLOCK) != 0) ; } } void RAMFUNCTION hal_flash_opt_lock(void) { FLASH_NS_CR |= FLASH_CR_OPTSTRT; hal_flash_wait_complete(0); FLASH_NS_CR |= FLASH_CR_OBL_LAUNCH; if ((FLASH_NS_CR & FLASH_CR_OPTLOCK) == 0) FLASH_NS_CR |= FLASH_CR_OPTLOCK; } int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { uint32_t end_address; uint32_t p; volatile uint32_t *cr = &FLASH_NS_CR; hal_flash_clear_errors(0); if (len == 0) return -1; if (address < ARCH_FLASH_OFFSET) return -1; end_address = address + len; for (p = address; p < end_address; p += FLASH_PAGE_SIZE) { uint32_t reg; uint32_t base; uint32_t bker = 0; cr = &FLASH_NS_CR; if ((((FLASH_OPTR & FLASH_OPTR_DBANK) == 0) && (p <= FLASH_TOP)) || (p < FLASH_BANK2_BASE)) { #if (TZ_SECURE()) cr = &FLASH_CR; #endif base = FLASHMEM_ADDRESS_SPACE; } else if (p >= (FLASH_BANK2_BASE) && (p <= (FLASH_TOP) )) { bker = FLASH_CR_BKER; base = FLASH_BANK2_BASE; } else { *cr &= ~FLASH_CR_PER ; return 0; /* Address out of range */ } /* BKER refers to the physical bank, whatever the SWAP_BANK setting * (RM0456 7.5.8): invert it when the banks are swapped, so that the * erased bank is the one currently mapped at the target address. */ if ((FLASH_OPTR & (FLASH_OPTR_DBANK | FLASH_OPTR_SWAP_BANK)) == (FLASH_OPTR_DBANK | FLASH_OPTR_SWAP_BANK)) { bker ^= FLASH_CR_BKER; } reg = *cr & (~((FLASH_CR_PNB_MASK << FLASH_CR_PNB_SHIFT) | FLASH_CR_BKER)); reg |= ((((p - base) >> 13) << FLASH_CR_PNB_SHIFT) | FLASH_CR_PER | bker ); *cr = reg; DMB(); *cr |= FLASH_CR_STRT; hal_flash_wait_complete(0); } /* If the erase operation is completed, disable the associated bits */ *cr &= ~FLASH_CR_PER ; return 0; } static void clock_pll_off(void) { uint32_t reg32, flash_waitstates ; /* Select MSI as SYSCLK source. */ reg32 = RCC_CFGR1; reg32 &= ~((1 << 1) | (1 << 0)); RCC_CFGR1 = (reg32 | RCC_CFGR_SW_MSI); DMB(); /* Wait for MSI clock to be selected. */ while ((RCC_CFGR1 & ((1 << 1) | (1 << 0))) != RCC_CFGR_SW_MSI) {}; flash_waitstates = 1; flash_set_waitstates(flash_waitstates); /* Turn off PLL */ RCC_CR &= ~RCC_CR_HSION; RCC_CR &= ~RCC_CR_PLL1ON; DMB(); } /* This implementation will setup MSI 48 MHz as PLL Source Mux, PLLCLK as * System Clock Source */ static void clock_pll_on(int powersave) { uint32_t reg32; uint32_t pll1n, pll1m, pll1mboost, pll1q, pll1p, pll1r, pll1fracn, pll1rge; uint32_t hpre, apb1pre, apb2pre, apb3pre, flash_waitstates; /* Reset the RCC clock configuration to the default reset state ----------*/ /* Set MSION bit */ RCC_CR = RCC_CR_MSISON; /* Reset CFGR register */ RCC_CFGR1 = 0U; RCC_CFGR2 = 0U; RCC_CFGR3 = 0U; /* Reset HSEON, CSSON , HSION, PLLxON bits */ RCC_CR &= ~(RCC_CR_HSEON | RCC_CR_CSSON | RCC_CR_PLL1ON | RCC_CR_PLL2ON | RCC_CR_PLL3ON); /* Reset PLLCFGR register */ RCC_PLL1CFGR = 0U; /* Reset HSEBYP bit */ RCC_CR &= ~(RCC_CR_HSEBYP); /* Disable all interrupts */ RCC_CIER = 0U; FLASH_ACR|=FLASH_ACR_PRFTEN; RCC_AHB3ENR |= RCC_AHB3ENR_PWREN; RCC_AHB1ENR |= RCC_AHB1ENR_GTZC1EN; RCC_AHB3ENR |= RCC_AHB3ENR_GTZC2EN; PWR_UCPDR |= PWR_UCPDR_DBDIS; PWR_SVMCR |= PWR_SVMCR_IOS2V; PWR_VOSR &= ~( (PWR_VOSR_VOS_1 << PWR_VOSR_VOS_SHIFT) | PWR_VOSR_BOOSTEN ); PWR_VOSR|= ((PWR_VOSR_VOS_1<< PWR_VOSR_VOS_SHIFT) | PWR_VOSR_BOOSTEN); /* Wait until VOSRDY is raised */ reg32 = PWR_VOSR; while ((PWR_VOSR & PWR_VOSR_VOSRDY) == 0) {}; RCC_ICSCR1|= RCC_ICSCR1_MSIRGSEL; reg32 = RCC_ICSCR1; reg32 &= ~( (0xF << RCC_ICSCR1_MSIRANGE_SHIFT)); reg32|= (RCC_ICSCR1_MSIRG_0 << RCC_ICSCR1_MSIRANGE_SHIFT); RCC_ICSCR1 = reg32; reg32 = RCC_ICSCR1; DMB(); /* Adjusts the Multiple Speed oscillator (MSI) calibration value */ reg32 = RCC_ICSCR2; reg32 &= ~((0x1F << RCC_ICSCR2_MSITRIM0_SHIFT)); reg32 |= (RCC_ICSCR2_MSITRIM0_DEFAULT << RCC_ICSCR2_MSITRIM0_SHIFT); RCC_ICSCR2 = reg32; reg32 = RCC_ICSCR2; DMB(); flash_waitstates = 1; flash_set_waitstates(flash_waitstates); /*----------------------------- HSI Configuration ------------------------*/ /* Enable the Internal High Speed oscillator (HSI) */ RCC_CR |= RCC_CR_HSION; /* Wait till HSI is ready */ while ((RCC_CR & RCC_CR_HSIRDY) == 0) {}; /* Adjusts the Internal High Speed oscillator (HSI) calibration value */ reg32 = RCC_ICSCR3; reg32 &= ~((1 << 20) | (1 << 19) | (1 << 18) | (1 << 17) | (1 << 16) ); reg32 |= (RCC_ICSCR3_HSITRIM_DEFAULT << RCC_ICSCR3_HSITRIM_SHIFT); RCC_ICSCR3 = reg32; reg32 = RCC_ICSCR3; DMB(); /*-------------------------------- PLL Configuration ---------------------*/ /* Select clock parameters (CPU Speed = 160 MHz) */ pll1m = 3; pll1mboost = RCC_PLL1CFGR_PLL1MBOOST_DIV4; pll1n = 10; pll1p = 2; pll1q = 2; pll1r = 1; pll1fracn = 0; pll1rge = RCC_PLL1VCIRANGE_1; hpre = RCC_AHB_PRESCALER_DIV_NONE; apb1pre = RCC_APB_PRESCALER_DIV_NONE; apb2pre = RCC_APB_PRESCALER_DIV_NONE; apb3pre = RCC_APB_PRESCALER_DIV_NONE; /* Disable the main PLL */ RCC_CR &= ~RCC_CR_PLL1ON; /* Wait till PLL is ready */ while ((RCC_CR & RCC_CR_PLL1RDY) != 0) {}; /* Enable PWR CLK */ RCC_AHB3ENR|= RCC_AHB3ENR_PWREN; /*Disable EPOD to configure PLL1MBOOST*/ PWR_VOSR &= ~PWR_VOSR_BOOSTEN; /* Configure the main PLL clock source, multiplication and division factors */ reg32 = RCC_PLL1CFGR ; reg32 &= ~((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 1) | (1 << 0)); reg32 |= RCC_PLLCKSELR_PLLSRC_MSI; reg32 |= ((pll1m-1) << RCC_PLL1CFGR_PLLM_SHIFT); reg32 |= ((pll1mboost) << RCC_PLL1CFGR_PLL1MBOOST_SHIFT); RCC_PLL1CFGR = reg32; reg32 =0; reg32 |= ((pll1n-1) << RCC_PLL1DIVR_PLLN_SHIFT); reg32 |= ((pll1p-1) << RCC_PLL1DIVR_PLLP_SHIFT); reg32 |= ((pll1q-1) << RCC_PLL1DIVR_PLLQ_SHIFT); reg32 |= ((pll1r-1) << RCC_PLL1DIVR_PLLR_SHIFT); RCC_PLL1DIVR = reg32; DMB(); /* Disable PLL1FRACN */ RCC_PLL1CFGR&= ~RCC_PLL1CFGR_PLL1FRACEN; /* Configure PLL PLL1FRACN */ reg32 = RCC_PLL1FRACR ; reg32 &= ~((1 << 15) | (1 << 14) | (1 << 13) | (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4) | (1 << 3)); reg32 |= ((pll1fracn) << RCC_PLL1FRACR_SHIFT); RCC_PLL1FRACR = reg32; /* Enable PLL1FRACN */ RCC_PLL1CFGR|= RCC_PLL1CFGR_PLL1FRACEN; /* Select PLL1 input reference frequency range: VCI */ reg32 = RCC_PLL1CFGR ; reg32 &= ~((1 << 3) | (1 << 2)); reg32 |= ((pll1rge) << RCC_PLL1CFGR_PLL1RGE_SHIFT); RCC_PLL1CFGR = reg32; /* Enable the EPOD to reach max frequency */ PWR_VOSR |= PWR_VOSR_BOOSTEN; /* Disable PWR clk */ RCC_AHB3ENR&=~RCC_AHB3ENR_PWREN; /* Enable PLL System Clock output */ RCC_PLL1CFGR|=RCC_PLL1CFGR_PLL1REN; /* Enable the main PLL */ RCC_CR|=RCC_CR_PLL1ON; /* Wait till PLL is ready */ while ((RCC_CR & RCC_CR_PLL1RDY) == 0) {}; /* Increasing the number of wait states because of higher CPU frequency */ flash_waitstates = 4; flash_set_waitstates(flash_waitstates); /* Enable PWR CLK */ RCC_AHB3ENR|= RCC_AHB3ENR_PWREN; /* Wait till BOOST is ready */ while ((PWR_VOSR & PWR_VOSR_BOOSTRDY) == 0) {}; /*Disable PWR clk */ RCC_AHB3ENR&=~RCC_AHB3ENR_PWREN; /* Select PLL as SYSCLK source. */ reg32 = RCC_CFGR1; reg32 &= ~((1 << 1) | (1 << 0)); RCC_CFGR1 = (reg32 | RCC_CFGR_SW_PLL); DMB(); /* Wait for PLL clock to be selected. */ while ((RCC_CFGR1 & ((1 << 1) | (1 << 0))) != RCC_CFGR_SW_PLL) {}; /* HCLK Configuration */ reg32 = RCC_CFGR2 ; reg32 &= ~((1 << 3) | (1 << 2) | (1 << 1) | (1 << 0)); reg32 |= hpre; RCC_CFGR2 = reg32; DMB(); /* PRE1 and PRE2 conf */ reg32 = RCC_CFGR2 ; reg32 &= ~((1 << 6) | (1 << 5) | (1 << 4)); reg32 |= ((apb1pre) << RCC_CFGR2_PPRE1_SHIFT) ; reg32 &= ~((1 << 10) | (1 << 9) | (1 << 8)); reg32 |= ((apb2pre) << RCC_CFGR2_PPRE2_SHIFT) ; RCC_CFGR2 = reg32; DMB(); /* PRE3 conf */ reg32 = RCC_CFGR3; reg32 &= ~((1 << 6) | (1 << 5) | (1 << 4)); reg32 |= ((apb3pre) << RCC_CFGR3_PPRE3_SHIFT) ; RCC_CFGR3 = reg32; DMB(); /* Disable PWR clk */ RCC_AHB3ENR&=~RCC_AHB3ENR_PWREN; } static void RAMFUNCTION stm32u5_reboot(void) { AIRCR = AIRCR_SYSRESETREQ | AIRCR_VKEY; while(1) ; } void RAMFUNCTION hal_flash_dualbank_swap(void) { uint32_t cur_opts; hal_flash_unlock(); hal_flash_opt_unlock(); cur_opts = (FLASH_OPTR & FLASH_OPTR_SWAP_BANK) >> 20; if (cur_opts) FLASH_OPTR &= (~FLASH_OPTR_SWAP_BANK); else FLASH_OPTR |= FLASH_OPTR_SWAP_BANK; hal_flash_opt_lock(); hal_flash_lock(); stm32u5_reboot(); } static void led_unsecure() { #ifdef STM32_DISCOVERY /* Enable clock for User LED GPIOs */ RCC_AHB2ENR1_CLOCK_ER|= GPIOH_AHB2ENR1_CLOCK_ER; /* Un-secure User LED GPIO pins */ GPIOH_SECCFGR &= ~(1 << LED_USR_PIN); GPIOH_SECCFGR &= ~(1 << LED_BOOT_PIN); #else /* Enable clock for User LED GPIOs */ RCC_AHB2ENR1_CLOCK_ER |= GPIOC_AHB2ENR1_CLOCK_ER; RCC_AHB2ENR1_CLOCK_ER |= GPIOG_AHB2ENR1_CLOCK_ER; /* Un-secure User LED GPIO pins */ GPIOG_SECCFGR &= ~(1 << LED_USR_PIN); GPIOC_SECCFGR &= ~(1 << LED_BOOT_PIN); #endif } #if TZ_SECURE() #define TZSC1_BASE 0x50032400u #define TZSC_SECCFGR1 (*(volatile uint32_t *)(TZSC1_BASE + 0x10u)) #define TZSC_SECCFGR1_USART3SEC (1u << 10) #define TZSC_SECCFGR2 (*(volatile uint32_t *)(TZSC1_BASE + 0x14u)) #define TZSC_SECCFGR2_USART1SEC (1u << 3) static void periph_unsecure(void) { volatile uint32_t reg; /* Enable clock for GPIO D (USART3 pins PD8/PD9) */ RCC_AHB2ENR1_CLOCK_ER |= GPIOD_AHB2ENR1_CLOCK_ER; /* Enable clock for USART3 */ RCC_APB1ENR |= (1u << 18); /* Unsecure USART3 pins (PD8 TX, PD9 RX) */ GPIOD_SECCFGR &= ~(1u << 8); GPIOD_SECCFGR &= ~(1u << 9); /* Unsecure USART3 peripheral in GTZC TZSC */ reg = TZSC_SECCFGR1; if (reg & TZSC_SECCFGR1_USART3SEC) { reg &= ~TZSC_SECCFGR1_USART3SEC; DMB(); TZSC_SECCFGR1 = reg; } /* Enable clock for GPIO A (USART1 pins PA9/PA10) */ RCC_AHB2ENR1_CLOCK_ER |= GPIOA_AHB2ENR1_CLOCK_ER; /* Enable clock for USART1 */ RCC_APB2ENR |= UART1_APB2_CLOCK_ER_VAL; /* Unsecure USART1 pins (PA9 TX, PA10 RX) */ GPIOA_SECCFGR &= ~(1u << UART1_TX_PIN); GPIOA_SECCFGR &= ~(1u << UART1_RX_PIN); /* Unsecure USART1 peripheral in GTZC TZSC */ reg = TZSC_SECCFGR2; if (reg & TZSC_SECCFGR2_USART1SEC) { reg &= ~TZSC_SECCFGR2_USART1SEC; DMB(); TZSC_SECCFGR2 = reg; } } #endif #if defined(DUALBANK_SWAP) && defined(__WOLFBOOT) static uint8_t bootloader_copy_mem[BOOTLOADER_SIZE]; static void RAMFUNCTION fork_bootloader(void) { uint8_t *data = (uint8_t *) FLASHMEM_ADDRESS_SPACE; uint32_t dst = FLASH_BANK2_BASE; /* Return if content already matches */ if (memcmp(data, (void *)FLASH_BANK2_BASE, BOOTLOADER_SIZE) == 0) return; /* Read the wolfBoot image in RAM */ memcpy(bootloader_copy_mem, data, BOOTLOADER_SIZE); /* Mass-erase */ hal_flash_unlock(); hal_flash_erase(dst, BOOTLOADER_SIZE); hal_flash_write(dst, bootloader_copy_mem, BOOTLOADER_SIZE); hal_flash_lock(); } #endif void hal_init(void) { #if defined(DUALBANK_SWAP) && defined(__WOLFBOOT) if ((FLASH_OPTR & (FLASH_OPTR_SWAP_BANK | FLASH_OPTR_DBANK)) == FLASH_OPTR_DBANK) fork_bootloader(); #endif clock_pll_on(0); #ifdef DEBUG_UART uart_init(115200, 8, 'N', 1); uart_write("wolfBoot Init\n", 14); #endif #if TZ_SECURE() hal_tz_sau_init(); hal_gtzc_init(); #endif } void hal_prepare_boot(void) { #ifdef WOLFBOOT_RESTORE_CLOCK clock_pll_off(); #endif #if TZ_SECURE() periph_unsecure(); led_unsecure(); #endif } void hal_cache_enable(int way) { ICACHE_CR |= (way ? ICACHE_CR_2WAYS : ICACHE_CR_1WAY); ICACHE_CR |= ICACHE_CR_CEN; } void hal_cache_disable(void) { ICACHE_CR &= ~ICACHE_CR_CEN; } void RAMFUNCTION hal_cache_invalidate(void) { /* only try and invalidate cache if enabled */ if ((ICACHE_CR & ICACHE_CR_CEN) == 0) return; /* If cache invalidate not in progress, start it */ if ((ICACHE_SR & ICACHE_SR_BUSYF) == 0) { /* Start invalidate */ ICACHE_CR |= ICACHE_CR_CACHEINV; } /* If cache invalidate is active, wait for busy end flag */ if (ICACHE_SR & ICACHE_SR_BUSYF) { while ((ICACHE_SR & ICACHE_SR_BSYENDF) == 0); } /* Clear busy end flag */ ICACHE_SR |= ICACHE_SR_BSYENDF; }