mirror of https://github.com/ARMmbed/mbed-os.git
757 lines
20 KiB
C
757 lines
20 KiB
C
/**************************************************************************//**
|
|
* @file fmc.c
|
|
* @version V1.00
|
|
* @brief M480 series FMC driver source file
|
|
*
|
|
* @copyright (C) 2016 Nuvoton Technology Corp. All rights reserved.
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
|
|
#include "M480.h"
|
|
|
|
|
|
/** @addtogroup M480_Device_Driver M480 Device Driver
|
|
@{
|
|
*/
|
|
|
|
/** @addtogroup M480_FMC_Driver FMC Driver
|
|
@{
|
|
*/
|
|
|
|
|
|
/** @addtogroup M480_FMC_EXPORTED_FUNCTIONS FMC Exported Functions
|
|
@{
|
|
*/
|
|
|
|
|
|
/**
|
|
* @brief Disable FMC ISP function.
|
|
* @return None
|
|
*/
|
|
void FMC_Close(void)
|
|
{
|
|
FMC->ISPCTL &= ~FMC_ISPCTL_ISPEN_Msk;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_PAGE_ERASE command to erase a flash page. The page size is 4096 bytes.
|
|
* @param[in] u32PageAddr Address of the flash page to be erased.
|
|
* It must be a 4096 bytes aligned address.
|
|
* @return ISP page erase success or not.
|
|
* @retval 0 Success
|
|
* @retval -1 Erase failed
|
|
*/
|
|
int32_t FMC_Erase(uint32_t u32PageAddr)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (u32PageAddr == FMC_SPROM_BASE) {
|
|
ret = FMC_Erase_SPROM();
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
|
|
FMC->ISPADDR = u32PageAddr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk) {
|
|
FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_PAGE_ERASE command to erase SPROM. The page size is 4096 bytes.
|
|
* @return SPROM page erase success or not.
|
|
* @retval 0 Success
|
|
* @retval -1 Erase failed
|
|
*/
|
|
int32_t FMC_Erase_SPROM(void)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE;
|
|
FMC->ISPADDR = FMC_SPROM_BASE;
|
|
FMC->ISPDAT = 0x0055AA03UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk) {
|
|
FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_BLOCK_ERASE command to erase a flash block. The block size is 4 pages.
|
|
* @param[in] u32BlockAddr Address of the flash block to be erased.
|
|
* It must be a 4 pages aligned address.
|
|
* @return ISP page erase success or not.
|
|
* @retval 0 Success
|
|
* @retval -1 Erase failed
|
|
*/
|
|
int32_t FMC_Erase_Block(uint32_t u32BlockAddr)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_BLOCK_ERASE;
|
|
FMC->ISPADDR = u32BlockAddr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk) {
|
|
FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_BANK_ERASE command to erase a flash block.
|
|
* @param[in] u32BankAddr Base address of the flash bank to be erased.
|
|
* @return ISP page erase success or not.
|
|
* @retval 0 Success
|
|
* @retval -1 Erase failed
|
|
*/
|
|
int32_t FMC_Erase_Bank(uint32_t u32BankAddr)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_BANK_ERASE;
|
|
FMC->ISPADDR = u32BankAddr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk) {
|
|
FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the current boot source.
|
|
* @return The current boot source.
|
|
* @retval 0 Is boot from APROM.
|
|
* @retval 1 Is boot from LDROM.
|
|
*/
|
|
int32_t FMC_GetBootSource (void)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (FMC->ISPCTL & FMC_ISPCTL_BS_Msk) {
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Enable FMC ISP function
|
|
* @return None
|
|
*/
|
|
void FMC_Open(void)
|
|
{
|
|
FMC->ISPCTL |= FMC_ISPCTL_ISPEN_Msk;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_READ command to read a word from flash.
|
|
* @param[in] u32Addr Address of the flash location to be read.
|
|
* It must be a word aligned address.
|
|
* @return The word data read from specified flash address.
|
|
*/
|
|
uint32_t FMC_Read(uint32_t u32Addr)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_READ;
|
|
FMC->ISPADDR = u32Addr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
return FMC->ISPDAT;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_READ_64 command to read a double-word from flash.
|
|
* @param[in] u32addr Address of the flash location to be read.
|
|
* It must be a double-word aligned address.
|
|
* @param[out] u32data0 Place holder of word 0 read from flash address u32addr.
|
|
* @param[out] u32data1 Place holder of word 0 read from flash address u32addr+4.
|
|
* @return 0 Success
|
|
* @return -1 Failed
|
|
*/
|
|
int32_t FMC_Read_64(uint32_t u32addr, uint32_t * u32data0, uint32_t * u32data1)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_64;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->ISPDAT = 0x0UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
} else {
|
|
*u32data0 = FMC->MPDAT0;
|
|
*u32data1 = FMC->MPDAT1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Read company ID.
|
|
* @retval The company ID.
|
|
*/
|
|
uint32_t FMC_ReadCID(void)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_CID;
|
|
FMC->ISPADDR = 0x0UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
return FMC->ISPDAT;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Read product ID.
|
|
* @retval The product ID.
|
|
*/
|
|
uint32_t FMC_ReadPID(void)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_PID;
|
|
FMC->ISPADDR = 0x04UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
return FMC->ISPDAT;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief This function reads one of the four UCID.
|
|
* @param[in] u32Index Index of the UCID to read. u32Index must be 0, 1, 2, or 3.
|
|
* @retval The UCID.
|
|
*/
|
|
uint32_t FMC_ReadUCID(uint32_t u32Index)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_UID;
|
|
FMC->ISPADDR = (0x04UL * u32Index) + 0x10UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
return FMC->ISPDAT;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief This function reads one of the three UID.
|
|
* @param[in] u32Index Index of the UID to read. u32Index must be 0, 1, or 2.
|
|
* @retval The UID.
|
|
*/
|
|
uint32_t FMC_ReadUID(uint32_t u32Index)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_UID;
|
|
FMC->ISPADDR = 0x04UL * u32Index;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
return FMC->ISPDAT;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Get the base address of Data Flash if enabled.
|
|
* @retval The base address of Data Flash
|
|
*/
|
|
uint32_t FMC_ReadDataFlashBaseAddr(void)
|
|
{
|
|
return FMC->DFBA;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief This function will force re-map assigned flash page to CPU address 0x0.
|
|
* @param[in] u32PageAddr Address of the page to be mapped to CPU address 0x0.
|
|
* @return None
|
|
*/
|
|
void FMC_SetVectorPageAddr(uint32_t u32PageAddr)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_VECMAP;
|
|
FMC->ISPADDR = u32PageAddr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute ISP FMC_ISPCMD_PROGRAM to program a word to flash.
|
|
* @param[in] u32Addr Address of the flash location to be programmed.
|
|
* It must be a word aligned address.
|
|
* @param[in] u32Data The word data to be programmed.
|
|
* @return None
|
|
*/
|
|
void FMC_Write(uint32_t u32Addr, uint32_t u32Data)
|
|
{
|
|
FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
|
|
FMC->ISPADDR = u32Addr;
|
|
FMC->ISPDAT = u32Data;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
}
|
|
|
|
/**
|
|
* @brief Execute ISP FMC_ISPCMD_PROGRAM_64 to program a double-word to flash.
|
|
* @param[in] u32addr Address of the flash location to be programmed.
|
|
* It must be a double-word aligned address.
|
|
* @param[in] u32data0 The word data to be programmed to flash address u32addr.
|
|
* @param[in] u32data1 The word data to be programmed to flash address u32addr+4.
|
|
* @return 0 Success
|
|
* @return -1 Failed
|
|
*/
|
|
int32_t FMC_Write_64(uint32_t u32addr, uint32_t u32data0, uint32_t u32data1)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_PROGRAM_64;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->MPDAT0 = u32data0;
|
|
FMC->MPDAT1 = u32data1;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Program a 64-bits data to the specified OTP.
|
|
* @param[in] otp_num The OTP number.
|
|
* @param[in] low_word Low word of the 64-bits data.
|
|
* @param[in] high_word Low word of the 64-bits data.
|
|
* @retval 0 Success
|
|
* @retval -1 Program failed.
|
|
* @retval -2 Invalid OTP number.
|
|
*/
|
|
int32_t FMC_Write_OTP(uint32_t otp_num, uint32_t low_word, uint32_t high_word)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (otp_num > 255UL) {
|
|
ret = -2;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
|
|
FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL;
|
|
FMC->ISPDAT = low_word;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
|
|
FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL + 4UL;
|
|
FMC->ISPDAT = high_word;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Read the 64-bits data from the specified OTP.
|
|
* @param[in] otp_num The OTP number.
|
|
* @param[in] low_word Low word of the 64-bits data.
|
|
* @param[in] high_word Low word of the 64-bits data.
|
|
* @retval 0 Success
|
|
* @retval -1 Read failed.
|
|
* @retval -2 Invalid OTP number.
|
|
*/
|
|
int32_t FMC_Read_OTP(uint32_t otp_num, uint32_t *low_word, uint32_t *high_word)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (otp_num > 255UL) {
|
|
ret = -2;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_64;
|
|
FMC->ISPADDR = FMC_OTP_BASE + otp_num * 8UL ;
|
|
FMC->ISPDAT = 0x0UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
} else {
|
|
*low_word = FMC->MPDAT0;
|
|
*high_word = FMC->MPDAT1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Lock the specified OTP.
|
|
* @param[in] otp_num The OTP number.
|
|
* @retval 0 Success
|
|
* @retval -1 Failed to write OTP lock bits.
|
|
* @retval -2 Invalid OTP number.
|
|
*/
|
|
int32_t FMC_Lock_OTP(uint32_t otp_num)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (otp_num > 255UL) {
|
|
ret = -2;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_PROGRAM;
|
|
FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
|
|
FMC->ISPDAT = 0UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Check the OTP is locked or not.
|
|
* @param[in] otp_num The OTP number.
|
|
* @retval 1 OTP is locked.
|
|
* @retval 0 OTP is not locked.
|
|
* @retval -1 Failed to read OTP lock bits.
|
|
* @retval -2 Invalid OTP number.
|
|
*/
|
|
int32_t FMC_Is_OTP_Locked(uint32_t otp_num)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
if (otp_num > 255UL) {
|
|
ret = -2;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->ISPCMD = FMC_ISPCMD_READ;
|
|
FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + otp_num * 4UL;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPTRG & FMC_ISPTRG_ISPGO_Msk) { }
|
|
|
|
if (FMC->ISPSTS & FMC_ISPSTS_ISPFF_Msk) {
|
|
FMC->ISPSTS |= FMC_ISPSTS_ISPFF_Msk;
|
|
ret = -1;
|
|
} else {
|
|
if (FMC->ISPDAT != 0xFFFFFFFFUL) {
|
|
ret = 1; /* Lock work was progrmmed. OTP was locked. */
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Execute FMC_ISPCMD_READ command to read User Configuration.
|
|
* @param[out] u32Config A two-word array.
|
|
* u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
|
|
* @param[in] u32Count Available word count in u32Config.
|
|
* @return Success or not.
|
|
* @retval 0 Success.
|
|
* @retval -1 Invalid parameter.
|
|
*/
|
|
int32_t FMC_ReadConfig(uint32_t u32Config[], uint32_t u32Count)
|
|
{
|
|
int32_t ret = 0;
|
|
|
|
u32Config[0] = FMC_Read(FMC_CONFIG_BASE);
|
|
|
|
if (u32Count < 2UL) {
|
|
ret = -1;
|
|
} else {
|
|
u32Config[1] = FMC_Read(FMC_CONFIG_BASE+4UL);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute ISP commands to erase then write User Configuration.
|
|
* @param[in] u32Config A two-word array.
|
|
* u32Config[0] holds CONFIG0, while u32Config[1] holds CONFIG1.
|
|
* @param[in] u32Count Always be 2 in this BSP.
|
|
* @return Success or not.
|
|
* @retval 0 Success.
|
|
* @retval -1 Invalid parameter.
|
|
*/
|
|
int32_t FMC_WriteConfig(uint32_t u32Config[], uint32_t u32Count)
|
|
{
|
|
FMC_ENABLE_CFG_UPDATE();
|
|
FMC_Erase(FMC_CONFIG_BASE);
|
|
FMC_Write(FMC_CONFIG_BASE, u32Config[0]);
|
|
FMC_Write(FMC_CONFIG_BASE+4UL, u32Config[1]);
|
|
FMC_DISABLE_CFG_UPDATE();
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Run CRC32 checksum calculation and get result.
|
|
* @param[in] u32addr Starting flash address. It must be a page aligned address.
|
|
* @param[in] u32count Byte count of flash to be calculated. It must be multiple of 512 bytes.
|
|
* @return Success or not.
|
|
* @retval 0 Success.
|
|
* @retval 0xFFFFFFFF Invalid parameter.
|
|
*/
|
|
uint32_t FMC_GetChkSum(uint32_t u32addr, uint32_t u32count)
|
|
{
|
|
uint32_t ret;
|
|
|
|
if ((u32addr % 512UL) || (u32count % 512UL)) {
|
|
ret = 0xFFFFFFFF;
|
|
} else {
|
|
FMC->ISPCMD = FMC_ISPCMD_RUN_CKS;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->ISPDAT = u32count;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_CKS;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
ret = FMC->ISPDAT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Run flash all one verification and get result.
|
|
* @param[in] u32addr Starting flash address. It must be a page aligned address.
|
|
* @param[in] u32count Byte count of flash to be calculated. It must be multiple of 512 bytes.
|
|
* @retval READ_ALLONE_YES The contents of verified flash area are 0xFFFFFFFF.
|
|
* @retval READ_ALLONE_NOT Some contents of verified flash area are not 0xFFFFFFFF.
|
|
* @retval READ_ALLONE_CMD_FAIL Unexpected error occurred.
|
|
*/
|
|
uint32_t FMC_CheckAllOne(uint32_t u32addr, uint32_t u32count)
|
|
{
|
|
uint32_t ret = READ_ALLONE_CMD_FAIL;
|
|
|
|
FMC->ISPSTS = 0x80UL; /* clear check all one bit */
|
|
|
|
FMC->ISPCMD = FMC_ISPCMD_RUN_ALL1;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->ISPDAT = u32count;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
|
|
do {
|
|
FMC->ISPCMD = FMC_ISPCMD_READ_ALL1;
|
|
FMC->ISPADDR = u32addr;
|
|
FMC->ISPTRG = FMC_ISPTRG_ISPGO_Msk;
|
|
while (FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) { }
|
|
} while (FMC->ISPDAT == 0UL);
|
|
|
|
if (FMC->ISPDAT == READ_ALLONE_YES) {
|
|
ret = FMC->ISPDAT;
|
|
}
|
|
|
|
if (FMC->ISPDAT == READ_ALLONE_NOT) {
|
|
ret = FMC->ISPDAT;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Setup security key.
|
|
* @param[in] key Key 0~2 to be setup.
|
|
* @param[in] kpmax Maximum unmatched power-on counting number.
|
|
* @param[in] kemax Maximum unmatched counting number.
|
|
* @param[in] lock_CONFIG 1: Security key lock CONFIG to write-protect. 0: Don't lock CONFIG.
|
|
* @param[in] lock_SPROM 1: Security key lock SPROM to write-protect. 0: Don't lock SPROM.
|
|
* @retval 0 Success.
|
|
* @retval -1 Key is locked. Cannot overwrite the current key.
|
|
* @retval -2 Failed to erase flash.
|
|
* @retval -3 Failed to program key.
|
|
* @retval -4 Key lock function failed.
|
|
* @retval -5 CONFIG lock function failed.
|
|
* @retval -6 SPROM lock function failed.
|
|
* @retval -7 KPMAX function failed.
|
|
* @retval -8 KEMAX function failed.
|
|
*/
|
|
int32_t FMC_SKey_Setup(uint32_t key[3], uint32_t kpmax, uint32_t kemax,
|
|
const int32_t lock_CONFIG, const int32_t lock_SPROM)
|
|
{
|
|
uint32_t lock_ctrl = 0UL;
|
|
uint32_t u32KeySts;
|
|
int32_t ret = 0;
|
|
|
|
if (FMC->KPKEYSTS != 0x200UL) {
|
|
ret = -1;
|
|
}
|
|
|
|
if (FMC_Erase(FMC_KPROM_BASE)) {
|
|
ret = -2;
|
|
}
|
|
|
|
if (FMC_Erase(FMC_KPROM_BASE+0x200UL)) {
|
|
ret = -3;
|
|
}
|
|
|
|
if (!lock_CONFIG) {
|
|
lock_ctrl |= 0x1UL;
|
|
}
|
|
|
|
if (!lock_SPROM) {
|
|
lock_ctrl |= 0x2UL;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC_Write(FMC_KPROM_BASE, key[0]);
|
|
FMC_Write(FMC_KPROM_BASE+0x4UL, key[1]);
|
|
FMC_Write(FMC_KPROM_BASE+0x8UL, key[2]);
|
|
FMC_Write(FMC_KPROM_BASE+0xCUL, kpmax);
|
|
FMC_Write(FMC_KPROM_BASE+0x10UL, kemax);
|
|
FMC_Write(FMC_KPROM_BASE+0x14UL, lock_ctrl);
|
|
|
|
while (FMC->KPKEYSTS & FMC_KPKEYSTS_KEYBUSY_Msk) { }
|
|
|
|
u32KeySts = FMC->KPKEYSTS;
|
|
|
|
if (!(u32KeySts & FMC_KPKEYSTS_KEYLOCK_Msk)) {
|
|
/* Security key lock failed! */
|
|
ret = -4;
|
|
} else if ((lock_CONFIG && (!(u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk))) ||
|
|
((!lock_CONFIG) && (u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk))) {
|
|
/* CONFIG lock failed! */
|
|
ret = -5;
|
|
} else if ((lock_SPROM && (!(u32KeySts & FMC_KPKEYSTS_SPFLAG_Msk))) ||
|
|
((!lock_SPROM) && (u32KeySts & FMC_KPKEYSTS_SPFLAG_Msk))) {
|
|
/* CONFIG lock failed! */
|
|
ret = -6;
|
|
} else if (((FMC->KPCNT & FMC_KPCNT_KPMAX_Msk) >> FMC_KPCNT_KPMAX_Pos) != kpmax) {
|
|
/* KPMAX failed! */
|
|
ret = -7;
|
|
} else if (((FMC->KPKEYCNT & FMC_KPKEYCNT_KPKEMAX_Msk) >> FMC_KPKEYCNT_KPKEMAX_Pos) != kemax) {
|
|
/* KEMAX failed! */
|
|
ret = -8;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Execute security key comparison.
|
|
* @param[in] key Key 0~2 to be compared.
|
|
* @retval 0 Key matched.
|
|
* @retval -1 Forbidden. Times of key comparison mismatch reach the maximum count.
|
|
* @retval -2 Key mismatched.
|
|
* @retval -3 No security key lock. Key comparison is not required.
|
|
*/
|
|
int32_t FMC_SKey_Compare(uint32_t key[3])
|
|
{
|
|
uint32_t u32KeySts;
|
|
int32_t ret = 0;
|
|
|
|
if (FMC->KPKEYSTS & FMC_KPKEYSTS_FORBID_Msk) {
|
|
/* FMC_SKey_Compare - FORBID! */
|
|
ret = -1;
|
|
}
|
|
|
|
if (!(FMC->KPKEYSTS & FMC_KPKEYSTS_KEYLOCK_Msk)) {
|
|
/* FMC_SKey_Compare - key is not locked! */
|
|
ret = -3;
|
|
}
|
|
|
|
if (ret == 0) {
|
|
FMC->KPKEY0 = key[0];
|
|
FMC->KPKEY1 = key[1];
|
|
FMC->KPKEY2 = key[2];
|
|
FMC->KPKEYTRG = FMC_KPKEYTRG_KPKEYGO_Msk | FMC_KPKEYTRG_TCEN_Msk;
|
|
|
|
while (FMC->KPKEYSTS & FMC_KPKEYSTS_KEYBUSY_Msk) { }
|
|
|
|
u32KeySts = FMC->KPKEYSTS;
|
|
|
|
if (!(u32KeySts & FMC_KPKEYSTS_KEYMATCH_Msk)) {
|
|
/* Key mismatched! */
|
|
ret = -2;
|
|
} else if (u32KeySts & FMC_KPKEYSTS_KEYLOCK_Msk) {
|
|
/* Key matched, but still be locked! */
|
|
ret = -2;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*@}*/ /* end of group M480_FMC_EXPORTED_FUNCTIONS */
|
|
|
|
/*@}*/ /* end of group M480_FMC_Driver */
|
|
|
|
/*@}*/ /* end of group M480_Device_Driver */
|
|
|
|
/*** (C) COPYRIGHT 2016 Nuvoton Technology Corp. ***/
|
|
|
|
|