drivers: flash: Add Ambiq flash controller driver.
This commit adds flash controller driver for Ambiq Apollo4 SoCs. Signed-off-by: Aaron Ye <aye@ambiq.com>
This commit is contained in:
parent
a6bfbedbf5
commit
51d5794387
|
@ -51,6 +51,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_CAD_QSPI_NOR flash_cadence_qspi_nor.c
|
|||
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_XMC4XXX soc_flash_xmc4xxx.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_FLASH_RPI_PICO flash_rpi_pico.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_FLASH_ANDES_QSPI flash_andes_qspi.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_FLASH_AMBIQ flash_ambiq.c)
|
||||
|
||||
if(CONFIG_FLASH_MCUX_FLEXSPI_XIP)
|
||||
dt_chosen(chosen_flash PROPERTY "zephyr,flash")
|
||||
|
|
|
@ -159,4 +159,6 @@ source "drivers/flash/Kconfig.nxp_s32"
|
|||
|
||||
source "drivers/flash/Kconfig.andes"
|
||||
|
||||
source "drivers/flash/Kconfig.ambiq"
|
||||
|
||||
endif # FLASH
|
||||
|
|
12
drivers/flash/Kconfig.ambiq
Normal file
12
drivers/flash/Kconfig.ambiq
Normal file
|
@ -0,0 +1,12 @@
|
|||
# Copyright (c) 2023 Ambiq Micro Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config FLASH_AMBIQ
|
||||
bool "Ambiq flash driver on MRAM"
|
||||
default y
|
||||
depends on DT_HAS_AMBIQ_FLASH_CONTROLLER_ENABLED
|
||||
select AMBIQ_HAL
|
||||
select FLASH_HAS_PAGE_LAYOUT
|
||||
select FLASH_HAS_DRIVER_ENABLED
|
||||
help
|
||||
Enables Ambiq flash driver on MRAM.
|
191
drivers/flash/flash_ambiq.c
Normal file
191
drivers/flash/flash_ambiq.c
Normal file
|
@ -0,0 +1,191 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Ambiq Micro Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT ambiq_flash_controller
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/devicetree.h>
|
||||
#include <zephyr/drivers/flash.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
#include <am_mcu_apollo.h>
|
||||
|
||||
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);
|
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2023, Ambiq Micro Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Ambiq flash controller
|
||||
|
||||
compatible: "ambiq,flash-controller"
|
||||
|
||||
include: flash-controller.yaml
|
Loading…
Reference in a new issue