Add uart driver for stm32u5

pull/840/head
Mattia Moffa 2026-07-09 05:19:51 +02:00 committed by Daniele Lacamera
parent 4efc00a806
commit 6b8702dd63
4 changed files with 262 additions and 10 deletions

View File

@ -24,6 +24,7 @@
#include <string.h>
#include "hal/stm32u5.h"
#include "hal.h"
#include "uart_drv.h"
static void RAMFUNCTION flash_set_waitstates(unsigned int waitstates)
@ -33,7 +34,7 @@ static void RAMFUNCTION flash_set_waitstates(unsigned int waitstates)
FLASH_ACR = (reg & ~FLASH_ACR_LATENCY_MASK) | waitstates;
}
static RAMFUNCTION void flash_wait_complete(uint8_t bank)
void RAMFUNCTION hal_flash_wait_complete(uint8_t bank)
{
while ((FLASH_NS_SR & (FLASH_SR_BSY | FLASH_SR_WDW)) != 0)
;
@ -44,7 +45,7 @@ static RAMFUNCTION void flash_wait_complete(uint8_t bank)
}
static void RAMFUNCTION flash_clear_errors(uint8_t bank)
void RAMFUNCTION hal_flash_clear_errors(uint8_t bank)
{
FLASH_NS_SR |= (FLASH_SR_OPERR | FLASH_SR_PROGERR | FLASH_SR_WRPERR |
@ -68,7 +69,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
uint32_t qword[4];
volatile uint32_t *sr, *cr;
flash_clear_errors(0);
hal_flash_clear_errors(0);
src = (uint32_t*)data;
dst = (uint32_t*)address;
@ -105,7 +106,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
ISB();
dst[(i >> 2) + 3] = qword[3];
ISB();
flash_wait_complete(0);
hal_flash_wait_complete(0);
if ((*sr & FLASH_SR_EOP) != 0)
*sr |= FLASH_SR_EOP;
*cr &= ~FLASH_CR_PG;
@ -117,7 +118,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
void RAMFUNCTION hal_flash_unlock(void)
{
flash_wait_complete(0);
hal_flash_wait_complete(0);
#if (TZ_SECURE())
if ((FLASH_CR & FLASH_CR_LOCK) != 0) {
FLASH_KEYR = FLASH_KEY1;
@ -140,7 +141,7 @@ void RAMFUNCTION hal_flash_unlock(void)
void RAMFUNCTION hal_flash_lock(void)
{
flash_wait_complete(0);
hal_flash_wait_complete(0);
#if (TZ_SECURE())
if ((FLASH_CR & FLASH_CR_LOCK) == 0)
FLASH_CR |= FLASH_CR_LOCK;
@ -151,7 +152,7 @@ void RAMFUNCTION hal_flash_lock(void)
void RAMFUNCTION hal_flash_opt_unlock(void)
{
flash_wait_complete(0);
hal_flash_wait_complete(0);
if ((FLASH_NS_CR & FLASH_CR_OPTLOCK) != 0) {
FLASH_NS_OPTKEYR = FLASH_OPTKEY1;
@ -167,7 +168,7 @@ void RAMFUNCTION hal_flash_opt_lock(void)
{
FLASH_NS_CR |= FLASH_CR_OPTSTRT;
flash_wait_complete(0);
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;
@ -179,7 +180,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
uint32_t p;
volatile uint32_t *cr = &FLASH_NS_CR;
flash_clear_errors(0);
hal_flash_clear_errors(0);
if (len == 0)
return -1;
@ -219,7 +220,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
*cr = reg;
DMB();
*cr |= FLASH_CR_STRT;
flash_wait_complete(0);
hal_flash_wait_complete(0);
}
/* If the erase operation is completed, disable the associated bits */
*cr &= ~FLASH_CR_PER ;
@ -500,6 +501,8 @@ static void led_unsecure()
#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)
{
@ -522,6 +525,24 @@ static void periph_unsecure(void)
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
@ -554,6 +575,12 @@ void hal_init(void)
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();

View File

@ -277,6 +277,68 @@
#define RCC_AHB2_CLOCK_ER RCC_AHB2ENR1_CLOCK_ER
/* UART */
#if (TZ_SECURE())
#define UART1 (0x50013800) /* USART1 - RM0456 - Table 4 */
#define GPIOA_BASE (0x52020000)
#else
#define UART1 (0x40013800) /* USART1 - RM0456 - Table 4 */
#define GPIOA_BASE (0x42020000)
#endif
#define UART_CR1(base) (*(volatile uint32_t *)((base) + 0x00))
#define UART_CR2(base) (*(volatile uint32_t *)((base) + 0x04))
#define UART_CR3(base) (*(volatile uint32_t *)((base) + 0x08))
#define UART_BRR(base) (*(volatile uint32_t *)((base) + 0x0c))
#define UART_ISR(base) (*(volatile uint32_t *)((base) + 0x1c))
#define UART_ICR(base) (*(volatile uint32_t *)((base) + 0x20))
#define UART_RDR(base) (*(volatile uint32_t *)((base) + 0x24))
#define UART_TDR(base) (*(volatile uint32_t *)((base) + 0x28))
#define UART_PRE(base) (*(volatile uint32_t *)((base) + 0x2C))
#define UART_CR1_UART_ENABLE (1 << 0)
#define UART_CR1_OVER8 (1 << 15)
#define UART_CR1_SYMBOL_LEN (1 << 12)
#define UART_CR1_PARITY_ENABLED (1 << 10)
#define UART_CR1_PARITY_ODD (1 << 9)
#define UART_CR1_TX_ENABLE (1 << 3)
#define UART_CR1_RX_ENABLE (1 << 2)
#define UART_CR2_STOPBITS (3 << 12)
#define UART_CR2_LINEN (1 << 14)
#define UART_CR2_CLKEN (1 << 11)
#define UART_CR3_HDSEL (1 << 3)
#define UART_CR3_SCEN (1 << 5)
#define UART_CR3_IREN (1 << 1)
#define UART_ISR_TX_EMPTY (1 << 7)
#define UART_ISR_TX_COMPLETE (1 << 6)
#define UART_ISR_RX_NOTEMPTY (1 << 5)
#define UART_EPE (1 << 0) /* Parity error */
#define UART_EFE (1 << 1) /* Framing error */
#define UART_ENE (1 << 2) /* Noise error */
#define UART_ORE (1 << 3) /* Overrun error */
#define UART1_APB2_CLOCK_ER_VAL (1 << 14) /* RM0456 - RCC_APB2ENR - USART1EN */
#define RCC_APB2ENR (*(volatile uint32_t *)(RCC_BASE + 0xA4)) /* RM0456 - Table 108 */
#define RCC_CCIPR1 (*(volatile uint32_t *)(RCC_BASE + 0xE0)) /* RM0456 - 11.8.46 */
#define RCC_CCIPR1_USART1SEL_SHIFT (0)
#define RCC_CCIPR1_USART1SEL_MASK (0x3)
#define RCC_CCIPR1_USART1SEL_HSI16 (0x2)
#define HSI16_FREQ (16000000)
/* USART1 pin configuration: PA9 (TX) / PA10 (RX), AF7.
* Connected to the ST-LINK VCP on Nucleo-U575ZI-Q. */
#define UART1_PIN_AF 7
#define UART1_TX_PIN 9
#define UART1_RX_PIN 10
#define GPIOA_AHB2ENR1_CLOCK_ER (1 << 0)
#define GPIOA_MODE (*(volatile uint32_t *)(GPIOA_BASE + 0x00))
#define GPIOA_AFL (*(volatile uint32_t *)(GPIOA_BASE + 0x20))
#define GPIOA_AFH (*(volatile uint32_t *)(GPIOA_BASE + 0x24))
#define GPIOA_SECCFGR (*(volatile uint32_t *)(GPIOA_BASE + 0x30))
/* Reset */
#define OPTR_SWAP_BANK (1 << 20)

View File

@ -0,0 +1,157 @@
/* uart_drv_stm32u5.c
*
* Driver for the back-end of the UART_FLASH module.
*
* Example implementation for stm32U5
* using USART1 (PA9/PA10, ST-LINK VCP on B-U585I-IOT02A).
*
*
* 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
*/
#ifdef TARGET_stm32u5
#include <stdint.h>
#include "hal/stm32u5.h"
static void uart_pins_setup(void)
{
uint32_t reg;
RCC_AHB2ENR1_CLOCK_ER |= GPIOA_AHB2ENR1_CLOCK_ER;
/* Set mode = AF */
reg = GPIOA_MODE & ~(0x03 << (UART1_RX_PIN * 2));
GPIOA_MODE = reg | (2 << (UART1_RX_PIN * 2));
reg = GPIOA_MODE & ~(0x03 << (UART1_TX_PIN * 2));
GPIOA_MODE = reg | (2 << (UART1_TX_PIN * 2));
/* Alternate function: use hi pins (9 and 10) */
reg = GPIOA_AFH & ~(0xf << ((UART1_TX_PIN - 8) * 4));
GPIOA_AFH = reg | (UART1_PIN_AF << ((UART1_TX_PIN - 8) * 4));
reg = GPIOA_AFH & ~(0xf << ((UART1_RX_PIN - 8) * 4));
GPIOA_AFH = reg | (UART1_PIN_AF << ((UART1_RX_PIN - 8) * 4));
}
static void uart_clear_errors(uint32_t base)
{
UART_ICR(base) = UART_ISR(base) & (UART_ENE | UART_EPE | UART_ORE | UART_EFE);
}
int uart_tx(const uint8_t c)
{
volatile uint32_t reg;
do {
reg = UART_ISR(UART1);
if (reg & (UART_ENE | UART_EPE | UART_ORE | UART_EFE))
uart_clear_errors(UART1);
} while ((reg & UART_ISR_TX_EMPTY) == 0);
UART_TDR(UART1) = c;
return 1;
}
int uart_rx(uint8_t *c)
{
volatile uint32_t reg;
reg = UART_ISR(UART1);
if (reg & (UART_ENE | UART_EPE | UART_ORE | UART_EFE))
uart_clear_errors(UART1);
if (reg & UART_ISR_RX_NOTEMPTY) {
*c = (uint8_t)UART_RDR(UART1);
return 1;
}
return 0;
}
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
{
uint32_t reg;
/* Enable USART1 peripheral clock */
RCC_APB2ENR |= UART1_APB2_CLOCK_ER_VAL;
/* If the UART is already running (e.g. left enabled by the secure
* bootloader), let the last frame finish shifting out before
* reconfiguring, or the character is cut mid-frame */
if (UART_CR1(UART1) & UART_CR1_UART_ENABLE) {
while ((UART_ISR(UART1) & UART_ISR_TX_COMPLETE) == 0) {};
}
uart_pins_setup();
/* Use HSI16 as USART1 kernel clock, so the baud rate does not
* depend on the current SYSCLK/PLL configuration */
RCC_CR |= RCC_CR_HSION;
while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
reg = RCC_CCIPR1 & (~(RCC_CCIPR1_USART1SEL_MASK << RCC_CCIPR1_USART1SEL_SHIFT));
RCC_CCIPR1 = reg | (RCC_CCIPR1_USART1SEL_HSI16 << RCC_CCIPR1_USART1SEL_SHIFT);
/* Disable UART to configure BRR (only writable while disabled) */
UART_CR1(UART1) &= ~UART_CR1_UART_ENABLE;
/* Enable 16-bit oversampling */
UART_CR1(UART1) &= ~UART_CR1_OVER8;
/* Configure baud rate */
UART_BRR(UART1) = (uint16_t)(HSI16_FREQ / bitrate);
/* Configure data bits */
if (data == 8)
UART_CR1(UART1) &= ~UART_CR1_SYMBOL_LEN;
else
UART_CR1(UART1) |= UART_CR1_SYMBOL_LEN;
/* Configure parity */
switch (parity) {
case 'O':
UART_CR1(UART1) |= UART_CR1_PARITY_ODD;
/* fall through to enable parity */
/* FALL THROUGH */
case 'E':
UART_CR1(UART1) |= UART_CR1_PARITY_ENABLED;
break;
default:
UART_CR1(UART1) &= ~(UART_CR1_PARITY_ENABLED | UART_CR1_PARITY_ODD);
}
/* Set stop bits */
reg = UART_CR2(UART1) & ~UART_CR2_STOPBITS;
if (stop > 1)
UART_CR2(UART1) = reg | (2 << 12);
else
UART_CR2(UART1) = reg;
/* Clear flags for async mode */
UART_CR2(UART1) &= ~(UART_CR2_LINEN | UART_CR2_CLKEN);
UART_CR3(UART1) &= ~(UART_CR3_SCEN | UART_CR3_HDSEL | UART_CR3_IREN);
/* Configure for RX+TX, turn on. */
UART_CR1(UART1) |= UART_CR1_TX_ENABLE | UART_CR1_RX_ENABLE | UART_CR1_UART_ENABLE;
return 0;
}
#ifdef DEBUG_UART
void uart_write(const char *buf, unsigned int len)
{
while (len--) {
uart_tx(*buf);
buf++;
}
}
#endif
#endif /* TARGET_stm32u5 */

View File

@ -654,6 +654,9 @@ ifeq ($(TARGET),stm32u5)
endif
CFLAGS+=-mcpu=cortex-m33
LDFLAGS+=-mcpu=cortex-m33
ifeq ($(DEBUG_UART),1)
APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET)_ns.o
endif
endif
ifeq ($(TARGET),stm32u3)
@ -1382,6 +1385,9 @@ delta-extra-data: image.bin
../hal/spi/spi_drv_$(SPI_TARGET)_ns.o: ../hal/spi/spi_drv_$(SPI_TARGET).c FORCE
$(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/spi/spi_drv_$(SPI_TARGET).c -DNONSECURE_APP
../hal/uart/uart_drv_$(UART_TARGET)_ns.o: ../hal/uart/uart_drv_$(UART_TARGET).c FORCE
$(Q)$(CC) $(CFLAGS) -c -o $(@) ../hal/uart/uart_drv_$(UART_TARGET).c -DNONSECURE_APP
board_$(BOARD).o: ../hal/boards/$(BOARD)/board.c
@echo "\t[CC-$(ARCH)] $@"
$(Q)$(CC) $(CFLAGS) -DDEBUG_UART -c $(OUTPUT_FLAG) $@ $<