mirror of https://github.com/wolfSSL/wolfBoot.git
400 lines
11 KiB
C
400 lines
11 KiB
C
/* same51.c
|
|
*
|
|
* Copyright (C) 2024 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 "image.h"
|
|
#include "hal.h"
|
|
|
|
/*
|
|
* Clock settings for cpu same51 @ 120MHz
|
|
*/
|
|
#define CPU_FREQ (120000000)
|
|
|
|
|
|
/*
|
|
* Flash settings for same51
|
|
*/
|
|
#define FLASH_SIZE (1024 * 1024)
|
|
#define FLASH_PAGESIZE 512
|
|
#define FLASH_N_PAGES 4096
|
|
|
|
|
|
/*
|
|
* Oscillator controller
|
|
*/
|
|
#define OSCCTRL_BASE (0x40001000U)
|
|
|
|
/* Map only DPLL0 */
|
|
#define OSCCTRL_DPLL0CTRLA *((volatile uint32_t *)(OSCCTRL_BASE + 0x30))
|
|
#define OSCCTRL_DPLL0RATIO *((volatile uint32_t *)(OSCCTRL_BASE + 0x34))
|
|
#define OSCCTRL_DPLL0CTRLB *((volatile uint32_t *)(OSCCTRL_BASE + 0x38))
|
|
#define OSCCTRL_DPLL0SYNCBUSY *((volatile uint32_t *)(OSCCTRL_BASE + 0x3c))
|
|
#define OSCCTRL_DPLL0STATUS *((volatile uint32_t *)(OSCCTRL_BASE + 0x40))
|
|
|
|
#define DPLL0CTRLA_ENABLE (1u << 1)
|
|
|
|
#define DPLL0CTRLB_FILTER_MASK (0x0fu << 0)
|
|
#define DPLL0CTRLB_REFCLK_MASK (0x07u << 5)
|
|
#define DPLL0CTRLB_LTIME_MASK (0x07u << 8)
|
|
|
|
#define DPLL0RATIO_LDR_MASK (0x0fffu << 0)
|
|
#define DPLL0RATIO_LDRFRAC_MASK (0xfu << 16)
|
|
|
|
#define DPLL0SYNCBUSY_ENABLE (1u << 1)
|
|
#define DPLL0SYNCBUSY_RATIO (1u << 2)
|
|
|
|
#define DPLL0STATUS_LOCK (1u << 0)
|
|
#define DPLL0STATUS_CLKRDY (1u << 1)
|
|
|
|
/*
|
|
* 32KHz Oscillator controller
|
|
*/
|
|
#define OSC32KCTRL_BASE (0x4001400U)
|
|
#define OSC32KCTRL_RTCCTRL *((volatile uint32_t *)(OSC32KCTRL_BASE + 0x10))
|
|
#define RTCCTRL_RTCSEL_MASK (0x03)
|
|
|
|
/*
|
|
* Generic clock generator
|
|
*/
|
|
#define GCLK_BASE (0x40001C00)
|
|
#define GCLK_CTRLA *((volatile uint32_t *)(GCLK_BASE + 0x00))
|
|
#define GCLK_SYNCBUSY *((volatile uint32_t *)(GCLK_BASE + 0x04))
|
|
#define CTRLA_SWRST (1 << 0)
|
|
#define SYNCBUSY_SWRST (1 << 0)
|
|
#define SYNCBUSY_GENCTRL(x) (1 << (2 + x))
|
|
#define GCLK_IS_BUSY(x) ((GCLK_SYNCBUSY & (SYNCBUSY_GENCTRL(x))) != 0)
|
|
|
|
/* Array of 12 GCLK_GENCTRL[x] 32-bit registers */
|
|
#define GCLK_GENCTRL ((volatile uint32_t *)(GCLK_BASE + 0x20))
|
|
#define GENCTRLx_SRC_DFLL 0x06u
|
|
#define GENCTRLx_SRC_PLL0 0x07u
|
|
#define GENCTRLx_GENEN (1u << 8)
|
|
#define GENCTRLx_DIVSHIFT (16)
|
|
#define GENCTRLx_SRC_MASK (0x0Fu)
|
|
#define GENCTRLx_DIV_MASK (0xFFFFu << 16)
|
|
|
|
/* Array of 48 GCLK_PCHCTRLx */
|
|
#define GCLK_PCHCTRL ((volatile uint32_t *)(GCLK_BASE + 0x80))
|
|
#define PCHCTRLx_CHEN (1u << 6)
|
|
|
|
/* Main clock */
|
|
#define MCLK_BASE (0x40000800)
|
|
#define MCLK_CPUDIV *((volatile uint8_t *)(MCLK_BASE + 0x05))
|
|
#define MCLK_INTFLAG *((volatile uint8_t *)(MCLK_BASE + 0x03))
|
|
#define MCLK_AHBMASK *((volatile uint32_t *)(MCLK_BASE + 0x10))
|
|
#define MCLK_APBAMASK *((volatile uint32_t *)(MCLK_BASE + 0x14))
|
|
#define MCLK_APBBMASK *((volatile uint32_t *)(MCLK_BASE + 0x18))
|
|
#define MCLK_APBCMASK *((volatile uint32_t *)(MCLK_BASE + 0x1c))
|
|
#define MCLK_APBDMASK *((volatile uint32_t *)(MCLK_BASE + 0x20))
|
|
|
|
|
|
#define CKRDY (1u << 0)
|
|
|
|
/*
|
|
* Watchdog controller
|
|
*/
|
|
#define WDT_CTRL *((volatile uint8_t *)(0x40002000))
|
|
#define WDT_EN (1u << 1)
|
|
|
|
/* Peripheral access control
|
|
*
|
|
*/
|
|
#define PAC_BASE (0x41000000)
|
|
#define PAC_WRCTRL *((volatile uint32_t *)(PAC_BASE))
|
|
#define PAC_WRKEY_SET (2 << 16U)
|
|
#define PAC_WRKEY_CLEAR (1 << 16U)
|
|
#define PAC_PERID_NVMCTL ((32 * 1) + 2)
|
|
|
|
#define PAC_WR
|
|
|
|
/*
|
|
* NVM controller */
|
|
|
|
#define NVMCTRL_BASE (0x41004000)
|
|
#define NVMCTRLA *((volatile uint16_t *)(NVMCTRL_BASE))
|
|
#define NVMCTRLB *((volatile uint32_t *)(NVMCTRL_BASE + 0x04))
|
|
#define NVMCTRL_INTFLAG *((volatile uint16_t *)(NVMCTRL_BASE + 0x10))
|
|
#define NVMCTRL_STATUS *((volatile uint16_t *)(NVMCTRL_BASE + 0x12))
|
|
#define NVMCTRL_ADDR *((volatile uint32_t *)(NVMCTRL_BASE + 0x14))
|
|
#define NVMCTRL_SEESTAT *((volatile uint32_t *)(NVMCTRL_BASE + 0x2c))
|
|
|
|
|
|
/* Extra NVMCTRL options (unused: leaving default values) */
|
|
#define NVMCTRLA_DISABLE_CACHES (0xC000)
|
|
#define NVMCTRLA_RWS_MASK (0x0F00)
|
|
#define NVMCTRLA_RWS_SHIFT 8
|
|
#define NVMCTRLA_AUTOWS (1 << 2)
|
|
|
|
#define NVMCMD_KEY ((0xA5) << 8)
|
|
#define NVMCMD_ERASE_PAGE (0x00)
|
|
#define NVMCMD_ERASE_BLOCK (0x01)
|
|
#define NVMCMD_WP (0x03)
|
|
#define NVMCMD_WQW (0x04)
|
|
#define NVMCMD_PBC (0x15)
|
|
#define NVMCMD_SSB (0x16) /* Set secure bit */
|
|
#define NVMCMD_BKSWRST (0x17) /* Bank swap + reset */
|
|
#define NVMCTRL_INTFLAG_CMD_DONE (1)
|
|
|
|
#define NVMSTATUS_AFIRST (1 << 4)
|
|
|
|
volatile uint32_t psize, bsize;
|
|
|
|
/* Clock initialization */
|
|
static void clock_init(void)
|
|
{
|
|
uint32_t reg;
|
|
|
|
/* Prepare 32K oscillator */
|
|
OSC32KCTRL_RTCCTRL &= ~RTCCTRL_RTCSEL_MASK;
|
|
|
|
/* Set Generic Clock generator #2 */
|
|
reg = GCLK_GENCTRL[2];
|
|
|
|
/* - Clear clock and source selection */
|
|
reg &= ~(GENCTRLx_DIV_MASK | GENCTRLx_SRC_MASK);
|
|
|
|
/* - Select DFLL48M oscillator output as source
|
|
* - Set divisor to 48
|
|
* - Enable the generator
|
|
*/
|
|
GCLK_GENCTRL[2] = reg | GENCTRLx_SRC_DFLL | (48u << GENCTRLx_DIVSHIFT) |
|
|
GENCTRLx_GENEN;
|
|
|
|
while(GCLK_IS_BUSY(2))
|
|
;
|
|
|
|
/* Connect peripheral '1' (FDPLL0) to clock generator '2' and enable */
|
|
reg = GCLK_PCHCTRL[1] & (~ 0x0F);
|
|
GCLK_PCHCTRL[1] = reg | 0x02 | PCHCTRLx_CHEN;
|
|
|
|
/* Wait until enabled */
|
|
while ((GCLK_PCHCTRL[1] & PCHCTRLx_CHEN) == 0)
|
|
;
|
|
|
|
/* Reset PLL parameters */
|
|
OSCCTRL_DPLL0CTRLB = 0;
|
|
|
|
/* Set PLL ratio LDR */
|
|
OSCCTRL_DPLL0RATIO = (120 - 1);
|
|
|
|
/* Wait until ratio has been set */
|
|
while ((OSCCTRL_DPLL0SYNCBUSY & DPLL0SYNCBUSY_RATIO) != 0)
|
|
;
|
|
|
|
/* Enable PLL */
|
|
OSCCTRL_DPLL0CTRLA = DPLL0CTRLA_ENABLE;
|
|
|
|
/* Wait until the PLL is enabled */
|
|
while ((OSCCTRL_DPLL0SYNCBUSY & DPLL0SYNCBUSY_ENABLE) != 0)
|
|
;
|
|
|
|
/* Wait for final lock + clock ready */
|
|
while ((OSCCTRL_DPLL0STATUS & (DPLL0STATUS_LOCK | DPLL0STATUS_CLKRDY)) !=
|
|
(DPLL0STATUS_LOCK | DPLL0STATUS_CLKRDY) )
|
|
;
|
|
|
|
/* Set main clock divisor */
|
|
MCLK_CPUDIV = 0x01u;
|
|
|
|
/* Wait until ready */
|
|
while ((MCLK_INTFLAG & CKRDY) == 0)
|
|
;
|
|
|
|
/* generic clock generator #0: set source to FDPLL200M0, div 1 and enable */
|
|
reg = GCLK_GENCTRL[0];
|
|
reg &= ~(GENCTRLx_SRC_MASK | GENCTRLx_DIV_MASK);
|
|
GCLK_GENCTRL[0] = reg | GENCTRLx_SRC_PLL0 | (1 << GENCTRLx_DIVSHIFT) |
|
|
GENCTRLx_GENEN;
|
|
while (GCLK_IS_BUSY(0))
|
|
;
|
|
|
|
/* generic clock generator #1: set source to FDPLL200M0, div 2 and enable */
|
|
reg = GCLK_GENCTRL[1];
|
|
reg &= ~(GENCTRLx_SRC_MASK | GENCTRLx_DIV_MASK);
|
|
GCLK_GENCTRL[1] = reg | GENCTRLx_SRC_PLL0 | (2 << GENCTRLx_DIVSHIFT) |
|
|
GENCTRLx_GENEN;
|
|
while (GCLK_IS_BUSY(1))
|
|
;
|
|
|
|
/* select clock generator for EIC */
|
|
reg = GCLK_PCHCTRL[4] & (~0x0F);
|
|
GCLK_PCHCTRL[4] = reg | 0x01 | PCHCTRLx_CHEN;
|
|
|
|
/* Wait until enabled */
|
|
while ((GCLK_PCHCTRL[4] & PCHCTRLx_CHEN) == 0)
|
|
;
|
|
|
|
/* select clock generator for SERCOM5_CORE */
|
|
reg = GCLK_PCHCTRL[35] & (~0x0F);
|
|
GCLK_PCHCTRL[35] = reg | 0x01 | PCHCTRLx_CHEN;
|
|
|
|
/* wait until enabled */
|
|
while ((GCLK_PCHCTRL[35] & PCHCTRLx_CHEN) == 0)
|
|
;
|
|
|
|
}
|
|
|
|
|
|
#ifdef __WOLFBOOT
|
|
#ifdef DUALBANK_SWAP
|
|
#define BANKA_BASE 0x00000000
|
|
#define BANKB_BASE 0x00080000
|
|
|
|
#include <string.h>
|
|
|
|
static void RAMFUNCTION fork_bootloader(void)
|
|
{
|
|
uint32_t r;
|
|
uint32_t len = (uint32_t)(WOLFBOOT_PARTITION_BOOT_ADDRESS - BANKA_BASE);
|
|
if (memcmp((void *)BANKA_BASE, (void *)BANKB_BASE, len) == 0)
|
|
return;
|
|
hal_flash_unlock();
|
|
hal_flash_erase(BANKB_BASE, len);
|
|
for (r = 0; r < len; r += WOLFBOOT_SECTOR_SIZE) {
|
|
hal_flash_write(BANKB_BASE + r, (void *)(BANKA_BASE + r), WOLFBOOT_SECTOR_SIZE);
|
|
}
|
|
hal_flash_lock();
|
|
}
|
|
#endif /* DUALBANK_SWAP */
|
|
|
|
void hal_init(void)
|
|
{
|
|
/* Turn off watchdog */
|
|
WDT_CTRL &= (~WDT_EN);
|
|
/* Run the bootloader with interrupts off */
|
|
__asm__ volatile ("cpsid i");
|
|
|
|
/* Initialize clock */
|
|
clock_init();
|
|
|
|
/* enable all the AHB clocks */
|
|
MCLK_AHBMASK = 0xffffffU;
|
|
|
|
/* Enable flash memory controller via APBB */
|
|
MCLK_APBBMASK |= (1 << 2);
|
|
|
|
/* enable all the APBA clocks */
|
|
MCLK_APBAMASK = 0x7ffU;
|
|
|
|
/* enable all the APBD clocks */
|
|
MCLK_APBDMASK = 0x2U;
|
|
|
|
#ifdef DUALBANK_SWAP
|
|
fork_bootloader();
|
|
#endif
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_dualbank_swap(void)
|
|
{
|
|
hal_flash_unlock();
|
|
NVMCTRLB = NVMCMD_BKSWRST | NVMCMD_KEY;
|
|
|
|
/* Next loop should never be reached: system is restarted */
|
|
while(!(NVMCTRL_INTFLAG & NVMCTRL_INTFLAG_CMD_DONE))
|
|
;
|
|
while( 1 )
|
|
;
|
|
}
|
|
|
|
|
|
void RAMFUNCTION hal_prepare_boot(void)
|
|
{
|
|
/* Reset clock controller */
|
|
GCLK_CTRLA |= CTRLA_SWRST;
|
|
|
|
/* Wait until reset is complete */
|
|
while ((GCLK_SYNCBUSY & SYNCBUSY_SWRST) != 0)
|
|
;
|
|
|
|
/* Disable PLL */
|
|
OSCCTRL_DPLL0CTRLA = 0;
|
|
/* Wait until the PLL is enabled */
|
|
while ((OSCCTRL_DPLL0SYNCBUSY & DPLL0SYNCBUSY_ENABLE) != 0)
|
|
;
|
|
|
|
/* Clear PLL options */
|
|
OSCCTRL_DPLL0CTRLB = 0;
|
|
}
|
|
|
|
#endif /* __WOLFBOOT */
|
|
|
|
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|
{
|
|
int i = 0;
|
|
uint32_t *src, *dst;
|
|
|
|
if (len <= 0)
|
|
return 0;
|
|
|
|
/* Clear page buffer */
|
|
NVMCTRLB = (NVMCMD_PBC | NVMCMD_KEY);
|
|
while (i < len) {
|
|
if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) {
|
|
dst = (uint32_t *)address;
|
|
src = (uint32_t *)data;
|
|
dst[i >> 2] = src[i >> 2];
|
|
i+=4;
|
|
} else {
|
|
uint32_t val;
|
|
uint8_t *vbytes = (uint8_t *)(&val);
|
|
uint32_t off = (address % 4);
|
|
dst = (uint32_t *)(address - off);
|
|
uint32_t dst_idx = (i + off) >> 2;
|
|
val = dst[dst_idx];
|
|
while (off < 4) {
|
|
if (i < len)
|
|
vbytes[off++] = data[i++];
|
|
else
|
|
off++;
|
|
}
|
|
dst[dst_idx] = val;
|
|
}
|
|
if ((i == len) || ((i % 16)== 0))
|
|
NVMCTRLB = (NVMCMD_WQW | NVMCMD_KEY);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_unlock(void)
|
|
{
|
|
PAC_WRCTRL = PAC_WRKEY_CLEAR | PAC_PERID_NVMCTL;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_lock(void)
|
|
{
|
|
PAC_WRCTRL = PAC_WRKEY_SET | PAC_PERID_NVMCTL;
|
|
}
|
|
|
|
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
|
{
|
|
while (len > 0) {
|
|
NVMCTRL_ADDR = (address);
|
|
NVMCTRLB = NVMCMD_ERASE_BLOCK | NVMCMD_KEY;
|
|
while(!(NVMCTRL_INTFLAG & NVMCTRL_INTFLAG_CMD_DONE))
|
|
;
|
|
len -= WOLFBOOT_SECTOR_SIZE;
|
|
address += WOLFBOOT_SECTOR_SIZE;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|