/**************************************************************************//** * @file fmc.c * @version V3.00 * $Revision: 1 $ * $Date: 16/07/07 7:50p $ * @brief M261 Series Flash Memory Controller(FMC) driver source file * * @note * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of Nuvoton Technology Corp. nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ #include #include "NuMicro.h" /** @addtogroup Standard_Driver Standard Driver @{ */ /** @addtogroup FMC_Driver FMC Driver @{ */ /** @addtogroup FMC_EXPORTED_FUNCTIONS FMC Exported Functions @{ */ /** * @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 0xA11FFFFF. * @retval READ_ALLONE_NOT Some contents of verified flash area are not 0xA1100000. * @retval READ_ALLONE_CMD_FAIL Unexpected error occurred. * * @details Run ISP check all one command to check specify area is all one or not. */ 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 Disable ISP Functions * * @param None * * @return None * * @details This function will clear ISPEN bit of ISPCON to disable ISP function * */ void FMC_Close(void) { FMC->ISPCTL &= ~FMC_ISPCTL_ISPEN_Msk; } /** * @brief Config XOM Region * @param[in] u32XomNum The XOM number(0~3) * @param[in] u32XomBase The XOM region base address. * @param[in] u8XomPage The XOM page number of region size. * * @retval 0 Success * @retval 1 XOM is has already actived. * @retval -1 Program failed. * @retval -2 Invalid XOM number. * * @details Program XOM base address and XOM size(page) */ int32_t FMC_ConfigXOM(uint32_t u32XomNum, uint32_t u32XomBase, uint8_t u8XomPage) { int32_t ret = 0; if(u32XomNum >= 4UL) { ret = -2; } if(ret == 0) { ret = FMC_GetXOMState(u32XomNum); } if(ret == 0) { FMC->ISPCMD = FMC_ISPCMD_PROGRAM; FMC->ISPADDR = FMC_XOM_BASE + (u32XomNum * 0x10u); FMC->ISPDAT = u32XomBase; 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_XOM_BASE + (u32XomNum * 0x10u + 0x04u); FMC->ISPDAT = u8XomPage; 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_XOM_BASE + (u32XomNum * 0x10u + 0x08u); FMC->ISPDAT = 0u; 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 Execute Flash Page erase * * @param[in] u32PageAddr Address of the flash page to be erased. * It must be a 2048 bytes aligned address. * * @return ISP page erase success or not. * @retval 0 Success * @retval -1 Erase failed * * @details Execute FMC_ISPCMD_PAGE_ERASE command to erase a flash page. The page size is 2048 bytes. */ int32_t FMC_Erase(uint32_t u32PageAddr) { int32_t ret = 0; 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 Flash Bank erase * * @param[in] u32BankAddr Base address of the flash bank to be erased. * * @return ISP bank erase success or not. * @retval 0 Success * @retval -1 Erase failed * * @details Execute FMC_ISPCMD_BANK_ERASE command to erase a flash block. */ 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 Execute Flash Block erase * * @param[in] u32BlockAddr Address of the flash block to be erased. * It must be a 4 pages aligned address. * * @return ISP block erase success or not. * @retval 0 Success * @retval -1 Erase failed * * @details Execute FMC_ISPCMD_BLOCK_ERASE command to erase a flash block. The block size is 4 pages. */ 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 Erase XOM Region * * @param[in] u32XomNum The XOMRn(n=0~3) * * @return XOM erase success or not. * @retval 0 Success * @retval -1 Erase failed * @retval -2 Invalid XOM number. * * @details Execute FMC_ISPCMD_PAGE_ERASE command to erase XOM. */ int32_t FMC_EraseXOM(uint32_t u32XomNum) { uint32_t u32Addr; int32_t i32Active, err = 0; if(u32XomNum >= 4UL) { err = -2; } if(err == 0) { i32Active = FMC_GetXOMState(u32XomNum); if(i32Active) { switch(u32XomNum) { case 0u: u32Addr = (FMC->XOMR0STS & 0xFFFFFF00u) >> 8u; break; case 1u: u32Addr = (FMC->XOMR1STS & 0xFFFFFF00u) >> 8u; break; case 2u: u32Addr = (FMC->XOMR2STS & 0xFFFFFF00u) >> 8u; break; case 3u: u32Addr = (FMC->XOMR3STS & 0xFFFFFF00u) >> 8u; break; default: break; } FMC->ISPCMD = FMC_ISPCMD_PAGE_ERASE; FMC->ISPADDR = u32Addr; FMC->ISPDAT = 0x55aa03u; FMC->ISPTRG = 0x1u; #if ISBEN __ISB(); #endif while(FMC->ISPTRG) {} /* Check ISPFF flag to know whether erase OK or fail. */ if(FMC->ISPCTL & FMC_ISPCTL_ISPFF_Msk) { FMC->ISPCTL |= FMC_ISPCTL_ISPFF_Msk; err = -1; } } else { err = -1; } } return err; } /** * @brief Get the current boot source * * @param None * * @return The current boot source. * @retval 0 This chip is currently booting from APROM * @retval 1 This chip is currently booting from LDROM * * @note This function only show the boot source. * User need to read ISPSTA register to know if IAP mode supported or not in relative boot. */ int32_t FMC_GetBootSource(void) { int32_t ret = 0; if(FMC->ISPCTL & FMC_ISPCTL_BS_Msk) { ret = 1; } return ret; } /** * @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 2048bytes. * * @return Success or not. * @retval 0 Success. * @retval 0xFFFFFFFF Invalid parameter. * * @details Run ISP CRC32 checksum command to calculate checksum then get and return checksum data. */ uint32_t FMC_GetChkSum(uint32_t u32addr, uint32_t u32count) { uint32_t ret; if((u32addr % 2048UL) || (u32count % 2048UL)) { 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 Check the OTP is locked or not. * * @param[in] u32OtpNum 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. * * @details To get specify OTP lock status */ int32_t FMC_Is_OTP_Locked(uint32_t u32OtpNum) { int32_t ret = 0; if(u32OtpNum > 255UL) { ret = -2; } if(ret == 0) { FMC->ISPCMD = FMC_ISPCMD_READ; FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + u32OtpNum * 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 Check the XOM is actived or not. * * @param[in] u32XomNum The xom number(0~3). * * @retval 1 XOM is actived. * @retval 0 XOM is not actived. * @retval -2 Invalid XOM number. * * @details To get specify XOMRn(n=0~3) active status */ int32_t FMC_GetXOMState(uint32_t u32XomNum) { uint32_t u32act; int32_t ret = 0; if(u32XomNum >= 4UL) { ret = -2; } if(ret >= 0) { u32act = (((FMC->XOMSTS) & 0xful) & (1ul << u32XomNum)) >> u32XomNum; ret = (int32_t)u32act; } return ret; } /** * @brief Lock the specified OTP. * * @param[in] u32OtpNum The OTP number. * * @retval 0 Success * @retval -1 Failed to write OTP lock bits. * @retval -2 Invalid OTP number. * * @details To lock specified OTP number */ int32_t FMC_Lock_OTP(uint32_t u32OtpNum) { int32_t ret = 0; if(u32OtpNum > 255UL) { ret = -2; } if(ret == 0) { FMC->ISPCMD = FMC_ISPCMD_PROGRAM; FMC->ISPADDR = FMC_OTP_BASE + 0x800UL + u32OtpNum * 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 Enable FMC ISP function * * @param None * * @return None * * @details ISPEN bit of ISPCON must be set before we can use ISP commands. * Therefore, To use all FMC function APIs, user needs to call FMC_Open() first to enable ISP functions. * * @note ISP functions are write-protected. user also needs to unlock it by calling SYS_UnlockReg() before using all ISP functions. * */ void FMC_Open(void) { FMC->ISPCTL |= FMC_ISPCTL_ISPEN_Msk; } /** * @brief Read a word bytes 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. * * @details Execute FMC_ISPCMD_READ command to read a word from flash. */ 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 Read a double-word bytes 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 * * @details Execute FMC_ISPCMD_READ_64 command to read a double-word from flash. */ 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 data from OTP * * @param[in] u32OtpNum The OTP number(0~255). * @param[in] u32LowWord Low word of the 64-bits data. * @param[in] u32HighWord High word of the 64-bits data. * * @retval 0 Success * @retval -1 Read failed. * @retval -2 Invalid OTP number. * * @details Read the 64-bits data from the specified OTP. */ int32_t FMC_Read_OTP(uint32_t u32OtpNum, uint32_t *u32LowWord, uint32_t *u32HighWord) { int32_t ret = 0; if(u32OtpNum > 255UL) { ret = -2; } if(ret == 0) { FMC->ISPCMD = FMC_ISPCMD_READ_64; FMC->ISPADDR = FMC_OTP_BASE + u32OtpNum * 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 { *u32LowWord = FMC->MPDAT0; *u32HighWord = FMC->MPDAT1; } } return ret; } /** * @brief Read the User Configuration words. * * @param[out] u32Config[] The word buffer to store the User Configuration data. * @param[in] u32Count The word count to be read. * * @return Success or not. * @retval 0 Success * @retval -1 Failed * * @details This function is used to read the settings of user configuration. * if u32Count = 1, Only CONFIG0 will be returned to the buffer specified by u32Config. * if u32Count = 2, Both CONFIG0 and CONFIG1 will be returned. */ int32_t FMC_ReadConfig(uint32_t u32Config[], uint32_t u32Count) { uint32_t i; for(i = 0u; i < u32Count; i++) { u32Config[i] = FMC_Read(FMC_CONFIG_BASE + i * 4u); } return 0; } /** * @brief Set boot source from LDROM or APROM after next software reset * * @param[in] i32BootSrc * 1: Boot from LDROM * 0: Boot from APROM * * @return None * * @details This function is used to switch APROM boot or LDROM boot. User need to call * FMC_SetBootSource to select boot source first, then use CPU reset or * System Reset Request to reset system. * */ void FMC_SetBootSource(int32_t i32BootSrc) { if(i32BootSrc) { FMC->ISPCTL |= FMC_ISPCTL_BS_Msk; /* Boot from LDROM */ } else { FMC->ISPCTL &= ~FMC_ISPCTL_BS_Msk;/* Boot from APROM */ } } /** * @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 KPROM key lock. Key comparison is not required. * * @ details Input a keys to compare with security key */ int32_t FMC_CompareSPKey(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; } /** * @brief Setup Security Key. * * @param[in] au32Key Key 0~2 to be setup. * @param[in] u32Kpmax Maximum unmatched power-on counting number. * @param[in] u32Kemax Maximum unmatched counting number. * @param[in] i32LockCONFIG 1: Security key lock CONFIG to write-protect. 0: Don't lock CONFIG. * @param[in] i32LockSPROM 1: Security key lock SPROM to write-protect. 0: Don't lock SPROM. (This param is not supported on M261) * * @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. (This status is not supported on M261) * @retval -7 KPMAX function failed. * @retval -8 KEMAX function failed. * * @details Set secure keys and setup key compare count. The secure key also can protect user config. */ int32_t FMC_SetSPKey(uint32_t au32Key[3], uint32_t u32Kpmax, uint32_t u32Kemax, const int32_t i32LockCONFIG, const int32_t i32LockSPROM) { 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(!i32LockCONFIG) { lock_ctrl |= 0x1UL; } if(!i32LockSPROM) { lock_ctrl |= 0x2UL; } if(ret == 0) { FMC_Write(FMC_KPROM_BASE, au32Key[0]); FMC_Write(FMC_KPROM_BASE + 0x4UL, au32Key[1]); FMC_Write(FMC_KPROM_BASE + 0x8UL, au32Key[2]); FMC_Write(FMC_KPROM_BASE + 0xCUL, u32Kpmax); FMC_Write(FMC_KPROM_BASE + 0x10UL, u32Kemax); 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((i32LockCONFIG && (!(u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk))) || ((!i32LockCONFIG) && (u32KeySts & FMC_KPKEYSTS_CFGFLAG_Msk))) { /* CONFIG lock failed! */ ret = -5; } else if(((FMC->KPCNT & FMC_KPCNT_KPMAX_Msk) >> FMC_KPCNT_KPMAX_Pos) != u32Kpmax) { /* KPMAX failed! */ ret = -7; } else if(((FMC->KPKEYCNT & FMC_KPKEYCNT_KPKEMAX_Msk) >> FMC_KPKEYCNT_KPKEMAX_Pos) != u32Kemax) { /* KEMAX failed! */ ret = -8; } } return ret; } /** * @brief Write a word bytes 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 * * @ details Execute ISP FMC_ISPCMD_PROGRAM to program a word to flash. */ 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 Write a double-word bytes 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 * * @ details Execute ISP FMC_ISPCMD_PROGRAM_64 to program a double-word to flash. */ int32_t FMC_Write8Bytes(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 Write User Configuration * * @param[in] au32Config[] The word buffer to store the User Configuration data. * @param[in] u32Count The word count to program to User Configuration. * * @retval 0 Success * @retval -1 Failed * * @details User must enable User Configuration update before writing it. * User must erase User Configuration before writing it. * User Configuration is also be page erase. User needs to backup necessary data * before erase User Configuration. */ int32_t FMC_WriteConfig(uint32_t au32Config[], uint32_t u32Count) { int32_t ret = 0; uint32_t i; FMC_ENABLE_CFG_UPDATE(); for(i = 0u; i < u32Count; i++) { FMC_Write(FMC_CONFIG_BASE + i * 4u, au32Config[i]); if(FMC_Read(FMC_CONFIG_BASE + i * 4u) != au32Config[i]) { ret = -1; } } FMC_DISABLE_CFG_UPDATE(); return ret; } /** * @brief Write Multi-Word bytes to flash * * @param[in] u32Addr Start flash address in APROM where the data chunk to be programmed into. * This address must be 8-bytes aligned to flash address. * @param[in] pu32Buf Buffer that carry the data chunk. * @param[in] u32Len Length of the data chunk in bytes. * * @retval >=0 Number of data bytes were programmed. * @return -1 Invalid address. * * @details Program Multi-Word data into specified address of flash. */ int32_t FMC_WriteMultiple(uint32_t u32Addr, uint32_t pu32Buf[], uint32_t u32Len) { uint32_t i, idx, u32OnProg, retval = 0; int32_t err; if((u32Addr >= FMC_APROM_END) || ((u32Addr % 8) != 0)) { return -1; } idx = 0u; FMC->ISPCMD = FMC_ISPCMD_PROGRAM_MUL; FMC->ISPADDR = u32Addr; retval += 16; do { err = 0; u32OnProg = 1u; FMC->MPDAT0 = pu32Buf[idx + 0u]; FMC->MPDAT1 = pu32Buf[idx + 1u]; FMC->MPDAT2 = pu32Buf[idx + 2u]; FMC->MPDAT3 = pu32Buf[idx + 3u]; FMC->ISPTRG = 0x1u; idx += 4u; for(i = idx; i < (FMC_MULTI_WORD_PROG_LEN / 4u); i += 4u) /* Max data length is 256 bytes (512/4 words)*/ { __set_PRIMASK(1u); /* Mask interrupt to avoid status check coherence error*/ do { if((FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk) == 0u) { __set_PRIMASK(0u); FMC->ISPADDR = FMC->MPADDR & (~0xful); idx = (FMC->ISPADDR - u32Addr) / 4u; err = -1; } } while((FMC->MPSTS & (3u << FMC_MPSTS_D0_Pos)) && (err == 0)); if(err == 0) { retval += 8; /* Update new data for D0 */ FMC->MPDAT0 = pu32Buf[i]; FMC->MPDAT1 = pu32Buf[i + 1u]; do { if((FMC->MPSTS & FMC_MPSTS_MPBUSY_Msk) == 0u) { __set_PRIMASK(0u); FMC->ISPADDR = FMC->MPADDR & (~0xful); idx = (FMC->ISPADDR - u32Addr) / 4u; err = -1; } } while((FMC->MPSTS & (3u << FMC_MPSTS_D2_Pos)) && (err == 0)); if(err == 0) { retval += 8; /* Update new data for D2*/ FMC->MPDAT2 = pu32Buf[i + 2u]; FMC->MPDAT3 = pu32Buf[i + 3u]; __set_PRIMASK(0u); } } if(err < 0) { break; } } if(err == 0) { u32OnProg = 0u; while(FMC->ISPSTS & FMC_ISPSTS_ISPBUSY_Msk) {} } } while(u32OnProg); return retval; } /** * @brief Write data to OTP * * @param[in] u32OtpNum The OTP number(0~255). * @param[in] u32LowWord Low word of the 64-bits data. * @param[in] u32HighWord High word of the 64-bits data. * * @retval 0 Success * @retval -1 Program failed. * @retval -2 Invalid OTP number. * * @details Program a 64-bits data to the specified OTP. */ int32_t FMC_Write_OTP(uint32_t u32OtpNum, uint32_t u32LowWord, uint32_t u32HighWord) { int32_t ret = 0; if(u32OtpNum > 255UL) { ret = -2; } if(ret == 0) { FMC->ISPCMD = FMC_ISPCMD_PROGRAM; FMC->ISPADDR = FMC_OTP_BASE + u32OtpNum * 8UL; FMC->ISPDAT = u32LowWord; 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 + u32OtpNum * 8UL + 4UL; FMC->ISPDAT = u32HighWord; 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; } /*@}*/ /* end of group FMC_EXPORTED_FUNCTIONS */ /*@}*/ /* end of group FMC_Driver */ /*@}*/ /* end of group Standard_Driver */ /*** (C) COPYRIGHT 2019 Nuvoton Technology Corp. ***/