Skip to content

File ns_nvm.c

File List > apollo5 > ns_nvm.c

Go to the documentation of this file

 #include <string.h>
 #include "am_mcu_apollo.h"
 #include "am_bsp.h"
 #include "am_util.h"

 #include "ns_core.h"
 #include "ns_ambiqsuite_harness.h"
 #include "ns_nvm.h"

 /* HAL for IS25WX064 */
 #include "am_devices_mspi_is25wx064.h"

 /* ===== ns_core API versioning ===== */
 const ns_core_api_t ns_nvm_V0_0_1                 = { .apiId = NS_NVM_API_ID, .version = NS_NVM_V0_0_1 };
 const ns_core_api_t ns_nvm_oldest_supported_version = { .apiId = NS_NVM_API_ID, .version = NS_NVM_OLDEST_SUPPORTED_VERSION };
 const ns_core_api_t ns_nvm_current_version          = { .apiId = NS_NVM_API_ID, .version = NS_NVM_CURRENT_VERSION };

 /* ===== Local state ===== */

 /* Single-instance storage (mirrors how ns_psram holds handles today). */
 static void *s_flash_handle = NULL;
 static void *s_mspi_handle  = NULL;
 static uint32_t s_mspi_module = 0;
 static ns_nvm_config_t *s_cfg = NULL;

 /* DMA/TCB buffer for non-blocking transactions (user may override in cfg). */
 AM_SHARED_RW static uint32_t s_dmatcb[256];

 /* Timing config determined by scan */
 static am_devices_mspi_is25wx064_timing_config_t s_timing;

 /* Convenience: MSPI IRQs by module index */
 #if defined(apollo510L_eb)
 static const IRQn_Type s_mspi_irqs[] = {
     MSPI0_IRQn, MSPI1_IRQn, MSPI2_IRQn
 };
 #else
 static const IRQn_Type s_mspi_irqs[] = {
     MSPI0_IRQn, MSPI1_IRQn, MSPI2_IRQn, MSPI3_IRQn
 };
 #endif

 /* ===== Optional MPU setup for the DMA TCB buffer (mirrors examples) ===== */
 #if defined(apollo510_evb) || defined(apollo510L_eb) || defined(apollo510b_evb)
 /* Configure MPU attributes for the DMATCB buffer to be normal write-back etc. */
 static am_hal_mpu_region_config_t sMPUCfg = {
     .ui32RegionNumber  = 6,
     .ui32BaseAddress   = (uint32_t)s_dmatcb,
     .eShareable        = NON_SHARE,
     .eAccessPermission = RW_NONPRIV,
     .bExecuteNever     = true,
     .ui32LimitAddress  = (uint32_t)s_dmatcb + sizeof(s_dmatcb) - 1,
     .ui32AttrIndex     = 0,
     .bEnable           = true,
 };
 static am_hal_mpu_attr_t sMPUAttr = {
     .ui8AttrIndex   = 0,
     .bNormalMem     = true,
     .sOuterAttr = {
         .bNonTransient = false,
         .bWriteBack    = true,
         .bReadAllocate = false,
         .bWriteAllocate= false
     },
     .sInnerAttr = {
         .bNonTransient = false,
         .bWriteBack    = true,
         .bReadAllocate = false,
         .bWriteAllocate= false
     },
     .eDeviceAttr = 0,
 };
 static void prv_setup_mpu_for_dmatcb(void)
 {
     am_hal_mpu_attr_configure(&sMPUAttr, 1);
     am_hal_mpu_region_clear();
     am_hal_mpu_region_configure(&sMPUCfg, 1);
     am_hal_cachectrl_dcache_invalidate(NULL, true);
     am_hal_mpu_enable(true, true);
 }
 #else
 static inline void prv_setup_mpu_for_dmatcb(void) { /* no-op on non-510 EVB */ }
 #endif

 /* ===== Helpers ===== */

 static inline uint32_t aperture_base_for_module(uint32_t module)
 {
     switch (module) {
         case 0: return MSPI0_APERTURE_START_ADDR;
         case 1: return MSPI1_APERTURE_START_ADDR;
         case 2: return MSPI2_APERTURE_START_ADDR;
 #ifndef apollo510L_eb
         default: return MSPI3_APERTURE_START_ADDR;
 #else
         default: return MSPI2_APERTURE_START_ADDR;
 #endif
     }
 }

 /* The 1-8-8 CE0 enum name changed across SDKs; provide a fallback if needed. */
 #ifndef AM_HAL_MSPI_FLASH_CE0_1_8_8
 #define AM_HAL_MSPI_FLASH_CE0_1_8_8 AM_HAL_MSPI_FLASH_OCTAL_CE0_1_8_8
 #endif

 static inline am_hal_mspi_device_e map_iface_to_hal(ns_nvm_interface_e iface, uint8_t ce)
 {
     const bool ce1 = (ce == 1);
     if (iface == NS_NVM_IF_OCTAL_DDR) {
         return ce1 ? AM_HAL_MSPI_FLASH_OCTAL_DDR_CE1 : AM_HAL_MSPI_FLASH_OCTAL_DDR_CE0;
     } else {
         return ce1 ? AM_HAL_MSPI_FLASH_OCTAL_CE1_1_8_8 : AM_HAL_MSPI_FLASH_CE0_1_8_8; // NOTE: use official enum
     }
 }



 /* Build HAL config from ns_nvm_config_t. */
 static void build_hal_config(const ns_nvm_config_t *cfg, am_devices_mspi_is25wx064_config_t *out)
 {
     out->eDeviceConfig         = map_iface_to_hal(cfg->iface, cfg->chip_select);
     out->eClockFreq            = cfg->clock_freq;
     out->pNBTxnBuf             = (cfg->nbtxn_buf) ? cfg->nbtxn_buf : s_dmatcb;
     out->ui32NBTxnBufLength    = (cfg->nbtxn_buf) ? cfg->nbtxn_buf_len
                                                   : (uint32_t)(sizeof(s_dmatcb)/sizeof(uint32_t));
     out->ui32ScramblingStartAddr = cfg->scrambling_start;
     out->ui32ScramblingEndAddr   = cfg->scrambling_end;
 }

 /* ===== MSPI ISRs (provide all four; only the configured module will be enabled) ===== */
 static inline void prv_mspi_isr_common(void)
 {
     if (s_mspi_handle) {
         uint32_t status;
         am_hal_mspi_interrupt_status_get(s_mspi_handle, &status, false);
         am_hal_mspi_interrupt_clear(s_mspi_handle, status);
         am_hal_mspi_interrupt_service(s_mspi_handle, status);
     }
 }
 void am_mspi0_isr(void) { prv_mspi_isr_common(); }
 void am_mspi1_isr(void) { prv_mspi_isr_common(); }
 void am_mspi2_isr(void) { prv_mspi_isr_common(); }
 void am_mspi3_isr(void) { prv_mspi_isr_common(); }

 /* ===== Public API ===== */

 uint32_t ns_nvm_init(ns_nvm_config_t *cfg)
 {
 #ifndef NS_DISABLE_API_VALIDATION
     if (cfg == NULL) {
         return NS_STATUS_INVALID_HANDLE;
     }
     if (ns_core_check_api(cfg->api, &ns_nvm_oldest_supported_version, &ns_nvm_current_version)) {
         return NS_STATUS_INVALID_VERSION;
     }
 #endif

     if (!cfg->enable) {
         return NS_STATUS_SUCCESS;
     }

     /* Save pointers for later operations. */
     s_cfg = cfg;
     s_mspi_module = cfg->mspi_module;

     /* Minimal defaults if caller didn't set them. */
     if (cfg->clock_freq == 0) {
         cfg->clock_freq = AM_HAL_MSPI_CLK_96MHZ;
     }
     /* Default: IS25WX064 is 8MB */
     cfg->size_bytes = (8u * 1024u * 1024u);
     cfg->xip_base_address = aperture_base_for_module(cfg->mspi_module);

     /* Optional MPU setup for DMATCB buffer. */
     prv_setup_mpu_for_dmatcb();

     /* Build HAL config and run timing scan. */
     am_devices_mspi_is25wx064_config_t hal_cfg;
     build_hal_config(cfg, &hal_cfg);
     ns_lp_printf("NVM timing scan...\n");
     uint32_t status = am_devices_mspi_is25wx064_init_timing_check(cfg->mspi_module, &hal_cfg, &s_timing);
     if (status != AM_DEVICES_MSPI_IS25WX064_STATUS_SUCCESS) {
         ns_lp_printf("NVM timing scan failed (status=%u). Falling back to defaults.\n", status);
         /* Continue; HAL will apply its defaults or best-known. */
     }
     ns_lp_printf("NVM timing scan succeeded.\n");
     /* Initialize the device. */
     status = am_devices_mspi_is25wx064_init(cfg->mspi_module, &hal_cfg, &s_flash_handle, &s_mspi_handle);
     if (status != AM_DEVICES_MSPI_IS25WX064_STATUS_SUCCESS) {
         ns_lp_printf("NVM init failed (status=%u). Check wiring/pins.\n", status);
         return status;
     }
     ns_lp_printf("NVM init succeeded.\n");
     /* Hook interrupts for the selected module. */
     if (cfg->mspi_module < (sizeof(s_mspi_irqs)/sizeof(s_mspi_irqs[0]))) {
         NVIC_SetPriority(s_mspi_irqs[cfg->mspi_module], AM_IRQ_PRIORITY_DEFAULT);
         NVIC_EnableIRQ(s_mspi_irqs[cfg->mspi_module]);
         am_hal_interrupt_master_enable();
     }

     /* Apply timing (from scan if succeeded; HAL will sanity-check). */
     (void)am_devices_mspi_is25wx064_apply_ddr_timing(s_flash_handle, &s_timing);
     ns_lp_printf("NVM timing applied.\n");
     /* (Optional) verify ID and keep a friendly log. */
     (void)am_devices_mspi_is25wx064_id(s_flash_handle);
     ns_lp_printf("NVM ID verified.\n");
     /* Optionally put device into XIP. */
     if (cfg->enable_xip) {
         status = am_devices_mspi_is25wx064_enable_xip(s_flash_handle);
         if (status != AM_DEVICES_MSPI_IS25WX064_STATUS_SUCCESS) {
             ns_lp_printf("NVM XIP enable failed (status=%u).\n", status);
             return status;
         }
         /* Invalidate DCache to ensure coherent instruction/data fetch if XIP used. */
         am_hal_cachectrl_dcache_invalidate(NULL, true);
     }
     ns_lp_printf("NVM XIP enabled.\n");
     return AM_DEVICES_MSPI_IS25WX064_STATUS_SUCCESS;
 }

 uint32_t ns_nvm_read(uint32_t addr, uint8_t *buf, uint32_t len, bool wait)
 {
     if (!s_flash_handle || !buf || len == 0) {
         return NS_STATUS_INVALID_HANDLE;
     }
     return am_devices_mspi_is25wx064_read(s_flash_handle, buf, addr, len, wait);
 }

 uint32_t ns_nvm_write(uint32_t addr, const uint8_t *buf, uint32_t len, bool wait)
 {
     if (!s_flash_handle || !buf || len == 0) {
         return NS_STATUS_INVALID_HANDLE;
     }
     /* HAL signature uses non-const pointer; cast away const for driver call. */
     return am_devices_mspi_is25wx064_write(s_flash_handle, (uint8_t *)buf, addr, len, wait);
 }

 uint32_t ns_nvm_sector_erase(uint32_t sector_addr)
 {
     if (!s_flash_handle) {
         return NS_STATUS_INVALID_HANDLE;
     }
     return am_devices_mspi_is25wx064_sector_erase(s_flash_handle, sector_addr);
 }

 uint32_t ns_nvm_mass_erase(void)
 {
     if (!s_flash_handle) {
         return NS_STATUS_INVALID_HANDLE;
     }
     return am_devices_mspi_is25wx064_mass_erase(s_flash_handle);
 }

 uint32_t ns_nvm_enable_xip(void)
 {
     if (!s_flash_handle) {
         return NS_STATUS_INVALID_HANDLE;
     }
     uint32_t status = am_devices_mspi_is25wx064_enable_xip(s_flash_handle);
     if (status == AM_DEVICES_MSPI_IS25WX064_STATUS_SUCCESS) {
         am_hal_cachectrl_dcache_invalidate(NULL, true);
     }
     return status;
 }

 uint32_t ns_nvm_disable_xip(void)
 {
     if (!s_flash_handle) {
         return NS_STATUS_INVALID_HANDLE;
     }
     return am_devices_mspi_is25wx064_disable_xip(s_flash_handle);
 }