/**************************************************************************//** * * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2021-02-17 FYChou First version * ******************************************************************************/ #include #if defined(BSP_USING_FMC) #include #include "NuMicro.h" #if defined(RT_USING_FAL) #include #endif /* Private define ---------------------------------------------------------------*/ #define FMC_APROM_END (FMC_APROM_BASE + 0x80000) #define FMC_LDROM_END (FMC_LDROM_BASE + 0x2000) #define NU_GETBYTE_OFST(addr) (((addr)&0x3)*8) #define NU_GET_WALIGN(addr) ((addr)&~0x3) #define NU_GET_LSB2BIT(addr) ((addr)&0x3) /* Private typedef --------------------------------------------------------------*/ /* Private functions ------------------------------------------------------------*/ static int nu_fmc_init(void); #if defined(RT_USING_FAL) static int aprom_read(long offset, uint8_t *buf, size_t size); static int aprom_write(long offset, const uint8_t *buf, size_t size); static int aprom_erase(long offset, size_t size); static int ldrom_read(long offset, uint8_t *buf, size_t size); static int ldrom_write(long offset, const uint8_t *buf, size_t size); static int ldrom_erase(long offset, size_t size); #endif /* RT_USING_FAL */ /* Public functions -------------------------------------------------------------*/ int nu_fmc_read(long offset, uint8_t *buf, size_t size); int nu_fmc_write(long offset, const uint8_t *buf, size_t size); int nu_fmc_erase(long offset, size_t size); /* Private variables ------------------------------------------------------------*/ static rt_mutex_t g_mutex_fmc = RT_NULL; /* Public variables -------------------------------------------------------------*/ #if defined(RT_USING_FAL) const struct fal_flash_dev Onchip_aprom_flash = { "OnChip_APROM", FMC_APROM_BASE, FMC_APROM_END, FMC_FLASH_PAGE_SIZE, {NULL, aprom_read, aprom_write, aprom_erase} }; const struct fal_flash_dev Onchip_ldrom_flash = { "OnChip_LDROM", FMC_LDROM_BASE, FMC_LDROM_END, FMC_FLASH_PAGE_SIZE, {NULL, ldrom_read, ldrom_write, ldrom_erase} }; #endif /* RT_USING_FAL */ int nu_fmc_read(long addr, uint8_t *buf, size_t size) { size_t read_size = 0; uint32_t addr_end = addr + size; uint32_t isp_rdata = 0; rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); SYS_UnlockReg(); if (NU_GET_LSB2BIT(addr)) isp_rdata = FMC_Read(NU_GET_WALIGN(addr)); for (; addr < addr_end ;) { if (NU_GET_LSB2BIT(addr) == 0) { isp_rdata = FMC_Read(addr); if (addr_end - addr >= 4) { *(uint32_t *)buf = isp_rdata; addr += 4; buf += 4; read_size += 4; continue; } } *buf = isp_rdata >> NU_GETBYTE_OFST(addr); addr++; buf++; read_size++; } SYS_LockReg(); rt_mutex_release(g_mutex_fmc); return read_size; } int nu_fmc_write(long addr, const uint8_t *buf, size_t size) { size_t write_size = 0; uint32_t addr_end = addr + size; uint32_t isp_rdata = 0; rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); SYS_UnlockReg(); if (addr < FMC_APROM_END) FMC_ENABLE_AP_UPDATE(); else if ((addr < FMC_LDROM_END) && addr >= FMC_LDROM_BASE) FMC_ENABLE_LD_UPDATE(); else { goto Exit2; } if (NU_GET_LSB2BIT(addr)) isp_rdata = FMC_Read(NU_GET_WALIGN(addr)); for (; addr < addr_end ;) { if (addr_end - addr >= 4 && NU_GET_LSB2BIT(addr) == 0) { FMC_Write(addr, *((uint32_t *)buf)); addr += 4; buf += 4; write_size += 4; continue; } if ((NU_GET_LSB2BIT(addr)) == 0x0) isp_rdata = FMC_Read(NU_GET_WALIGN(addr)); isp_rdata = (isp_rdata & ~(0xFF << NU_GETBYTE_OFST(addr))) | ((*buf) << NU_GETBYTE_OFST(addr)); if ((NU_GET_LSB2BIT(addr)) == 0x3) FMC_Write(NU_GET_WALIGN(addr), isp_rdata); addr++; buf++; write_size++; } if (NU_GET_LSB2BIT(addr)) FMC_Write(NU_GET_WALIGN(addr), isp_rdata); FMC_DISABLE_AP_UPDATE(); FMC_DISABLE_LD_UPDATE(); Exit2: SYS_LockReg(); rt_mutex_release(g_mutex_fmc); return write_size; } int nu_fmc_erase(long addr, size_t size) { size_t erased_size = 0; uint32_t addrptr; uint32_t addr_end = addr + size; #if defined(NU_SUPPORT_NONALIGN) uint8_t *page_sdtemp = RT_NULL; uint8_t *page_edtemp = RT_NULL; addrptr = addr & (FMC_FLASH_PAGE_SIZE - 1); if (addrptr) { page_sdtemp = rt_malloc(addrptr); if (page_sdtemp == RT_NULL) { erased_size = 0; goto Exit3; } if (nu_fmc_read(addr & ~(FMC_FLASH_PAGE_SIZE - 1), page_sdtemp, addrptr) != addrptr) { erased_size = 0; goto Exit3; } } addrptr = addr_end & (FMC_FLASH_PAGE_SIZE - 1); if (addrptr) { page_edtemp = rt_malloc(FMC_FLASH_PAGE_SIZE - addrptr); if (page_edtemp == RT_NULL) { erased_size = 0; goto Exit3; } if (nu_fmc_read(addr_end, page_edtemp, FMC_FLASH_PAGE_SIZE - addrptr) != FMC_FLASH_PAGE_SIZE - addrptr) { erased_size = 0; goto Exit3; } } #endif rt_mutex_take(g_mutex_fmc, RT_WAITING_FOREVER); SYS_UnlockReg(); if (addr <= FMC_APROM_END) FMC_ENABLE_AP_UPDATE(); else if ((addr < FMC_LDROM_END) && addr >= FMC_LDROM_BASE) FMC_ENABLE_LD_UPDATE(); else { goto Exit2; } addrptr = (addr & ~(FMC_FLASH_PAGE_SIZE - 1)); while (addrptr < addr_end) { if (FMC_Erase(addrptr) != RT_EOK) { goto Exit1; } erased_size += FMC_FLASH_PAGE_SIZE; addrptr += FMC_FLASH_PAGE_SIZE; } Exit1: FMC_DISABLE_AP_UPDATE(); FMC_DISABLE_LD_UPDATE(); Exit2: SYS_LockReg(); rt_mutex_release(g_mutex_fmc); #if defined(NU_SUPPORT_NONALIGN) if (erased_size >= size) { addrptr = addr & (FMC_FLASH_PAGE_SIZE - 1); if (addrptr) { if (nu_fmc_write(addr & ~(FMC_FLASH_PAGE_SIZE - 1), page_sdtemp, addrptr) != addrptr) goto Exit3; erased_size += addrptr; } addrptr = addr_end & (FMC_FLASH_PAGE_SIZE - 1); if (addrptr) { if (nu_fmc_write(addr_end, page_edtemp, FMC_FLASH_PAGE_SIZE - addrptr) != FMC_FLASH_PAGE_SIZE - addrptr) goto Exit3; erased_size += FMC_FLASH_PAGE_SIZE - addrptr; } } else erased_size = 0; Exit3: if (page_sdtemp != RT_NULL) rt_free(page_sdtemp); if (page_edtemp != RT_NULL) rt_free(page_edtemp); #endif return erased_size; } #if defined(RT_USING_FAL) static int aprom_read(long offset, uint8_t *buf, size_t size) { return nu_fmc_read(Onchip_aprom_flash.addr + offset, buf, size); } static int aprom_write(long offset, const uint8_t *buf, size_t size) { return nu_fmc_write(Onchip_aprom_flash.addr + offset, buf, size); } static int aprom_erase(long offset, size_t size) { return nu_fmc_erase(Onchip_aprom_flash.addr + offset, size); } static int ldrom_read(long offset, uint8_t *buf, size_t size) { return nu_fmc_read(Onchip_ldrom_flash.addr + offset, buf, size); } static int ldrom_write(long offset, const uint8_t *buf, size_t size) { return nu_fmc_write(Onchip_ldrom_flash.addr + offset, buf, size); } static int ldrom_erase(long offset, size_t size) { return nu_fmc_erase(Onchip_ldrom_flash.addr + offset, size); } #endif /* RT_USING_FAL */ static int nu_fmc_init(void) { SYS_UnlockReg(); FMC_ENABLE_ISP(); SYS_LockReg(); g_mutex_fmc = rt_mutex_create("nu_fmc_lock", RT_IPC_FLAG_PRIO); /* RT_USING_FAL */ #if defined(RT_USING_FAL) fal_init(); #endif return (int)RT_EOK; } INIT_APP_EXPORT(nu_fmc_init); #endif /* BSP_USING_FMC */