mirror of https://github.com/wolfSSL/wolfBoot.git
451 lines
13 KiB
C
451 lines
13 KiB
C
/* max32666.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
|
|
*
|
|
* HAL for Analog Devices MAX32665/MAX32666
|
|
* Tested on MAX32666FTHR: Cortex-M4 @ 96MHz, 1MB Flash, 560KB SRAM
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include "image.h"
|
|
#include "hal.h"
|
|
#include "printf.h"
|
|
|
|
/* Override RAMFUNCTION for test-app when RAM_CODE is set */
|
|
#if defined(RAM_CODE) && !defined(__WOLFBOOT)
|
|
#undef RAMFUNCTION
|
|
#define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call))
|
|
#endif
|
|
|
|
/* Assembly helpers */
|
|
#define DMB() __asm__ volatile ("dmb")
|
|
#define DSB() __asm__ volatile ("dsb")
|
|
#define ISB() __asm__ volatile ("isb")
|
|
|
|
#define __disable_irq() __asm__ volatile ("cpsid i" ::: "memory")
|
|
#define __enable_irq() __asm__ volatile ("cpsie i" ::: "memory")
|
|
|
|
#include "max32666.h"
|
|
|
|
/* CMSIS variable required by MSDK drivers (mxc_delay.c). */
|
|
#if defined(WOLFSSL_MAX3266X) || defined(WOLFSSL_MAX3266X_OLD)
|
|
uint32_t SystemCoreClock = 96000000;
|
|
#endif
|
|
|
|
/* Helper to access FLC registers by base + offset */
|
|
#define FLC_REG(base, off) (*(volatile uint32_t *)((uint32_t)(base) + (off)))
|
|
|
|
/* ============== Flash Bank Helper ============== */
|
|
|
|
/* Determine which FLC bank controls the given address */
|
|
static volatile uint32_t* flc_base_for_addr(uint32_t address)
|
|
{
|
|
if (address < (FLASH_BASE + (FLASH_SIZE / 2))) {
|
|
return (volatile uint32_t*)FLC0_BASE;
|
|
}
|
|
return (volatile uint32_t*)FLC1_BASE;
|
|
}
|
|
|
|
/* Convert memory-mapped address into physical bank-relative offset */
|
|
static uint32_t flc_phys_addr(uint32_t address)
|
|
{
|
|
return address & ((FLASH_SIZE / 2) - 1);
|
|
}
|
|
|
|
/* ============== Watchdog Functions ============== */
|
|
|
|
static void watchdog_disable(void)
|
|
{
|
|
/* Disable WDT0 */
|
|
WDT0_CTRL &= ~(WDT_CTRL_EN | WDT_CTRL_RST_EN | WDT_CTRL_INT_EN);
|
|
}
|
|
|
|
/* ============== Clock Configuration ============== */
|
|
|
|
static void clock_init(void)
|
|
{
|
|
/* Enable HIRC96M (96 MHz) */
|
|
GCR_CLKCN |= GCR_CLKCN_HIRC96M_EN;
|
|
|
|
/* Wait for HIRC96M to be ready */
|
|
while (!(GCR_CLKCN & GCR_CLKCN_HIRC96M_RDY)) {}
|
|
|
|
/* Select HIRC96 as system clock, no prescaler (PSC=0 = div1) */
|
|
GCR_CLKCN = (GCR_CLKCN & ~(GCR_CLKCN_CLKSEL_MASK | GCR_CLKCN_PSC_MASK)) |
|
|
GCR_CLKCN_CLKSEL_HIRC96 |
|
|
GCR_CLKCN_HIRC96M_EN;
|
|
|
|
/* Wait for clock switch to complete */
|
|
while (!(GCR_CLKCN & GCR_CLKCN_CKRDY)) {}
|
|
}
|
|
|
|
/* ============== ICC (Instruction Cache) Functions ============== */
|
|
|
|
static void RAMFUNCTION icc_disable(void)
|
|
{
|
|
ICC0_CTRL &= ~ICC_CTRL_EN;
|
|
}
|
|
|
|
static void RAMFUNCTION icc_enable(void)
|
|
{
|
|
/* Invalidate and re-enable cache */
|
|
ICC0_INVALIDATE = 1;
|
|
ICC0_CTRL |= ICC_CTRL_EN;
|
|
while (!(ICC0_CTRL & ICC_CTRL_RDY)) {}
|
|
}
|
|
|
|
/* ============== Flash Functions ============== */
|
|
|
|
static void RAMFUNCTION flc_unlock(volatile uint32_t *flc_base)
|
|
{
|
|
FLC_REG(flc_base, FLC_CN_OFF) =
|
|
(FLC_REG(flc_base, FLC_CN_OFF) & ~FLC_CN_UNLOCK_MASK) |
|
|
FLC_CN_UNLOCK_UNLOCKED;
|
|
}
|
|
|
|
static void RAMFUNCTION flc_lock(volatile uint32_t *flc_base)
|
|
{
|
|
FLC_REG(flc_base, FLC_CN_OFF) =
|
|
(FLC_REG(flc_base, FLC_CN_OFF) & ~FLC_CN_UNLOCK_MASK) |
|
|
FLC_CN_UNLOCK_LOCKED;
|
|
}
|
|
|
|
static void RAMFUNCTION flc_wait_done(volatile uint32_t *flc_base)
|
|
{
|
|
/* Wait for any pending operation */
|
|
while (FLC_REG(flc_base, FLC_CN_OFF) &
|
|
(FLC_CN_WR | FLC_CN_PGE | FLC_CN_ME)) {}
|
|
|
|
/* Check done flag */
|
|
if (FLC_REG(flc_base, FLC_INTR_OFF) & FLC_INTR_DONE) {
|
|
/* Clear done flag (write 1 to clear) */
|
|
FLC_REG(flc_base, FLC_INTR_OFF) = FLC_INTR_DONE;
|
|
}
|
|
}
|
|
|
|
static int RAMFUNCTION flc_write128(uint32_t address, const uint32_t *data,
|
|
volatile uint32_t *flc_base)
|
|
{
|
|
/* Skip if data is all 0xFF (erased) */
|
|
if (data[0] == 0xFFFFFFFF && data[1] == 0xFFFFFFFF &&
|
|
data[2] == 0xFFFFFFFF && data[3] == 0xFFFFFFFF) {
|
|
return 0;
|
|
}
|
|
|
|
flc_unlock(flc_base);
|
|
flc_wait_done(flc_base);
|
|
|
|
/* Clear any previous errors */
|
|
if (FLC_REG(flc_base, FLC_INTR_OFF) & FLC_INTR_AF) {
|
|
FLC_REG(flc_base, FLC_INTR_OFF) = FLC_INTR_AF;
|
|
}
|
|
|
|
/* Set flash clock divider for 1 MHz operation */
|
|
FLC_REG(flc_base, FLC_CLKDIV_OFF) = FLC_CLKDIV_VALUE;
|
|
|
|
/* Set address */
|
|
FLC_REG(flc_base, FLC_ADDR_OFF) = flc_phys_addr(address);
|
|
|
|
/* Load 128-bit data (4 x 32-bit words) */
|
|
*(volatile uint32_t *)((uint32_t)flc_base + FLC_DATA_OFF + 0x00) = data[0];
|
|
*(volatile uint32_t *)((uint32_t)flc_base + FLC_DATA_OFF + 0x04) = data[1];
|
|
*(volatile uint32_t *)((uint32_t)flc_base + FLC_DATA_OFF + 0x08) = data[2];
|
|
*(volatile uint32_t *)((uint32_t)flc_base + FLC_DATA_OFF + 0x0C) = data[3];
|
|
|
|
/* Trigger 128-bit write */
|
|
DSB();
|
|
FLC_REG(flc_base, FLC_CN_OFF) |= FLC_CN_WR;
|
|
|
|
/* Wait for completion */
|
|
flc_wait_done(flc_base);
|
|
|
|
flc_lock(flc_base);
|
|
|
|
/* Check for access fault */
|
|
if (FLC_REG(flc_base, FLC_INTR_OFF) & FLC_INTR_AF) {
|
|
FLC_REG(flc_base, FLC_INTR_OFF) = FLC_INTR_AF;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int RAMFUNCTION flc_page_erase(uint32_t address,
|
|
volatile uint32_t *flc_base)
|
|
{
|
|
flc_unlock(flc_base);
|
|
flc_wait_done(flc_base);
|
|
|
|
/* Clear any previous errors */
|
|
if (FLC_REG(flc_base, FLC_INTR_OFF) & FLC_INTR_AF) {
|
|
FLC_REG(flc_base, FLC_INTR_OFF) = FLC_INTR_AF;
|
|
}
|
|
|
|
/* Set flash clock divider */
|
|
FLC_REG(flc_base, FLC_CLKDIV_OFF) = FLC_CLKDIV_VALUE;
|
|
|
|
/* Set address (any address within the page) */
|
|
FLC_REG(flc_base, FLC_ADDR_OFF) = flc_phys_addr(address);
|
|
|
|
/* Set erase code and trigger page erase */
|
|
FLC_REG(flc_base, FLC_CN_OFF) =
|
|
(FLC_REG(flc_base, FLC_CN_OFF) & ~FLC_CN_ERASE_CODE_MASK) |
|
|
FLC_CN_ERASE_CODE_PGE;
|
|
DSB();
|
|
FLC_REG(flc_base, FLC_CN_OFF) |= FLC_CN_PGE;
|
|
|
|
/* Wait for completion */
|
|
flc_wait_done(flc_base);
|
|
|
|
/* Clear erase bits */
|
|
FLC_REG(flc_base, FLC_CN_OFF) &=
|
|
~(FLC_CN_PGE | FLC_CN_ERASE_CODE_MASK);
|
|
|
|
flc_lock(flc_base);
|
|
|
|
/* Check for access fault */
|
|
if (FLC_REG(flc_base, FLC_INTR_OFF) & FLC_INTR_AF) {
|
|
FLC_REG(flc_base, FLC_INTR_OFF) = FLC_INTR_AF;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ============== UART Functions ============== */
|
|
|
|
#ifdef DEBUG_UART
|
|
|
|
void uart_init(void)
|
|
{
|
|
/* Enable UART peripheral clock (clear disable bit) */
|
|
GCR_PERCKCN0 &= ~DEBUG_UART_PCLKDIS;
|
|
|
|
#if DEBUG_UART_NUM == 0
|
|
/* UART0 MAP_A: P0.9 (TX), P0.10 (RX) = AF3
|
|
* AF3: EN0=0, EN1=0, EN2=1 */
|
|
GCR_PERCKCN0 &= ~GCR_PERCKCN0_GPIO0D;
|
|
GPIO0_EN0_CLR = UART0A_PINS;
|
|
GPIO0_EN1_CLR = UART0A_PINS;
|
|
*(volatile uint32_t *)(GPIO0_BASE + GPIO_EN2_SET_OFF) = UART0A_PINS;
|
|
#elif DEBUG_UART_NUM == 1
|
|
/* UART1 MAP_B: P1.12 (RX), P1.13 (TX) = AF3
|
|
* AF3: EN0=0, EN1=0, EN2=1 (per MSDK gpio_reva.c) */
|
|
GCR_PERCKCN0 &= ~GCR_PERCKCN0_GPIO1D;
|
|
GPIO1_EN0_CLR = UART1B_PINS;
|
|
GPIO1_EN1_CLR = UART1B_PINS;
|
|
*(volatile uint32_t *)(GPIO1_BASE + GPIO_EN2_SET_OFF) = UART1B_PINS;
|
|
#endif
|
|
|
|
/* Disable UART before configuration */
|
|
DEBUG_UART_CTRL = 0;
|
|
|
|
/* Configure: 8-bit, no parity, 1 stop bit, PCLK clock source.
|
|
* Per errata #8: do NOT use HIRC8M (CLKSEL=1) for TX,
|
|
* it generates a spurious pulse that corrupts framing. */
|
|
DEBUG_UART_CTRL = UART_CTRL_CHAR_SZ_8;
|
|
|
|
/* Set baud rate using PCLK (SYSCLK/2 = 48 MHz @ 96 MHz sys clock).
|
|
* baud = clk / ((IBAUD + DBAUD/128) * (128 >> FACTOR))
|
|
* For 115200 with FACTOR=0 (prescale 128):
|
|
* 48000000 / 115200 = 416.667
|
|
* IBAUD = floor(416.667/128) = 3
|
|
* DBAUD = round((416.667/128 - 3) * 128) = round(32.667) = 33
|
|
* ME10-650 DBAUD workaround (per MSDK uart_reva.c): 33 - 3 = 30
|
|
* Effective baud = 48000000 / ((3 + 33/128) * 128) = 115200.0
|
|
*/
|
|
DEBUG_UART_BAUD0 = (3UL << UART_BAUD0_IBAUD_SHIFT) |
|
|
UART_BAUD0_FACTOR_128;
|
|
DEBUG_UART_BAUD1 = 30;
|
|
|
|
/* Disable all interrupts */
|
|
DEBUG_UART_INT_EN = 0;
|
|
|
|
/* Clear any pending interrupt flags */
|
|
DEBUG_UART_INT_FL = DEBUG_UART_INT_FL;
|
|
|
|
/* Enable UART */
|
|
DEBUG_UART_CTRL |= UART_CTRL_ENABLE;
|
|
}
|
|
|
|
void RAMFUNCTION uart_write(const char* buf, unsigned int sz)
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < sz; i++) {
|
|
if (buf[i] == '\n') {
|
|
while (DEBUG_UART_STATUS & UART_STATUS_TX_FULL) {}
|
|
DEBUG_UART_FIFO = '\r';
|
|
}
|
|
while (DEBUG_UART_STATUS & UART_STATUS_TX_FULL) {}
|
|
DEBUG_UART_FIFO = buf[i];
|
|
}
|
|
/* Wait for transmission complete */
|
|
while (DEBUG_UART_STATUS & UART_STATUS_TX_BUSY) {}
|
|
}
|
|
|
|
int RAMFUNCTION uart_read(char* c)
|
|
{
|
|
if (DEBUG_UART_STATUS & UART_STATUS_RX_EMPTY) {
|
|
return 0;
|
|
}
|
|
*c = (char)(DEBUG_UART_FIFO & 0xFF);
|
|
return 1;
|
|
}
|
|
|
|
#endif /* DEBUG_UART */
|
|
|
|
/* ============== HAL Interface Functions ============== */
|
|
|
|
void hal_init(void)
|
|
{
|
|
/* Disable watchdog first */
|
|
watchdog_disable();
|
|
|
|
/* Initialize clocks to 96 MHz */
|
|
clock_init();
|
|
|
|
/* Set FLC clock dividers for both banks */
|
|
FLC0_CLKDIV = FLC_CLKDIV_VALUE;
|
|
FLC1_CLKDIV = FLC_CLKDIV_VALUE;
|
|
|
|
/* Enable instruction cache */
|
|
icc_enable();
|
|
|
|
#ifdef DEBUG_UART
|
|
uart_init();
|
|
|
|
#ifdef __WOLFBOOT
|
|
#ifdef WOLFBOOT_REPRODUCIBLE_BUILD
|
|
wolfBoot_printf("wolfBoot Version: %s\n", LIBWOLFBOOT_VERSION_STRING);
|
|
#else
|
|
wolfBoot_printf("wolfBoot Version: %s (%s %s)\n",
|
|
LIBWOLFBOOT_VERSION_STRING, __DATE__, __TIME__);
|
|
#endif
|
|
#endif /* __WOLFBOOT */
|
|
#endif /* DEBUG_UART */
|
|
}
|
|
|
|
void hal_prepare_boot(void)
|
|
{
|
|
#ifdef DEBUG_UART
|
|
/* Wait for UART to finish transmitting */
|
|
while (DEBUG_UART_STATUS & UART_STATUS_TX_BUSY) {}
|
|
|
|
/* Disable UART for clean handoff to application */
|
|
DEBUG_UART_CTRL = 0;
|
|
#endif
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|
{
|
|
int ret;
|
|
int i = 0;
|
|
uint32_t write_buf[4]; /* 128-bit (16-byte) write buffer */
|
|
volatile uint32_t *flc_base;
|
|
|
|
icc_disable();
|
|
|
|
while (len > 0) {
|
|
flc_base = flc_base_for_addr(address);
|
|
|
|
if ((len < FLASH_WRITE_SIZE) || (address & (FLASH_WRITE_SIZE - 1))) {
|
|
/* Handle unaligned start or partial write with RMW */
|
|
uint32_t aligned_addr = address & ~(FLASH_WRITE_SIZE - 1);
|
|
uint32_t offset = address - aligned_addr;
|
|
int bytes_to_copy = FLASH_WRITE_SIZE - offset;
|
|
if (bytes_to_copy > len)
|
|
bytes_to_copy = len;
|
|
|
|
memcpy(write_buf, (void*)aligned_addr, FLASH_WRITE_SIZE);
|
|
memcpy((uint8_t*)write_buf + offset, data + i, bytes_to_copy);
|
|
|
|
ret = flc_write128(aligned_addr, write_buf, flc_base);
|
|
if (ret != 0) {
|
|
icc_enable();
|
|
return ret;
|
|
}
|
|
|
|
address += bytes_to_copy;
|
|
i += bytes_to_copy;
|
|
len -= bytes_to_copy;
|
|
} else {
|
|
/* Write full 128-bit aligned words */
|
|
while (len >= FLASH_WRITE_SIZE) {
|
|
flc_base = flc_base_for_addr(address);
|
|
memcpy(write_buf, data + i, FLASH_WRITE_SIZE);
|
|
|
|
ret = flc_write128(address, write_buf, flc_base);
|
|
if (ret != 0) {
|
|
icc_enable();
|
|
return ret;
|
|
}
|
|
|
|
address += FLASH_WRITE_SIZE;
|
|
i += FLASH_WRITE_SIZE;
|
|
len -= FLASH_WRITE_SIZE;
|
|
}
|
|
}
|
|
}
|
|
|
|
icc_enable();
|
|
return 0;
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
|
{
|
|
int ret;
|
|
volatile uint32_t *flc_base;
|
|
|
|
/* Align to page boundary */
|
|
if (address & (FLASH_PAGE_SIZE - 1)) {
|
|
address &= ~(FLASH_PAGE_SIZE - 1);
|
|
}
|
|
|
|
icc_disable();
|
|
|
|
while (len > 0) {
|
|
flc_base = flc_base_for_addr(address);
|
|
|
|
ret = flc_page_erase(address, flc_base);
|
|
if (ret != 0) {
|
|
icc_enable();
|
|
return ret;
|
|
}
|
|
|
|
address += FLASH_PAGE_SIZE;
|
|
len -= FLASH_PAGE_SIZE;
|
|
}
|
|
|
|
icc_enable();
|
|
return 0;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_unlock(void)
|
|
{
|
|
flc_unlock((volatile uint32_t*)FLC0_BASE);
|
|
flc_unlock((volatile uint32_t*)FLC1_BASE);
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_lock(void)
|
|
{
|
|
flc_lock((volatile uint32_t*)FLC0_BASE);
|
|
flc_lock((volatile uint32_t*)FLC1_BASE);
|
|
}
|