mirror of https://github.com/wolfSSL/wolfBoot.git
1148 lines
37 KiB
C
1148 lines
37 KiB
C
/* psoc6.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
|
|
*/
|
|
|
|
/* Standard library includes */
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
/* Project includes */
|
|
#include "image.h"
|
|
#include "printf.h"
|
|
#include "psoc6_02_config.h"
|
|
#include <target.h>
|
|
|
|
#ifdef WOLFSSL_PSOC6_CRYPTO
|
|
#include "wolfssl/wolfcrypt/port/cypress/psoc6_crypto.h"
|
|
#endif
|
|
|
|
#include "cy_flash.h"
|
|
#include "cy_sysclk.h"
|
|
#include "cy_syslib.h"
|
|
|
|
#define EMULATED_EEPROM_START (CY_EM_EEPROM_BASE)
|
|
#define ROW_SIZE (CY_FLASH_SIZEOF_ROW)
|
|
#define FLASH_SUBSECTOR_SIZE (CY_FLASH_SIZEOF_ROW * 8)
|
|
#define FLASH_SECTOR_SIZE (CY_FLASH_SIZEOF_ROW * CPUSS_FLASHC_SONOS_MAIN_ROWS)
|
|
#define FLASH_BASE_ADDRESS (CY_FLASH_BASE)
|
|
#define FLASH_SIZE (CY_FLASH_SIZE)
|
|
#define CPU_FREQ (100000000)
|
|
|
|
/* Magic number constants */
|
|
#define EXT_FLASH_TMP_MAGIC (0x4C465845U) /* 'EXFL' */
|
|
#define BANKMODE_MAGIC (0x4B424C44U) /* 'DLBK' */
|
|
#define EXT_FLASH_WORD_ERASED_VALUE (0xFFFFFFFFU)
|
|
|
|
#ifdef EXT_FLASH
|
|
|
|
static cy_stc_smif_context_t smif_context;
|
|
|
|
/*
|
|
* SFDP-based external memory configuration (self-contained, no BSP generated
|
|
* source dependency).
|
|
*/
|
|
static cy_stc_smif_mem_cmd_t sfdp_cmd = {
|
|
.command = 0x5Au,
|
|
.cmdWidth = CY_SMIF_WIDTH_SINGLE,
|
|
.addrWidth = CY_SMIF_WIDTH_SINGLE,
|
|
.mode = 0xFFFFFFFFU,
|
|
.dummyCycles = 8u,
|
|
.dataWidth = CY_SMIF_WIDTH_SINGLE,
|
|
};
|
|
|
|
/* Zero-initialized command structs — populated at runtime by Cy_SMIF_MemInit()
|
|
* after reading the device's SFDP table */
|
|
static cy_stc_smif_mem_cmd_t rdcmd0;
|
|
static cy_stc_smif_mem_cmd_t wrencmd0;
|
|
static cy_stc_smif_mem_cmd_t wrdiscmd0;
|
|
static cy_stc_smif_mem_cmd_t erasecmd0;
|
|
static cy_stc_smif_mem_cmd_t chiperasecmd0;
|
|
static cy_stc_smif_mem_cmd_t pgmcmd0;
|
|
static cy_stc_smif_mem_cmd_t readsts0;
|
|
static cy_stc_smif_mem_cmd_t readstsqecmd0;
|
|
static cy_stc_smif_mem_cmd_t writestseqcmd0;
|
|
|
|
static cy_stc_smif_mem_device_cfg_t dev_sfdp_0 = {
|
|
.numOfAddrBytes = 4u,
|
|
.readSfdpCmd = &sfdp_cmd,
|
|
.readCmd = &rdcmd0,
|
|
.writeEnCmd = &wrencmd0,
|
|
.writeDisCmd = &wrdiscmd0,
|
|
.programCmd = &pgmcmd0,
|
|
.eraseCmd = &erasecmd0,
|
|
.chipEraseCmd = &chiperasecmd0,
|
|
.readStsRegWipCmd = &readsts0,
|
|
.readStsRegQeCmd = &readstsqecmd0,
|
|
.writeStsRegQeCmd = &writestseqcmd0,
|
|
};
|
|
|
|
static cy_stc_smif_mem_config_t mem_sfdp_0 = {
|
|
.baseAddress = CY_XIP_BASE,
|
|
.memMappedSize = CY_XIP_SIZE,
|
|
.flags = CY_SMIF_FLAG_DETECT_SFDP | CY_SMIF_FLAG_MEMORY_MAPPED,
|
|
.slaveSelect = CY_SMIF_SLAVE_SELECT_0,
|
|
.dataSelect = CY_SMIF_DATA_SEL0,
|
|
.deviceCfg = &dev_sfdp_0,
|
|
};
|
|
|
|
static cy_stc_smif_mem_config_t* mems_sfdp[1] = {
|
|
&mem_sfdp_0,
|
|
};
|
|
|
|
static cy_stc_smif_block_config_t smifBlockConfig_sfdp = {
|
|
.memCount = 1u,
|
|
.memConfig = mems_sfdp,
|
|
};
|
|
|
|
static cy_stc_smif_block_config_t* smif_blk_config;
|
|
|
|
#define TMP_SECTOR_INFO_ADDR EMULATED_EEPROM_START
|
|
|
|
/**
|
|
* Address of the temporary sector in external flash.
|
|
*
|
|
* External flash has a larger minimum erase size (determined at runtime via
|
|
* SFDP, e.g. 0x40000 bytes for S25FL512S) than internal flash rows. Since
|
|
* read-modify-erase-write cannot use SRAM as a temporary buffer due to size
|
|
* constraints, we use a dedicated sector in external flash for temporary
|
|
* storage during sector updates.
|
|
*/
|
|
#define SWAP_TMP_SECTOR_ADDR (WOLFBOOT_PARTITION_UPDATE_ADDRESS + CY_FLASH_SIZE)
|
|
|
|
typedef struct tmp_sector_info {
|
|
uint32_t magic;
|
|
uint32_t sector_address;
|
|
} tmp_sector_info;
|
|
|
|
static SMIF_Type* QSPIPort = SMIF0;
|
|
|
|
/*
|
|
* QSPI / SMIF hardware configuration
|
|
*
|
|
* SS0 - P11_2 D3 - P11_3 D2 - P11_4
|
|
* D1 - P11_5 D0 - P11_6 SCK - P11_7
|
|
*
|
|
* SMIF Block - SMIF0
|
|
*/
|
|
#define CY_SMIF_SYSCLK_HFCLK_DIVIDER (CY_SYSCLK_CLKHF_DIVIDE_BY_4)
|
|
#define CY_SMIF_INIT_TRY_COUNT (10U)
|
|
#define CY_SMIF_INIT_RETRY_DELAY_MS (500U)
|
|
#define CY_SMIF_INIT_TIMEOUT_US (1000U)
|
|
|
|
/*
|
|
* SMIF SlaveSelect configuration — only SS0 is used here.
|
|
* Available slave select options
|
|
* SS0 - P11_2 (P11_2_SMIF_SPI_SELECT0) — default, external QSPI flash
|
|
* SS1 - P11_1 (P11_1_SMIF_SPI_SELECT1)
|
|
* SS2 - P11_0 (P11_0_SMIF_SPI_SELECT2)
|
|
* SS3 - P12_4 (P12_4_SMIF_SPI_SELECT3) — only on 1M/2M packages
|
|
*/
|
|
static GPIO_PRT_Type* const qspi_SS_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_SS_Pin = 2u;
|
|
static const en_hsiom_sel_t qspi_SS_Mux = P11_2_SMIF_SPI_SELECT0;
|
|
|
|
static GPIO_PRT_Type* const qspi_D3_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_D3_Pin = 3u;
|
|
static const en_hsiom_sel_t qspi_D3_Mux = P11_3_SMIF_SPI_DATA3;
|
|
|
|
static GPIO_PRT_Type* const qspi_D2_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_D2_Pin = 4u;
|
|
static const en_hsiom_sel_t qspi_D2_Mux = P11_4_SMIF_SPI_DATA2;
|
|
|
|
static GPIO_PRT_Type* const qspi_D1_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_D1_Pin = 5u;
|
|
static const en_hsiom_sel_t qspi_D1_Mux = P11_5_SMIF_SPI_DATA1;
|
|
|
|
static GPIO_PRT_Type* const qspi_D0_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_D0_Pin = 6u;
|
|
static const en_hsiom_sel_t qspi_D0_Mux = P11_6_SMIF_SPI_DATA0;
|
|
|
|
static GPIO_PRT_Type* const qspi_SCK_Port = GPIO_PRT11;
|
|
static const uint32_t qspi_SCK_Pin = 7u;
|
|
static const en_hsiom_sel_t qspi_SCK_Mux = P11_7_SMIF_SPI_CLK;
|
|
|
|
/* SMIF GPIO pin configurations */
|
|
static cy_stc_gpio_pin_config_t qspi_ss_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
|
|
.hsiom = P11_2_SMIF_SPI_SELECT0,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
static const cy_stc_gpio_pin_config_t qspi_d3_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG,
|
|
.hsiom = P11_3_SMIF_SPI_DATA3,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
static const cy_stc_gpio_pin_config_t qspi_d2_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG,
|
|
.hsiom = P11_4_SMIF_SPI_DATA2,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
static const cy_stc_gpio_pin_config_t qspi_d1_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG,
|
|
.hsiom = P11_5_SMIF_SPI_DATA1,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
static const cy_stc_gpio_pin_config_t qspi_d0_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG,
|
|
.hsiom = P11_6_SMIF_SPI_DATA0,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
static const cy_stc_gpio_pin_config_t qspi_sck_config = {
|
|
.outVal = 1,
|
|
.driveMode = CY_GPIO_DM_STRONG_IN_OFF,
|
|
.hsiom = P11_7_SMIF_SPI_CLK,
|
|
.intEdge = CY_GPIO_INTR_DISABLE,
|
|
.intMask = 0UL,
|
|
.vtrip = CY_GPIO_VTRIP_CMOS,
|
|
.slewRate = CY_GPIO_SLEW_FAST,
|
|
.driveSel = CY_GPIO_DRIVE_1_2,
|
|
.vregEn = 0UL,
|
|
.ibufMode = 0UL,
|
|
.vtripSel = 0UL,
|
|
.vrefSel = 0UL,
|
|
.vohSel = 0UL,
|
|
};
|
|
|
|
/* SMIF block configuration */
|
|
static const cy_stc_smif_config_t QSPI_config = {
|
|
.mode = (uint32_t)CY_SMIF_NORMAL,
|
|
.deselectDelay = 1,
|
|
.rxClockSel = (uint32_t)CY_SMIF_SEL_INV_INTERNAL_CLK,
|
|
.blockEvent = (uint32_t)CY_SMIF_BUS_ERROR,
|
|
};
|
|
|
|
#if CY_CPU_CORTEX_M0P
|
|
/* CM0+ SMIF interrupt configuration */
|
|
static cy_stc_sysint_t smifIntConfig = {
|
|
/* ATTENTION: make sure proper Interrupts configured for CM0p or M4 cores */
|
|
.intrSrc = NvicMux7_IRQn,
|
|
.cm0pSrc = smif_interrupt_IRQn,
|
|
.intrPriority = 1,
|
|
};
|
|
#endif
|
|
|
|
#if CY_CPU_CORTEX_M0P
|
|
static void Isr_SMIF(void)
|
|
{
|
|
Cy_SMIF_Interrupt(QSPIPort, &smif_context);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* @brief Initialize external QSPI flash hardware
|
|
*
|
|
* Configures GPIO pins, SMIF clock, interrupt, and initializes the SMIF
|
|
* block and external memory device.
|
|
*
|
|
* Initialization steps:
|
|
* 1. Configure QSPI data, clock, and slave-select GPIO pins
|
|
* 2. Enable SMIF HF clock (CLK_HF2) with divide-by-4
|
|
* 3. Setup SMIF interrupt for CM0+
|
|
* 4. Initialize SMIF block in normal mode
|
|
* 5. Enable SMIF block
|
|
* 6. Initialize external memory configuration with retry
|
|
*
|
|
* @return CY_SMIF_SUCCESS on success, error code on failure
|
|
*/
|
|
cy_en_smif_status_t hal_ext_flash_init(void)
|
|
{
|
|
cy_en_smif_status_t st;
|
|
uint32_t try_count;
|
|
|
|
/* Step 1: Configure QSPI GPIO pins */
|
|
(void)Cy_GPIO_Pin_Init(qspi_D3_Port, qspi_D3_Pin, &qspi_d3_config);
|
|
Cy_GPIO_SetHSIOM(qspi_D3_Port, qspi_D3_Pin, qspi_D3_Mux);
|
|
|
|
(void)Cy_GPIO_Pin_Init(qspi_D2_Port, qspi_D2_Pin, &qspi_d2_config);
|
|
Cy_GPIO_SetHSIOM(qspi_D2_Port, qspi_D2_Pin, qspi_D2_Mux);
|
|
|
|
(void)Cy_GPIO_Pin_Init(qspi_D1_Port, qspi_D1_Pin, &qspi_d1_config);
|
|
Cy_GPIO_SetHSIOM(qspi_D1_Port, qspi_D1_Pin, qspi_D1_Mux);
|
|
|
|
(void)Cy_GPIO_Pin_Init(qspi_D0_Port, qspi_D0_Pin, &qspi_d0_config);
|
|
Cy_GPIO_SetHSIOM(qspi_D0_Port, qspi_D0_Pin, qspi_D0_Mux);
|
|
|
|
(void)Cy_GPIO_Pin_Init(qspi_SCK_Port, qspi_SCK_Pin, &qspi_sck_config);
|
|
Cy_GPIO_SetHSIOM(qspi_SCK_Port, qspi_SCK_Pin, qspi_SCK_Mux);
|
|
|
|
/* Configure SS pin */
|
|
qspi_ss_config.hsiom = qspi_SS_Mux;
|
|
(void)Cy_GPIO_Pin_Init(qspi_SS_Port, qspi_SS_Pin, &qspi_ss_config);
|
|
Cy_GPIO_SetHSIOM(qspi_SS_Port, qspi_SS_Pin, qspi_SS_Mux);
|
|
|
|
/* Step 2: Enable SMIF HF clock (CLK_HF2) sourced from CLK_PATH0 */
|
|
(void)Cy_SysClk_ClkHfSetSource(CY_SYSCLK_CLKHF_IN_CLKPATH2,
|
|
CY_SYSCLK_CLKHF_IN_CLKPATH0);
|
|
(void)Cy_SysClk_ClkHfSetDivider(CY_SYSCLK_CLKHF_IN_CLKPATH2,
|
|
CY_SMIF_SYSCLK_HFCLK_DIVIDER);
|
|
(void)Cy_SysClk_ClkHfEnable(CY_SYSCLK_CLKHF_IN_CLKPATH2);
|
|
|
|
/* Step 3: Setup the interrupt for the SMIF block. For the CM0+ there
|
|
* is a two stage process to setup the interrupts. */
|
|
#if CY_CPU_CORTEX_M0P
|
|
(void)Cy_SysInt_Init(&smifIntConfig, Isr_SMIF);
|
|
#endif
|
|
|
|
/* Step 4: Initialize SMIF block */
|
|
st = Cy_SMIF_Init(QSPIPort, &QSPI_config, CY_SMIF_INIT_TIMEOUT_US,
|
|
&smif_context);
|
|
if (st != CY_SMIF_SUCCESS) {
|
|
return st;
|
|
}
|
|
|
|
#if CY_CPU_CORTEX_M0P
|
|
NVIC_EnableIRQ(
|
|
smifIntConfig.intrSrc); /* Finally, Enable the SMIF interrupt */
|
|
#endif
|
|
|
|
/* Step 5: Enable SMIF block */
|
|
Cy_SMIF_Enable(QSPIPort, &smif_context);
|
|
|
|
/* Step 6: Initialize external memory via SFDP with retry */
|
|
smif_blk_config = &smifBlockConfig_sfdp;
|
|
try_count = CY_SMIF_INIT_TRY_COUNT;
|
|
do {
|
|
st = Cy_SMIF_MemInit(QSPIPort, smif_blk_config, &smif_context);
|
|
try_count--;
|
|
if (st != CY_SMIF_SUCCESS) {
|
|
Cy_SysLib_Delay(CY_SMIF_INIT_RETRY_DELAY_MS);
|
|
}
|
|
} while ((st != CY_SMIF_SUCCESS) && (try_count > 0U));
|
|
|
|
return st;
|
|
}
|
|
|
|
/**
|
|
* @brief Deinitialize external QSPI flash hardware
|
|
*
|
|
* Reverses the initialization performed by hal_ext_flash_init().
|
|
* Deinitializes SMIF memory, disables the SMIF block, disables the
|
|
* HF clock, disconnects interrupts (on CM0+), and deinitializes
|
|
* GPIO ports. Matches mcuboot's qspi_deinit() sequence.
|
|
*/
|
|
static void hal_ext_flash_deinit(void)
|
|
{
|
|
Cy_SMIF_MemDeInit(QSPIPort);
|
|
Cy_SMIF_Disable(QSPIPort);
|
|
|
|
(void)Cy_SysClk_ClkHfDisable(CY_SYSCLK_CLKHF_IN_CLKPATH2);
|
|
|
|
#if CY_CPU_CORTEX_M0P
|
|
NVIC_DisableIRQ(smifIntConfig.intrSrc);
|
|
Cy_SysInt_DisconnectInterruptSource(smifIntConfig.intrSrc,
|
|
smifIntConfig.cm0pSrc);
|
|
#endif
|
|
|
|
Cy_GPIO_Port_Deinit(qspi_SS_Port);
|
|
Cy_GPIO_Port_Deinit(qspi_SCK_Port);
|
|
Cy_GPIO_Port_Deinit(qspi_D0_Port);
|
|
Cy_GPIO_Port_Deinit(qspi_D1_Port);
|
|
Cy_GPIO_Port_Deinit(qspi_D2_Port);
|
|
Cy_GPIO_Port_Deinit(qspi_D3_Port);
|
|
}
|
|
|
|
#endif /* EXT_FLASH */
|
|
|
|
#ifdef DUALBANK_SWAP
|
|
|
|
#define FLASH_BANK1_BASE (0x10000000) /* Base address of Flash Bank1 */
|
|
#define FLASH_BANK2_BASE (0x12000000) /* Base address of Flash Bank2 */
|
|
|
|
typedef struct active_bank {
|
|
uint32_t magic;
|
|
uint32_t mapping; /* Changed from cy_en_maptype_t to uint32_t for 32-bit
|
|
alignment */
|
|
} active_bank;
|
|
|
|
/* EXT_FLASH and DUALBANK_SWAP are mutually exclusive, so its safe to overwrite
|
|
* the data at same address. */
|
|
#define ACTIVE_BANK_MODE_DATA_ADDRESS (EMULATED_EEPROM_START)
|
|
|
|
int RAMFUNCTION bankmode_init(void);
|
|
#endif
|
|
|
|
/* Global write buffer to avoid stack allocation and enable RAMFUNCTION usage */
|
|
static uint8_t psoc6_write_buffer[ROW_SIZE];
|
|
|
|
#ifdef __WOLFBOOT
|
|
static const cy_stc_pll_manual_config_t srss_0_clock_0_pll_0_pllConfig = {
|
|
.feedbackDiv = 100,
|
|
.referenceDiv = 2,
|
|
.outputDiv = 4,
|
|
.lfMode = false,
|
|
.outputMode = CY_SYSCLK_FLLPLL_OUTPUT_AUTO,
|
|
};
|
|
|
|
/**
|
|
* @brief Configure PLL for 100 MHz system clock
|
|
*
|
|
* Sets up the Phase-Locked Loop (PLL) to generate a 100 MHz clock from
|
|
* the Internal Main Oscillator (IMO). Configures clock paths and dividers
|
|
* for CM0+, CM4, and peripheral clocks.
|
|
*
|
|
* Clock configuration:
|
|
* - Clock path 1 source: IMO (8 MHz)
|
|
* - PLL output: 100 MHz (8 MHz * 100 / 2 / 4 = 100 MHz)
|
|
* - CLK_HF0: PLL output (100 MHz)
|
|
* - CM4 (fast clock): No division (100 MHz)
|
|
* - Peripheral clock: No division (100 MHz)
|
|
* - CM0+ (slow clock): No division (100 MHz)
|
|
*
|
|
* @note This function will hang in an infinite loop if PLL configuration
|
|
* or enable fails.
|
|
*/
|
|
static void hal_set_pll(void)
|
|
{
|
|
/*Set clock path 1 source to IMO, this feeds PLL1*/
|
|
Cy_SysClk_ClkPathSetSource(1U, CY_SYSCLK_CLKPATH_IN_IMO);
|
|
|
|
/*Set the input for CLK_HF0 to the output of the PLL, which is on clock path
|
|
* 1*/
|
|
Cy_SysClk_ClkHfSetSource(0U, CY_SYSCLK_CLKHF_IN_CLKPATH1);
|
|
Cy_SysClk_ClkHfSetDivider(0U, CY_SYSCLK_CLKHF_NO_DIVIDE);
|
|
|
|
/*Set divider for CM4 clock to 0, might be able to lower this to save power
|
|
* if needed*/
|
|
Cy_SysClk_ClkFastSetDivider(0U);
|
|
|
|
/*Set divider for peripheral and CM0 clock to 0 - This must be 0 to get
|
|
* fastest clock to CM0*/
|
|
Cy_SysClk_ClkPeriSetDivider(0U);
|
|
|
|
/*Set divider for CM0 clock to 0*/
|
|
Cy_SysClk_ClkSlowSetDivider(0U);
|
|
|
|
/*Set flash memory wait states */
|
|
Cy_SysLib_SetWaitStates(false, 100);
|
|
|
|
/*Configure PLL for 100 MHz*/
|
|
if (CY_SYSCLK_SUCCESS !=
|
|
Cy_SysClk_PllManualConfigure(1U, &srss_0_clock_0_pll_0_pllConfig)) {
|
|
while (1)
|
|
;
|
|
}
|
|
/*Enable PLL*/
|
|
if (CY_SYSCLK_SUCCESS != Cy_SysClk_PllEnable(1U, 10000u)) {
|
|
while (1)
|
|
;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize hardware abstraction layer
|
|
*
|
|
* Performs essential hardware initialization for the PSoC6 bootloader:
|
|
* 1. Sets the Vector Table Offset Register (VTOR) to flash base address
|
|
* 2. Initializes Cypress PDL (Peripheral Driver Library)
|
|
* 3. Initializes the flash subsystem for program/erase operations
|
|
* 4. Configures PLL for 100 MHz system clock
|
|
* 5. Initializes crypto hardware acceleration (if WOLFSSL_PSOC6_CRYPTO enabled)
|
|
*
|
|
* @note Must be called early in the boot process before any flash operations
|
|
* or cryptographic functions.
|
|
*/
|
|
void hal_init(void)
|
|
{
|
|
#define VTOR (*(volatile uint32_t*)(0xE000ED08))
|
|
VTOR = FLASH_BASE_ADDRESS;
|
|
#undef VTOR
|
|
|
|
#ifdef DUALBANK_SWAP
|
|
bankmode_init();
|
|
#endif
|
|
|
|
Cy_PDL_Init(CY_DEVICE_CFG);
|
|
Cy_Flash_Init();
|
|
hal_set_pll();
|
|
|
|
#ifdef EXT_FLASH
|
|
if (hal_ext_flash_init() != CY_SMIF_SUCCESS) {
|
|
wolfBoot_printf("hal_init: QSPI ext flash init failed\n");
|
|
while (1)
|
|
;
|
|
}
|
|
#endif
|
|
|
|
#ifdef WOLFSSL_PSOC6_CRYPTO
|
|
psoc6_crypto_port_init();
|
|
#endif
|
|
}
|
|
|
|
void hal_prepare_boot(void)
|
|
{
|
|
#ifdef EXT_FLASH
|
|
hal_ext_flash_deinit();
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
/**
|
|
* @brief Write data to internal flash memory
|
|
*
|
|
* Writes data to the PSoC6 internal flash with automatic handling of
|
|
* unaligned addresses and partial row writes. Uses read-modify-write
|
|
* for rows that are not fully overwritten.
|
|
*
|
|
* @param address Flash address to write to (must be >= FLASH_BASE_ADDRESS)
|
|
* @param data Pointer to source data buffer
|
|
* @param len Number of bytes to write (must be > 0)
|
|
*
|
|
* @return 0 on success, -1 on invalid parameters, or PDL error code on
|
|
* flash programming failure
|
|
*
|
|
* @note Uses global psoc6_write_buffer for row operations to avoid stack
|
|
* allocation.
|
|
* @note For optimal performance, align writes to ROW_SIZE (512 bytes)
|
|
* boundaries.
|
|
*/
|
|
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t* data, int len)
|
|
{
|
|
uint32_t write_len;
|
|
uint32_t row_start = address & ~(ROW_SIZE - 1);
|
|
int ret = 0;
|
|
uint32_t offset = address - row_start;
|
|
|
|
/* Validate input parameters (address checked against base, PDL handles bank
|
|
* mapping) */
|
|
if (!data || (len <= 0) || (address < FLASH_BASE_ADDRESS))
|
|
return -1;
|
|
|
|
/* Handle unaligned start address with read-modify-write */
|
|
if (address != row_start) {
|
|
/* Read existing row from flash */
|
|
memcpy(psoc6_write_buffer, (const void*)row_start, ROW_SIZE);
|
|
|
|
/* Merge new data at offset, limiting to row boundary */
|
|
write_len = ((ROW_SIZE - offset) < (uint32_t)len) ? (ROW_SIZE - offset)
|
|
: (uint32_t)len;
|
|
memcpy(&psoc6_write_buffer[offset], data, write_len);
|
|
|
|
/* Program the updated data into flash row */
|
|
ret =
|
|
Cy_Flash_ProgramRow(row_start, (const uint32_t*)psoc6_write_buffer);
|
|
if (ret) {
|
|
wolfBoot_printf(
|
|
"hal_flash_write: ProgramRow failed at 0x%08X ret=%d\n",
|
|
row_start, ret);
|
|
return ret;
|
|
}
|
|
|
|
row_start += ROW_SIZE;
|
|
data += write_len;
|
|
len -= write_len;
|
|
}
|
|
|
|
/* Write remaining rows (row-aligned) */
|
|
while (len > 0) {
|
|
/* Choose write length for this row */
|
|
write_len = ((uint32_t)len >= ROW_SIZE) ? ROW_SIZE : (uint32_t)len;
|
|
|
|
if (write_len == ROW_SIZE) {
|
|
/* Full-row write - avoid unnecessary read */
|
|
memcpy(psoc6_write_buffer, data, ROW_SIZE);
|
|
}
|
|
else {
|
|
/* Read-modify-write for partial row support */
|
|
memcpy(psoc6_write_buffer, (const void*)row_start, ROW_SIZE);
|
|
memcpy(psoc6_write_buffer, data, write_len);
|
|
}
|
|
|
|
/* Program the updated data into flash row */
|
|
ret =
|
|
Cy_Flash_ProgramRow(row_start, (const uint32_t*)psoc6_write_buffer);
|
|
if (ret) {
|
|
wolfBoot_printf(
|
|
"hal_flash_write: ProgramRow failed at 0x%08X ret=%d\n",
|
|
row_start, ret);
|
|
return ret;
|
|
}
|
|
|
|
row_start += ROW_SIZE;
|
|
data += write_len;
|
|
len -= (int)write_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_unlock(void)
|
|
{
|
|
}
|
|
|
|
void RAMFUNCTION hal_flash_lock(void)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* @brief Erase internal flash memory with optimized erase size selection
|
|
*
|
|
* Erases flash memory by automatically selecting the most efficient erase
|
|
* operation based on address alignment and remaining length:
|
|
* - Sector erase (largest): When aligned to FLASH_SECTOR_SIZE and len >= sector
|
|
* - Subsector erase (medium): When aligned to FLASH_SUBSECTOR_SIZE and len >=
|
|
* subsector
|
|
* - Row erase (smallest): For unaligned addresses or small regions
|
|
*
|
|
* @param address Starting flash address (must be row-aligned and >=
|
|
* FLASH_BASE_ADDRESS)
|
|
* @param len Number of bytes to erase (must be > 0)
|
|
*
|
|
* @return 0 on success, -1 on invalid parameters or erase failure
|
|
*
|
|
* @note Address must be aligned to ROW_SIZE (512 bytes)
|
|
* @note This function optimizes erase time by using larger erase operations
|
|
* when possible.
|
|
*/
|
|
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
|
{
|
|
cy_rslt_t status;
|
|
|
|
/* Validate input parameters */
|
|
if ((len <= 0) || (address % ROW_SIZE) || (address < FLASH_BASE_ADDRESS))
|
|
return -1;
|
|
|
|
/* Select optimal erase size based on alignment and length */
|
|
while (len > 0) {
|
|
if ((address % FLASH_SECTOR_SIZE == 0) &&
|
|
((uint32_t)len >= FLASH_SECTOR_SIZE)) {
|
|
/* Erase full sector (most efficient) */
|
|
status = Cy_Flash_EraseSector(address);
|
|
if (status != CY_RSLT_SUCCESS)
|
|
return -1;
|
|
|
|
address += FLASH_SECTOR_SIZE;
|
|
len -= FLASH_SECTOR_SIZE;
|
|
}
|
|
else if ((address % FLASH_SUBSECTOR_SIZE == 0) &&
|
|
((uint32_t)len >= FLASH_SUBSECTOR_SIZE)) {
|
|
/* Erase subsector (medium efficiency) */
|
|
status = Cy_Flash_EraseSubsector(address);
|
|
if (status != CY_RSLT_SUCCESS)
|
|
return -1;
|
|
|
|
address += FLASH_SUBSECTOR_SIZE;
|
|
len -= FLASH_SUBSECTOR_SIZE;
|
|
}
|
|
else {
|
|
/* Erase single row (smallest granularity) */
|
|
status = Cy_Flash_EraseRow(address);
|
|
if (status != CY_RSLT_SUCCESS)
|
|
return -1;
|
|
|
|
address += ROW_SIZE;
|
|
len -= ROW_SIZE;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Perform a system reset
|
|
*
|
|
* Triggers a software reset of the PSoC6 device by calling NVIC_SystemReset().
|
|
*
|
|
* @note This function does not return.
|
|
*/
|
|
void RAMFUNCTION arch_reboot(void)
|
|
{
|
|
NVIC_SystemReset();
|
|
}
|
|
|
|
/******************************************************************************
|
|
* External Flash Support
|
|
******************************************************************************/
|
|
|
|
#ifdef EXT_FLASH
|
|
|
|
/**
|
|
* @brief Write data to external flash with sector management
|
|
*
|
|
* Handles writing to external flash which has larger erase sectors
|
|
* (e.g. 0x40000 bytes for S25FL512S, determined at runtime via SFDP) than
|
|
* the row size used by wolfBoot. Implements a temporary sector backup
|
|
* mechanism to preserve existing data during partial sector writes.
|
|
*
|
|
* Algorithm:
|
|
* 1. Check if the target sector differs from the cached temporary sector
|
|
* 2. If different: backup entire source sector to temp sector, then erase
|
|
* source
|
|
* 3. If same: check if target area needs erasing (contains non-0xFF data)
|
|
* 4. Write new data to the target location
|
|
*
|
|
* @param address External flash address (must be row-aligned)
|
|
* @param data Pointer to source data buffer
|
|
* @param len Number of bytes to write (must be row-aligned)
|
|
*
|
|
* @return 0 on success, -1 on error
|
|
*
|
|
* @note Uses internal emulated EEPROM to track temporary sector state for
|
|
* power-fail safety.
|
|
* @note SWAP_TMP_SECTOR_ADDR is used as temporary storage during sector
|
|
* updates.
|
|
*/
|
|
int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t* data, int len)
|
|
{
|
|
tmp_sector_info info;
|
|
cy_rslt_t status;
|
|
uint32_t base_address;
|
|
uint32_t erase_size;
|
|
uint32_t sector_start;
|
|
uint8_t need_update;
|
|
uint32_t offset;
|
|
int ret = -1;
|
|
int i;
|
|
|
|
/* Validate alignment requirements */
|
|
if (!data || (address % ROW_SIZE) || (len % ROW_SIZE))
|
|
return -1;
|
|
|
|
base_address = smif_blk_config->memConfig[0]->baseAddress;
|
|
erase_size = smif_blk_config->memConfig[0]->deviceCfg->eraseSize;
|
|
|
|
memcpy(&info, (void*)TMP_SECTOR_INFO_ADDR, sizeof(tmp_sector_info));
|
|
sector_start = address - (address % erase_size);
|
|
|
|
/* Determine if temporary sector needs updating (invalid magic or different
|
|
* sector) */
|
|
need_update = ((info.magic != EXT_FLASH_TMP_MAGIC) ||
|
|
(info.sector_address != sector_start));
|
|
|
|
if (need_update) {
|
|
/* Invalidate temp sector info before starting update process */
|
|
if (hal_flash_erase(TMP_SECTOR_INFO_ADDR, ROW_SIZE) != 0)
|
|
goto error;
|
|
|
|
/* Erase the temporary sector */
|
|
status = Cy_SMIF_MemEraseSector(QSPIPort, smif_blk_config->memConfig[0],
|
|
SWAP_TMP_SECTOR_ADDR - base_address,
|
|
erase_size, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
goto error;
|
|
|
|
/* Backup source sector to temporary location row by row */
|
|
for (offset = 0; offset < erase_size; offset += ROW_SIZE) {
|
|
status =
|
|
Cy_SMIF_MemRead(QSPIPort, smif_blk_config->memConfig[0],
|
|
sector_start - base_address + offset,
|
|
psoc6_write_buffer, ROW_SIZE, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
goto error;
|
|
|
|
status =
|
|
Cy_SMIF_MemWrite(QSPIPort, smif_blk_config->memConfig[0],
|
|
SWAP_TMP_SECTOR_ADDR - base_address + offset,
|
|
psoc6_write_buffer, ROW_SIZE, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
goto error;
|
|
}
|
|
|
|
/* Mark temporary sector as valid */
|
|
info.magic = EXT_FLASH_TMP_MAGIC;
|
|
info.sector_address = sector_start;
|
|
if (hal_flash_write(TMP_SECTOR_INFO_ADDR, (const void*)&info,
|
|
sizeof(tmp_sector_info)) != 0)
|
|
goto error;
|
|
|
|
/* Erase the source sector */
|
|
status = Cy_SMIF_MemEraseSector(QSPIPort, smif_blk_config->memConfig[0],
|
|
sector_start - base_address, erase_size,
|
|
&smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
goto error;
|
|
}
|
|
else {
|
|
/* Check if target write region needs erasing (contains non-erased data)
|
|
*/
|
|
for (i = 0; i < len; i += ROW_SIZE) {
|
|
status =
|
|
Cy_SMIF_MemRead(QSPIPort, smif_blk_config->memConfig[0],
|
|
address - base_address + i, psoc6_write_buffer,
|
|
ROW_SIZE, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS) {
|
|
wolfBoot_printf(
|
|
"ext_flash_write: SMIF read failed at 0x%08lX\n",
|
|
(unsigned long)(address + i));
|
|
goto error;
|
|
}
|
|
|
|
/* Check if all bytes are in erased state. */
|
|
if ((*(uint32_t*)psoc6_write_buffer !=
|
|
EXT_FLASH_WORD_ERASED_VALUE) ||
|
|
(memcmp(psoc6_write_buffer + 4, psoc6_write_buffer,
|
|
ROW_SIZE - 4) != 0)) {
|
|
/* Non-erased data found, erase the containing sector */
|
|
status = Cy_SMIF_MemEraseSector(
|
|
QSPIPort, smif_blk_config->memConfig[0],
|
|
sector_start - base_address, erase_size, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS) {
|
|
wolfBoot_printf("ext_flash_write: SMIF erase failed for "
|
|
"sector 0x%08lX\n",
|
|
(unsigned long)sector_start);
|
|
goto error;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write the row data */
|
|
status = Cy_SMIF_MemWrite(QSPIPort, smif_blk_config->memConfig[0],
|
|
address - base_address, data, len, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
goto error;
|
|
|
|
ret = 0;
|
|
|
|
error:
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Read data from external flash with temporary sector redirection
|
|
*
|
|
* Reads data from external flash, automatically redirecting reads to the
|
|
* temporary backup sector if the requested address falls within a sector
|
|
* that is currently being updated (indicated by valid tmp_sector_info).
|
|
*
|
|
* This redirection ensures data consistency during interrupted write
|
|
* operations, as the temporary sector contains the original data before
|
|
* modification.
|
|
*
|
|
* @param address External flash address to read from
|
|
* @param data Pointer to destination buffer
|
|
* @param len Number of bytes to read (must be > 0)
|
|
*
|
|
* @return Number of bytes read on success, -1 on invalid parameters or
|
|
* SMIF failure
|
|
*
|
|
* @note Redirection is transparent to the caller and ensures read consistency
|
|
* even during power-fail recovery scenarios.
|
|
*/
|
|
int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t* data, int len)
|
|
{
|
|
tmp_sector_info info;
|
|
uint32_t base_address;
|
|
uint32_t erase_size;
|
|
uint32_t sector_start;
|
|
uint32_t offset;
|
|
cy_rslt_t status;
|
|
|
|
if (!data || (len <= 0))
|
|
return -1;
|
|
|
|
base_address = smif_blk_config->memConfig[0]->baseAddress;
|
|
erase_size = smif_blk_config->memConfig[0]->deviceCfg->eraseSize;
|
|
|
|
memcpy(&info, (void*)TMP_SECTOR_INFO_ADDR, sizeof(tmp_sector_info));
|
|
|
|
/* Redirect to temporary sector if active and address falls within that
|
|
* sector */
|
|
if (info.magic == EXT_FLASH_TMP_MAGIC) {
|
|
sector_start = address - (address % erase_size);
|
|
if (sector_start == info.sector_address) {
|
|
/* Remap to temporary sector while preserving offset within sector
|
|
*/
|
|
address = SWAP_TMP_SECTOR_ADDR + (address % erase_size);
|
|
}
|
|
}
|
|
|
|
offset = address - base_address;
|
|
status = Cy_SMIF_MemRead(QSPIPort, smif_blk_config->memConfig[0], offset,
|
|
data, len, &smif_context);
|
|
if (status != CY_SMIF_SUCCESS)
|
|
return -1;
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* @brief External flash erase operation (no-op)
|
|
*
|
|
* External flash erase is handled by ext_flash_write() due to sector size
|
|
* mismatch. wolfBoot expects row-sized erase granularity, but external
|
|
* flash minimum erase size is much larger (eg. 0x40000 bytes for S25FL512S).
|
|
* The write function manages sector erasing and preserves existing data
|
|
* via temporary sector backup.
|
|
*
|
|
* @param address External flash address (unused)
|
|
* @param len Number of bytes (unused)
|
|
* @return Always returns 0 (success)
|
|
*/
|
|
int RAMFUNCTION ext_flash_erase(uintptr_t address, int len)
|
|
{
|
|
(void)address;
|
|
(void)len;
|
|
return 0;
|
|
}
|
|
#endif /* EXT_FLASH */
|
|
|
|
#ifdef EXT_FLASH
|
|
|
|
/**
|
|
* @brief Lock external flash and finalize sector operations
|
|
*
|
|
* Invalidates the temporary sector tracking information stored in internal
|
|
* emulated EEPROM. This signals that any pending sector update has been
|
|
* completed and the temporary sector data is no longer needed.
|
|
*
|
|
* Should be called after a series of external flash write operations to
|
|
* commit the changes and free the temporary sector for future use.
|
|
*
|
|
* @note Erases the tmp_sector_info structure if it contains valid magic,
|
|
* which clears the EXT_FLASH_TMP_MAGIC marker.
|
|
*/
|
|
void ext_flash_lock(void)
|
|
{
|
|
tmp_sector_info info;
|
|
memcpy(&info, (void*)TMP_SECTOR_INFO_ADDR, sizeof(tmp_sector_info));
|
|
|
|
/* Clear temporary sector metadata to invalidate any active state */
|
|
if (info.magic == EXT_FLASH_TMP_MAGIC) {
|
|
hal_flash_erase(TMP_SECTOR_INFO_ADDR, ROW_SIZE);
|
|
}
|
|
}
|
|
|
|
void ext_flash_unlock(void)
|
|
{
|
|
}
|
|
#endif /* EXT_FLASH */
|
|
|
|
/******************************************************************************
|
|
* Dual Bank Flash Swap Support
|
|
******************************************************************************/
|
|
|
|
#if defined(DUALBANK_SWAP) && defined(__WOLFBOOT)
|
|
|
|
#if (WOLFBOOT_BOOTLOADER_START != FLASH_BANK1_BASE)
|
|
#error "The bootloader must point to FLASH_BANK1_BASE"
|
|
#endif
|
|
|
|
/**
|
|
* @brief Copy bootloader from Bank 1 to Bank 2 for dual-bank operation
|
|
*
|
|
* Creates an identical copy of the bootloader in the second flash bank.
|
|
* This enables the bootloader to execute from either bank when the flash
|
|
* mapping is swapped during firmware updates.
|
|
*
|
|
* The function is idempotent - if both banks already contain identical
|
|
* bootloader code, no flash operations are performed.
|
|
*
|
|
* @note Bootloader size is defined by WOLFBOOT_BOOTLOADER_SIZE.
|
|
* @note This function should be called once during initial setup or after
|
|
* bootloader updates.
|
|
*/
|
|
void RAMFUNCTION fork_bootloader(void)
|
|
{
|
|
uint8_t* src = (uint8_t*)FLASH_BANK1_BASE;
|
|
uint8_t* dst = (uint8_t*)(FLASH_BANK1_BASE + (CY_FLASH_SIZE / 2));
|
|
uint32_t i;
|
|
int ret;
|
|
|
|
/* Skip fork if bootloader already identical in both banks (idempotent
|
|
* operation) */
|
|
if (memcmp(src, dst, WOLFBOOT_BOOTLOADER_SIZE) == 0)
|
|
return;
|
|
|
|
hal_flash_unlock();
|
|
|
|
/* Erase entire bootloader region in Bank 2, then copy from Bank 1 row by
|
|
* row */
|
|
ret = hal_flash_erase((uintptr_t)dst, WOLFBOOT_BOOTLOADER_SIZE);
|
|
if (ret == 0) {
|
|
for (i = 0; i < WOLFBOOT_BOOTLOADER_SIZE; i += ROW_SIZE) {
|
|
ret = hal_flash_write((uintptr_t)(dst + i), src + i, ROW_SIZE);
|
|
if (ret != 0) {
|
|
wolfBoot_printf(
|
|
"Fork bootloader: write failed at offset 0x%X, ret=%d\n", i,
|
|
ret);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
wolfBoot_printf("Fork bootloader: erase failed, ret=%d\n", ret);
|
|
}
|
|
|
|
hal_flash_lock();
|
|
}
|
|
|
|
/**
|
|
* @brief Swap active flash bank mapping and persist the new state
|
|
*
|
|
* Toggles between Bank A and Bank B flash mapping to switch which physical
|
|
* flash bank appears at the base address. This enables A/B firmware update
|
|
* schemes where new firmware is written to the inactive bank and activated
|
|
* by swapping.
|
|
*
|
|
* The current bank mapping is persisted to emulated EEPROM so it survives
|
|
* reset and can be restored on subsequent boots.
|
|
*
|
|
* State transitions:
|
|
* - Bank A -> Bank B
|
|
* - Bank B -> Bank A
|
|
* - Invalid/first boot -> Bank A (initialized)
|
|
*
|
|
* @note Waits for flash controller to complete before returning.
|
|
* @note Bank mode data is stored at ACTIVE_BANK_MODE_DATA_ADDRESS in
|
|
* emulated EEPROM.
|
|
*/
|
|
void RAMFUNCTION hal_flash_dualbank_swap(void)
|
|
{
|
|
active_bank bankmode_data = *(active_bank*)ACTIVE_BANK_MODE_DATA_ADDRESS;
|
|
|
|
/* Read and validate saved bank mode from emulated EEPROM area */
|
|
if (bankmode_data.magic == BANKMODE_MAGIC) {
|
|
if (bankmode_data.mapping == CY_FLASH_MAPPING_A) {
|
|
bankmode_data.mapping = CY_FLASH_MAPPING_B;
|
|
}
|
|
else {
|
|
bankmode_data.mapping = CY_FLASH_MAPPING_A;
|
|
}
|
|
}
|
|
else {
|
|
/* First boot: initialize to Bank A */
|
|
bankmode_data.magic = BANKMODE_MAGIC;
|
|
bankmode_data.mapping = CY_FLASH_MAPPING_A;
|
|
}
|
|
|
|
/* Set the active bank mapping */
|
|
Cy_Flashc_SetMain_Flash_Mapping(bankmode_data.mapping);
|
|
|
|
/* Persist bank mode to emulated EEPROM flash area */
|
|
hal_flash_erase(ACTIVE_BANK_MODE_DATA_ADDRESS, ROW_SIZE);
|
|
hal_flash_write(ACTIVE_BANK_MODE_DATA_ADDRESS, (uint8_t*)&bankmode_data,
|
|
sizeof(active_bank));
|
|
|
|
/* Wait for flash command completion */
|
|
while (FLASHC_FLASH_CMD & FLASHC_V2_FLASH_CMD_INV_Msk)
|
|
;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize dual-bank flash mode on system startup
|
|
*
|
|
* Sets up the flash controller for dual-bank operation and restores the
|
|
* previously active bank mapping from persistent storage. Called during
|
|
* bootloader initialization.
|
|
*
|
|
* Initialization sequence:
|
|
* 1. Fork bootloader to Bank 2 (if not already present)
|
|
* 2. Enable dual-bank mode in flash controller
|
|
* 3. Read saved bank mapping from emulated EEPROM
|
|
* 4. Restore bank mapping or initialize to Bank A on first boot
|
|
* 5. Wait for flash controller ready
|
|
*
|
|
* @return 0 on success
|
|
*
|
|
* @note Must be called before any firmware verification or boot operations
|
|
* that depend on correct flash mapping.
|
|
* @note On first boot (no valid saved state), initializes to Bank A and
|
|
* persists this configuration.
|
|
*/
|
|
int RAMFUNCTION bankmode_init(void)
|
|
{
|
|
/* Ensure bootloader is present in both banks */
|
|
fork_bootloader();
|
|
|
|
/* Enable dual bank mode */
|
|
Cy_Flashc_SetMainBankMode(CY_FLASH_DUAL_BANK_MODE);
|
|
__ASM volatile("isb 0xF" ::: "memory");
|
|
|
|
/* Read and restore bank mode from emulated EEPROM flash area */
|
|
active_bank bankmode_data = *(active_bank*)ACTIVE_BANK_MODE_DATA_ADDRESS;
|
|
if (bankmode_data.magic == BANKMODE_MAGIC) {
|
|
/* Restore previously saved bank mapping */
|
|
Cy_Flashc_SetMain_Flash_Mapping(bankmode_data.mapping);
|
|
}
|
|
else {
|
|
/* First boot: initialize to Bank A */
|
|
bankmode_data.magic = BANKMODE_MAGIC;
|
|
bankmode_data.mapping = CY_FLASH_MAPPING_A;
|
|
|
|
/* Save initial bank mode to emulated EEPROM flash area */
|
|
hal_flash_erase(ACTIVE_BANK_MODE_DATA_ADDRESS, ROW_SIZE);
|
|
hal_flash_write(ACTIVE_BANK_MODE_DATA_ADDRESS, (uint8_t*)&bankmode_data,
|
|
sizeof(active_bank));
|
|
}
|
|
|
|
/* Wait for flash controller ready */
|
|
while (FLASHC_FLASH_CMD & FLASHC_V2_FLASH_CMD_INV_Msk)
|
|
;
|
|
|
|
return 0;
|
|
}
|
|
#endif /* DUALBANK_SWAP && __WOLFBOOT */
|