mirror of https://github.com/wolfSSL/wolfBoot.git
335 lines
8.9 KiB
C
335 lines
8.9 KiB
C
/* stm32f1.c
|
|
*
|
|
* Copyright (C) 2025 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 <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "image.h"
|
|
|
|
#ifndef ARCH_FLASH_OFFSET
|
|
#define ARCH_FLASH_OFFSET 0x08000000U
|
|
#endif
|
|
|
|
/* STM32 F103 register Assembly helpers */
|
|
#define DMB() asm volatile ("dmb")
|
|
|
|
/*** RCC ***/
|
|
#define RCC_BASE (0x40021000U)
|
|
#define RCC_CR (*(volatile uint32_t *)(RCC_BASE + 0x00U))
|
|
#define RCC_CFGR (*(volatile uint32_t *)(RCC_BASE + 0x04U))
|
|
|
|
#define RCC_CR_PLLRDY (1 << 25)
|
|
#define RCC_CR_PLLON (1 << 24)
|
|
#define RCC_CR_HSERDY (1 << 17)
|
|
#define RCC_CR_HSEON (1 << 16)
|
|
#define RCC_CR_HSIRDY (1 << 1)
|
|
#define RCC_CR_HSION (1 << 0)
|
|
|
|
#define RCC_CFGR_SW_MASK 0x0003
|
|
#define RCC_CFGR_SW_HSI 0x0
|
|
#define RCC_CFGR_SW_HSE 0x1
|
|
#define RCC_CFGR_SW_PLL 0x2
|
|
|
|
#define RCC_CFGR_SWS_MASK 0x000C
|
|
#define RCC_CFGR_SWS_HSI (0 << 2)
|
|
#define RCC_CFGR_SWS_HSE (1 << 2)
|
|
#define RCC_CFGR_SWS_PLL (2 << 2)
|
|
|
|
#define RCC_CFGR_HPRE_MASK 0x00F0
|
|
#define RCC_CFGR_HPRE_DIV_NONE (0 << 4)
|
|
#define RCC_CFGR_HPRE_DIV_2 (8 << 4)
|
|
#define RCC_CFGR_HPRE_DIV_4 (9 << 4)
|
|
|
|
#define RCC_CFGR_PPRE1_MASK 0x0700
|
|
#define RCC_CFGR_PPRE1_DIV_NONE (0 << 8)
|
|
#define RCC_CFGR_PPRE1_DIV_2 (4 << 8)
|
|
#define RCC_CFGR_PPRE1_DIV_4 (5 << 8)
|
|
|
|
#define RCC_CFGR_PPRE2_MASK 0x3800
|
|
#define RCC_CFGR_PPRE2_DIV_NONE (0 << 11)
|
|
#define RCC_CFGR_PPRE2_DIV_2 (4 << 11)
|
|
#define RCC_CFGR_PPRE2_DIV_4 (5 << 11)
|
|
|
|
#define PLL_FULL_MASK (0x003F0000)
|
|
#define RCC_CFGR_PLLSRC (1 << 22)
|
|
#define RCC_CFGR_PLLMUL_MUL_9 (7 << 18)
|
|
|
|
/*** FLASH ***/
|
|
#define RCC_APB1ENR (*(volatile uint32_t *)(RCC_BASE + 0x1C))
|
|
#define RCC_APB1ENR_PWREN (1 << 28)
|
|
|
|
#define FLASH_BASE (0x40022000U)
|
|
#define FLASH_ACR (*(volatile uint32_t *)(FLASH_BASE + 0x00U))
|
|
#define FLASH_KEYR (*(volatile uint32_t *)(FLASH_BASE + 0x04U))
|
|
#define FLASH_SR (*(volatile uint32_t *)(FLASH_BASE + 0x0CU))
|
|
#define FLASH_CR (*(volatile uint32_t *)(FLASH_BASE + 0x10U))
|
|
#define FLASH_AR (*(volatile uint32_t *)(FLASH_BASE + 0x14U))
|
|
|
|
#define FLASH_MAX_SZ (128*1024) /* only low- and medium-density devices supported */
|
|
#define FLASH_PAGE_SZ (1024)
|
|
|
|
/* Register values */
|
|
#define FLASH_ACR_ENABLE_PRFT (1 << 4)
|
|
|
|
#define FLASH_SR_BSY (1 << 0)
|
|
|
|
#define FLASH_CR_LOCK (1 << 7)
|
|
#define FLASH_CR_STRT (1 << 6)
|
|
#define FLASH_CR_PER (1 << 1)
|
|
#define FLASH_CR_PG (1 << 0)
|
|
|
|
#define FLASH_KEY1 (0x45670123U)
|
|
#define FLASH_KEY2 (0xCDEF89ABU)
|
|
|
|
static void RAMFUNCTION flash_set_waitstates(int waitstates)
|
|
{
|
|
FLASH_ACR |= waitstates | FLASH_ACR_ENABLE_PRFT;
|
|
}
|
|
|
|
static int RAMFUNCTION valid_flash_area(uint32_t address, int len)
|
|
{
|
|
if (len <= 0 || len > FLASH_MAX_SZ)
|
|
return -1;
|
|
if (address < ARCH_FLASH_OFFSET || address >= (ARCH_FLASH_OFFSET + FLASH_MAX_SZ))
|
|
return -1;
|
|
if ((address + len) > (ARCH_FLASH_OFFSET + FLASH_MAX_SZ))
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void RAMFUNCTION flash_wait_complete(void)
|
|
{
|
|
while ((FLASH_SR & FLASH_SR_BSY) == FLASH_SR_BSY)
|
|
;
|
|
}
|
|
|
|
static int RAMFUNCTION flash_erase_page(uint32_t address)
|
|
{
|
|
const uint32_t end = address + FLASH_PAGE_SZ;
|
|
|
|
flash_wait_complete();
|
|
|
|
FLASH_CR |= FLASH_CR_PER;
|
|
FLASH_AR = address;
|
|
FLASH_CR |= FLASH_CR_STRT;
|
|
flash_wait_complete();
|
|
FLASH_CR &= ~FLASH_CR_PER;
|
|
|
|
/* verify erase */
|
|
while (address < end) {
|
|
if (*(uint32_t*)address != (uint32_t)~0)
|
|
return -1;
|
|
|
|
address += 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int RAMFUNCTION flash_w16(volatile uint16_t *dst, const uint16_t value)
|
|
{
|
|
/* do the write */
|
|
*dst = value;
|
|
DMB();
|
|
flash_wait_complete();
|
|
|
|
/* verify */
|
|
return (*dst == value) ? 0 : -1;
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|
{
|
|
uint16_t tmp;
|
|
const uint8_t *src = data;
|
|
const uint8_t * const end = src + len;
|
|
/* make sure dst is aligned to 16 bits */
|
|
volatile uint16_t *dst = (volatile uint16_t*)(address & ~1);
|
|
|
|
if (valid_flash_area(address, len) != 0)
|
|
return -1;
|
|
|
|
flash_wait_complete();
|
|
FLASH_CR |= FLASH_CR_PG;
|
|
/* writes are 16 bits. Check for unaligned initial write */
|
|
if (address & 1) {
|
|
/* read-modify-write */
|
|
tmp = (*dst & 0x00FF) | (data[0] << 8);
|
|
if (flash_w16(dst, tmp) != 0) {
|
|
FLASH_CR &= ~FLASH_CR_PG;
|
|
return -1;
|
|
}
|
|
/* advance dst 2 bytes, src 1 byte */
|
|
dst++;
|
|
src++;
|
|
}
|
|
/* main write loop */
|
|
while (src < end) {
|
|
/* check for unaligned last write */
|
|
if (src + 1 == end) {
|
|
/* read-modify-write */
|
|
tmp = (*dst & 0xFF00) | *src;
|
|
if (flash_w16(dst, tmp) != 0) {
|
|
FLASH_CR &= ~FLASH_CR_PG;
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
/* all systems go for a regular write */
|
|
tmp = *(const uint16_t*)src;
|
|
if (flash_w16(dst, tmp) != 0) {
|
|
FLASH_CR &= ~FLASH_CR_PG;
|
|
return -1;
|
|
}
|
|
/* advance 2 bytes */
|
|
src += 2;
|
|
dst++;
|
|
}
|
|
FLASH_CR &= ~FLASH_CR_PG;
|
|
return 0;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_unlock(void)
|
|
{
|
|
FLASH_KEYR = FLASH_KEY1;
|
|
FLASH_KEYR = FLASH_KEY2;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_lock(void)
|
|
{
|
|
FLASH_CR |= FLASH_CR_LOCK;
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
|
{
|
|
const uint32_t end_address = address + len;
|
|
|
|
if (valid_flash_area(address, len) != 0)
|
|
return -1;
|
|
if (len % FLASH_PAGE_SZ)
|
|
return -1;
|
|
|
|
while (address < end_address) {
|
|
if (flash_erase_page(address))
|
|
return -1;
|
|
|
|
address += FLASH_PAGE_SZ;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void clock_pll_off(void)
|
|
{
|
|
uint32_t reg32;
|
|
|
|
/* Enable internal high-speed oscillator. */
|
|
RCC_CR |= RCC_CR_HSION;
|
|
DMB();
|
|
while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
|
|
|
|
/* Select HSI as SYSCLK source. */
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_SW_MASK);
|
|
RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
|
|
DMB();
|
|
|
|
/* Turn off PLL */
|
|
RCC_CR &= ~RCC_CR_PLLON;
|
|
DMB();
|
|
}
|
|
|
|
static void clock_pll_on(int powersave)
|
|
{
|
|
uint32_t reg32;
|
|
|
|
/* Enable Power controller */
|
|
RCC_APB1ENR |= RCC_APB1ENR_PWREN;
|
|
|
|
/* 2 wait states, if 48 MHz < SYSCLK <= 72 MHz */
|
|
flash_set_waitstates(2);
|
|
|
|
/* Enable internal high-speed oscillator. */
|
|
RCC_CR |= RCC_CR_HSION;
|
|
DMB();
|
|
while ((RCC_CR & RCC_CR_HSIRDY) == 0) {};
|
|
|
|
/* Select HSI as SYSCLK source. */
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_SW_MASK);
|
|
RCC_CFGR = (reg32 | RCC_CFGR_SW_HSI);
|
|
DMB();
|
|
|
|
/* Enable external high-speed oscillator 8MHz. */
|
|
RCC_CR |= RCC_CR_HSEON;
|
|
DMB();
|
|
while ((RCC_CR & RCC_CR_HSERDY) == 0) {};
|
|
|
|
/*
|
|
* Set prescalers for AHB, ABP1, ABP2.
|
|
*/
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_HPRE_MASK);
|
|
RCC_CFGR = reg32 | RCC_CFGR_HPRE_DIV_NONE;
|
|
DMB();
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_PPRE1_MASK);
|
|
RCC_CFGR = reg32 | RCC_CFGR_PPRE1_DIV_2;
|
|
DMB();
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_PPRE2_MASK);
|
|
RCC_CFGR = reg32 | RCC_CFGR_PPRE1_DIV_NONE;
|
|
DMB();
|
|
|
|
/* Set PLL config */
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(PLL_FULL_MASK);
|
|
/* PLL clock: 8 MHz (HSE) * 9 = 72 MHz */
|
|
RCC_CFGR = reg32 | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMUL_MUL_9;
|
|
DMB();
|
|
/* Enable PLL oscillator and wait for it to stabilize. */
|
|
RCC_CR |= RCC_CR_PLLON;
|
|
DMB();
|
|
while ((RCC_CR & RCC_CR_PLLRDY) == 0) {};
|
|
|
|
/* Select PLL as SYSCLK source. */
|
|
reg32 = RCC_CFGR;
|
|
reg32 &= ~(RCC_CFGR_SW_MASK);
|
|
RCC_CFGR = (reg32 | RCC_CFGR_SW_PLL);
|
|
DMB();
|
|
|
|
/* Wait for PLL clock to be selected. */
|
|
while ((RCC_CFGR & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_PLL) {};
|
|
|
|
/* Disable internal high-speed oscillator. */
|
|
RCC_CR &= ~RCC_CR_HSION;
|
|
}
|
|
|
|
void hal_init(void)
|
|
{
|
|
clock_pll_on(0);
|
|
}
|
|
|
|
void hal_prepare_boot(void)
|
|
{
|
|
clock_pll_off();
|
|
}
|