/* * Copyright (c) 2023 Ambiq Micro Inc. * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT ambiq_flash_controller #include #include #include #include #include LOG_MODULE_REGISTER(flash_ambiq, CONFIG_FLASH_LOG_LEVEL); #define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) #define SOC_NV_FLASH_ADDR DT_REG_ADDR(SOC_NV_FLASH_NODE) #define SOC_NV_FLASH_SIZE DT_REG_SIZE(SOC_NV_FLASH_NODE) #define MIN_WRITE_SIZE 16 #define FLASH_WRITE_BLOCK_SIZE MAX(DT_PROP(SOC_NV_FLASH_NODE, write_block_size), MIN_WRITE_SIZE) #define FLASH_ERASE_BLOCK_SIZE DT_PROP(SOC_NV_FLASH_NODE, erase_block_size) BUILD_ASSERT((FLASH_WRITE_BLOCK_SIZE & (MIN_WRITE_SIZE - 1)) == 0, "The flash write block size must be a multiple of 16!"); #define FLASH_ERASE_BYTE 0xFF #define FLASH_ERASE_WORD \ (((uint32_t)(FLASH_ERASE_BYTE << 24)) | ((uint32_t)(FLASH_ERASE_BYTE << 16)) | \ ((uint32_t)(FLASH_ERASE_BYTE << 8)) | ((uint32_t)FLASH_ERASE_BYTE)) #if defined(CONFIG_MULTITHREADING) static struct k_sem flash_ambiq_sem; #define FLASH_SEM_INIT() k_sem_init(&flash_ambiq_sem, 1, 1) #define FLASH_SEM_TAKE() k_sem_take(&flash_ambiq_sem, K_FOREVER) #define FLASH_SEM_GIVE() k_sem_give(&flash_ambiq_sem) #else #define FLASH_SEM_INIT() #define FLASH_SEM_TAKE() #define FLASH_SEM_GIVE() #endif /* CONFIG_MULTITHREADING */ static const struct flash_parameters flash_ambiq_parameters = { .write_block_size = FLASH_WRITE_BLOCK_SIZE, .erase_value = FLASH_ERASE_BYTE, }; static bool flash_ambiq_valid_range(off_t offset, size_t len) { if ((offset < 0) || offset >= SOC_NV_FLASH_SIZE || (SOC_NV_FLASH_SIZE - offset) < len) { return false; } return true; } static int flash_ambiq_read(const struct device *dev, off_t offset, void *data, size_t len) { ARG_UNUSED(dev); if (!flash_ambiq_valid_range(offset, len)) { return -EINVAL; } if (len == 0) { return 0; } memcpy(data, (uint8_t *)(SOC_NV_FLASH_ADDR + offset), len); return 0; } static int flash_ambiq_write(const struct device *dev, off_t offset, const void *data, size_t len) { ARG_UNUSED(dev); int ret = 0; uint32_t critical = 0; uint32_t aligned[FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t)] = {0}; uint32_t *src = (uint32_t *)data; /* write address must be block size aligned and the write length must be multiple of block * size. */ if (!flash_ambiq_valid_range(offset, len) || ((uint32_t)offset & (FLASH_WRITE_BLOCK_SIZE - 1)) || (len & (FLASH_WRITE_BLOCK_SIZE - 1))) { return -EINVAL; } if (len == 0) { return 0; } FLASH_SEM_TAKE(); critical = am_hal_interrupt_master_disable(); for (int i = 0; i < len / FLASH_WRITE_BLOCK_SIZE; i++) { for (int j = 0; j < FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t); j++) { /* Make sure the source data is 4-byte aligned. */ aligned[j] = UNALIGNED_GET((uint32_t *)src); src++; } ret = am_hal_mram_main_program( AM_HAL_MRAM_PROGRAM_KEY, aligned, (uint32_t *)(SOC_NV_FLASH_ADDR + offset + i * FLASH_WRITE_BLOCK_SIZE), FLASH_WRITE_BLOCK_SIZE / sizeof(uint32_t)); if (ret) { break; } } am_hal_interrupt_master_set(critical); FLASH_SEM_GIVE(); return ret; } static int flash_ambiq_erase(const struct device *dev, off_t offset, size_t len) { ARG_UNUSED(dev); int ret = 0; if (!flash_ambiq_valid_range(offset, len)) { return -EINVAL; } /* The erase address and length alignment check will be done in HAL.*/ if (len == 0) { return 0; } FLASH_SEM_TAKE(); ret = am_hal_mram_main_fill(AM_HAL_MRAM_PROGRAM_KEY, FLASH_ERASE_WORD, (uint32_t *)(SOC_NV_FLASH_ADDR + offset), (len / sizeof(uint32_t))); FLASH_SEM_GIVE(); return ret; } static const struct flash_parameters *flash_ambiq_get_parameters(const struct device *dev) { ARG_UNUSED(dev); return &flash_ambiq_parameters; } #if CONFIG_FLASH_PAGE_LAYOUT static const struct flash_pages_layout pages_layout = { .pages_count = SOC_NV_FLASH_SIZE / FLASH_ERASE_BLOCK_SIZE, .pages_size = FLASH_ERASE_BLOCK_SIZE, }; static void flash_ambiq_pages_layout(const struct device *dev, const struct flash_pages_layout **layout, size_t *layout_size) { ARG_UNUSED(dev); *layout = &pages_layout; *layout_size = 1; } #endif /* CONFIG_FLASH_PAGE_LAYOUT */ static const struct flash_driver_api flash_ambiq_driver_api = { .read = flash_ambiq_read, .write = flash_ambiq_write, .erase = flash_ambiq_erase, .get_parameters = flash_ambiq_get_parameters, #ifdef CONFIG_FLASH_PAGE_LAYOUT .page_layout = flash_ambiq_pages_layout, #endif }; static int flash_ambiq_init(const struct device *dev) { ARG_UNUSED(dev); FLASH_SEM_INIT(); return 0; } DEVICE_DT_INST_DEFINE(0, flash_ambiq_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &flash_ambiq_driver_api);