diff --git a/boards/arm/numaker_pfm_m467/numaker_pfm_m467_defconfig b/boards/arm/numaker_pfm_m467/numaker_pfm_m467_defconfig index 8b9e8d75b12..abc0d144a94 100644 --- a/boards/arm/numaker_pfm_m467/numaker_pfm_m467_defconfig +++ b/boards/arm/numaker_pfm_m467/numaker_pfm_m467_defconfig @@ -20,3 +20,7 @@ CONFIG_UART_INTERRUPT_DRIVEN=y # console CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y + +# Enable FMC +CONFIG_FLASH=y +CONFIG_SOC_FLASH_NUMAKER=y diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index b78ce14e78c..fd9aff229e1 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -109,3 +109,4 @@ zephyr_library_include_directories_ifdef( zephyr_library_sources_ifdef(CONFIG_FLASH_SHELL flash_shell.c) zephyr_library_sources_ifdef(CONFIG_FLASH_JESD216 jesd216.c) zephyr_library_sources_ifdef(CONFIG_FLASH_INFINEON_CAT1 flash_ifx_cat1.c) +zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NUMAKER soc_flash_numaker.c) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index 2229d520705..8d50a77e78d 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -152,4 +152,6 @@ source "drivers/flash/Kconfig.xmc4xxx" source "drivers/flash/Kconfig.ifx_cat1" +source "drivers/flash/Kconfig.numaker" + endif # FLASH diff --git a/drivers/flash/Kconfig.numaker b/drivers/flash/Kconfig.numaker new file mode 100644 index 00000000000..98d4790589d --- /dev/null +++ b/drivers/flash/Kconfig.numaker @@ -0,0 +1,16 @@ +# NUMAKER GPIO driver configuration options + +# Copyright (c) 2023 Nuvoton Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config SOC_FLASH_NUMAKER + bool "Nuvoton NuMaker MCU flash driver" + default y + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_DRIVER_ENABLED + select HAS_NUMAKER_FMC + depends on DT_HAS_NUVOTON_NUMAKER_FMC_ENABLED + help + This option enables the FMC driver for Nuvoton NuMaker family of + processors. + Say y if you wish to enable NuMaker FMC. diff --git a/drivers/flash/soc_flash_numaker.c b/drivers/flash/soc_flash_numaker.c new file mode 100644 index 00000000000..81daec426c8 --- /dev/null +++ b/drivers/flash/soc_flash_numaker.c @@ -0,0 +1,284 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2023 Nuvoton Technology Corporation. + */ + +#define DT_DRV_COMPAT nuvoton_numaker_fmc + +#include +#include +#include +#include +#include +#include +#include "flash_priv.h" +#include + +LOG_MODULE_REGISTER(flash_numaker, CONFIG_FLASH_LOG_LEVEL); + +#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash) +#define SOC_NV_FLASH_WRITE_BLOCK_SIZE DT_PROP_OR(SOC_NV_FLASH_NODE, write_block_size, 0x04) + +struct flash_numaker_data { + FMC_T *fmc; + struct k_sem write_lock; + uint32_t flash_block_base; +}; + +static const struct flash_parameters flash_numaker_parameters = { + .write_block_size = SOC_NV_FLASH_WRITE_BLOCK_SIZE, + .erase_value = 0xff, +}; + +/* Validate offset and length */ +static bool flash_numaker_is_range_valid(off_t offset, size_t len) +{ + uint32_t aprom_size = (FMC_APROM_END - FMC_APROM_BASE); + + /* check for min value */ + if ((offset < 0) || (len == 0)) { + return false; + } + + /* check for max value */ + if (offset >= aprom_size || len > aprom_size || (aprom_size - offset) < len) { + return false; + } + + return true; +} + +/* + * Erase a flash memory area. + * + * param dev Device struct + * param offset The address's offset + * param len The size of the buffer + * return 0 on success + * return -EINVAL erroneous code + */ + +static int flash_numaker_erase(const struct device *dev, off_t offset, size_t len) +{ + struct flash_numaker_data *dev_data = dev->data; + uint32_t rc = 0; + unsigned int key; + int page_nums = (len / FMC_FLASH_PAGE_SIZE); + uint32_t addr = dev_data->flash_block_base + offset; + + /* return SUCCESS for len == 0 (required by tests/drivers/flash) */ + if (!len) { + return 0; + } + + /* Validate range */ + if (!flash_numaker_is_range_valid(offset, len)) { + return -EINVAL; + } + + /* check alignment and erase only by pages */ + if (((addr % FMC_FLASH_PAGE_SIZE) != 0) || ((len % FMC_FLASH_PAGE_SIZE) != 0)) { + return -EINVAL; + } + + /* take semaphore */ + if (k_sem_take(&dev_data->write_lock, K_NO_WAIT)) { + return -EACCES; + } + + SYS_UnlockReg(); + key = irq_lock(); + while (page_nums) { + if (((len >= FMC_BANK_SIZE)) && ((addr % FMC_BANK_SIZE) == 0)) { + if (FMC_Erase_Bank(addr)) { + LOG_ERR("Erase flash bank failed or erase time-out"); + rc = -EIO; + goto done; + } + page_nums -= (FMC_BANK_SIZE / FMC_FLASH_PAGE_SIZE); + addr += FMC_BANK_SIZE; + } else { + /* erase page */ + if (FMC_Erase(addr)) { + LOG_ERR("Erase flash page failed or erase time-out"); + rc = -EIO; + goto done; + } + page_nums--; + addr += FMC_FLASH_PAGE_SIZE; + } + } + +done: + SYS_LockReg(); + irq_unlock(key); + /* release semaphore */ + k_sem_give(&dev_data->write_lock); + + return rc; +} + +/* + * Read a flash memory area. + * + * param dev Device struct + * param offset The address's offset + * param data The buffer to store or read the value + * param length The size of the buffer + * return 0 on success, + * return -EIO erroneous code + */ +static int flash_numaker_read(const struct device *dev, off_t offset, void *data, size_t len) +{ + struct flash_numaker_data *dev_data = dev->data; + uint32_t addr = dev_data->flash_block_base + offset; + + /* return SUCCESS for len == 0 (required by tests/drivers/flash) */ + if (!len) { + return 0; + } + + /* Validate range */ + if (!flash_numaker_is_range_valid(offset, len)) { + return -EINVAL; + } + + /* read flash */ + memcpy(data, (void *)addr, len); + + return 0; +} + +static int32_t flash_numaker_block_write(uint32_t u32_addr, uint8_t *pu8_data, int block_size) +{ + int32_t retval; + uint32_t *pu32_data = (uint32_t *)pu8_data; + + SYS_UnlockReg(); + if (block_size == 4) { + retval = FMC_Write(u32_addr, *pu32_data); + } else if (block_size == 8) { + retval = FMC_Write8Bytes(u32_addr, *pu32_data, *(pu32_data + 1)); + } else { + retval = -1; + } + SYS_LockReg(); + + return retval; +} + +static int flash_numaker_write(const struct device *dev, off_t offset, const void *data, size_t len) +{ + struct flash_numaker_data *dev_data = dev->data; + uint32_t rc = 0; + unsigned int key; + uint32_t addr = dev_data->flash_block_base + offset; + int block_size = flash_numaker_parameters.write_block_size; + int blocks = (len / flash_numaker_parameters.write_block_size); + uint8_t *pu8_data = (uint8_t *)data; + + /* return SUCCESS for len == 0 (required by tests/drivers/flash) */ + if (!len) { + return 0; + } + + /* Validate range */ + if (!flash_numaker_is_range_valid(offset, len)) { + return -EINVAL; + } + + /* Validate address alignment */ + if ((addr % flash_numaker_parameters.write_block_size) != 0) { + return -EINVAL; + } + + /* Validate write size be multiples of the write block size */ + if ((len % block_size) != 0) { + return -EINVAL; + } + + /* Validate offset be multiples of the write block size */ + if ((offset % block_size) != 0) { + return -EINVAL; + } + + if (k_sem_take(&dev_data->write_lock, K_FOREVER)) { + return -EACCES; + } + + key = irq_lock(); + + while (blocks) { + if (flash_numaker_block_write(addr, pu8_data, block_size)) { + rc = -EIO; + goto done; + } + pu8_data += block_size; + addr += block_size; + blocks--; + } + +done: + irq_unlock(key); + + k_sem_give(&dev_data->write_lock); + + return rc; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static const struct flash_pages_layout dev_layout = { + .pages_count = + DT_REG_SIZE(SOC_NV_FLASH_NODE) / DT_PROP(SOC_NV_FLASH_NODE, erase_block_size), + .pages_size = DT_PROP(SOC_NV_FLASH_NODE, erase_block_size), +}; + +static void flash_numaker_pages_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + *layout = &dev_layout; + *layout_size = 1; +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +static const struct flash_parameters *flash_numaker_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + return &flash_numaker_parameters; +} + +static struct flash_numaker_data flash_data; + +static const struct flash_driver_api flash_numaker_api = { + .erase = flash_numaker_erase, + .write = flash_numaker_write, + .read = flash_numaker_read, + .get_parameters = flash_numaker_get_parameters, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = flash_numaker_pages_layout, +#endif +}; + +static int flash_numaker_init(const struct device *dev) +{ + struct flash_numaker_data *dev_data = dev->data; + + k_sem_init(&dev_data->write_lock, 1, 1); + + /* Enable FMC ISP function */ + SYS_UnlockReg(); + FMC_Open(); + /* Enable APROM update. */ + FMC_ENABLE_AP_UPDATE(); + SYS_LockReg(); + dev_data->flash_block_base = (uint32_t)FMC_APROM_BASE; + dev_data->fmc = (FMC_T *)DT_REG_ADDR(DT_NODELABEL(fmc)); + + return 0; +} + +DEVICE_DT_INST_DEFINE(0, flash_numaker_init, NULL, &flash_data, NULL, POST_KERNEL, + CONFIG_FLASH_INIT_PRIORITY, &flash_numaker_api); diff --git a/dts/arm/nuvoton/m46x.dtsi b/dts/arm/nuvoton/m46x.dtsi index d4bec675f61..b018032cc82 100644 --- a/dts/arm/nuvoton/m46x.dtsi +++ b/dts/arm/nuvoton/m46x.dtsi @@ -12,6 +12,10 @@ #include / { + chosen { + zephyr,flash-controller = &fmc; + }; + cpus { #address-cells = <1>; #size-cells = <0>; @@ -28,13 +32,6 @@ reg = <0x20000000 DT_SIZE_K(512)>; }; - flash0: flash@0 { - compatible = "soc-nv-flash"; - reg = <0 DT_SIZE_K(1024)>; - erase-block-size = <4096>; - write-block-size = <4>; - }; - sysclk: system-clock { compatible = "fixed-clock"; clock-frequency = <200000000>; @@ -65,6 +62,20 @@ status = "okay"; }; + fmc: flash-controller@4000c000 { + compatible = "nuvoton,numaker-fmc"; + reg = <0x4000c000 0x110>; + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0 DT_SIZE_K(1024)>; + erase-block-size = <4096>; + write-block-size = <4>; + }; + }; + uart0: serial@40070000 { compatible = "nuvoton,numaker-uart"; reg = <0x40070000 0x1000>; diff --git a/dts/bindings/flash_controller/nuvoton,numaker-fmc.yaml b/dts/bindings/flash_controller/nuvoton,numaker-fmc.yaml new file mode 100644 index 00000000000..8555885214d --- /dev/null +++ b/dts/bindings/flash_controller/nuvoton,numaker-fmc.yaml @@ -0,0 +1,9 @@ +description: Nuvoton NuMaker Flash Controller + +compatible: "nuvoton,numaker-fmc" + +include: flash-controller.yaml + +properties: + reg: + required: true diff --git a/west.yml b/west.yml index efefa62175f..f0a69af00ab 100644 --- a/west.yml +++ b/west.yml @@ -178,7 +178,7 @@ manifest: groups: - hal - name: hal_nuvoton - revision: 8a2b5de1670b59fcacd20d7495cb1c0f26fbe7bd + revision: 3e0a4c4d3328b2f72b164219add19d5308b53cb5 path: modules/hal/nuvoton groups: - hal