| /* |
| * Copyright (c) 2023-2024 Analog Devices, Inc. |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT adi_max32_flash_controller |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/flash.h> |
| #include <zephyr/init.h> |
| |
| #include "flc.h" |
| |
| struct max32_flash_dev_config { |
| uint32_t flash_base; |
| uint32_t flash_erase_blk_sz; |
| struct flash_parameters parameters; |
| #if CONFIG_FLASH_PAGE_LAYOUT |
| struct flash_pages_layout pages_layouts; |
| #endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
| }; |
| |
| struct max32_flash_dev_data { |
| #ifdef CONFIG_MULTITHREADING |
| struct k_sem sem; |
| #endif |
| }; |
| |
| #ifdef CONFIG_MULTITHREADING |
| static inline void max32_sem_take(const struct device *dev) |
| { |
| struct max32_flash_dev_data *data = dev->data; |
| |
| k_sem_take(&data->sem, K_FOREVER); |
| } |
| |
| static inline void max32_sem_give(const struct device *dev) |
| { |
| struct max32_flash_dev_data *data = dev->data; |
| |
| k_sem_give(&data->sem); |
| } |
| #else |
| |
| #define max32_sem_take(dev) |
| #define max32_sem_give(dev) |
| |
| #endif /* CONFIG_MULTITHREADING */ |
| |
| static int api_read(const struct device *dev, off_t address, void *buffer, size_t length) |
| { |
| const struct max32_flash_dev_config *const cfg = dev->config; |
| |
| address += cfg->flash_base; |
| MXC_FLC_Read(address, buffer, length); |
| return 0; |
| } |
| |
| static int api_write(const struct device *dev, off_t address, const void *buffer, size_t length) |
| { |
| const struct max32_flash_dev_config *const cfg = dev->config; |
| int ret = 0; |
| |
| max32_sem_take(dev); |
| |
| address += cfg->flash_base; |
| ret = MXC_FLC_Write(address, length, (uint32_t *)buffer); |
| |
| max32_sem_give(dev); |
| |
| return ret != 0 ? -EIO : 0; |
| } |
| |
| static int api_erase(const struct device *dev, off_t start, size_t len) |
| { |
| const struct max32_flash_dev_config *const cfg = dev->config; |
| uint32_t page_size = cfg->flash_erase_blk_sz; |
| uint32_t addr = (start + cfg->flash_base); |
| int ret = 0; |
| |
| max32_sem_take(dev); |
| |
| while (len) { |
| ret = MXC_FLC_PageErase(addr); |
| if (ret) { |
| break; |
| } |
| |
| addr += page_size; |
| if (len > page_size) { |
| len -= page_size; |
| } else { |
| len = 0; |
| } |
| } |
| |
| max32_sem_give(dev); |
| |
| return ret != 0 ? -EIO : 0; |
| } |
| |
| #if CONFIG_FLASH_PAGE_LAYOUT |
| static void api_page_layout(const struct device *dev, const struct flash_pages_layout **layout, |
| size_t *layout_size) |
| { |
| const struct max32_flash_dev_config *const cfg = dev->config; |
| |
| *layout = &cfg->pages_layouts; |
| *layout_size = 1; |
| } |
| #endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
| |
| static const struct flash_parameters *api_get_parameters(const struct device *dev) |
| { |
| const struct max32_flash_dev_config *const cfg = dev->config; |
| |
| return &cfg->parameters; |
| } |
| |
| static int flash_max32_init(const struct device *dev) |
| { |
| int ret = MXC_FLC_Init(); |
| |
| #ifdef CONFIG_MULTITHREADING |
| struct max32_flash_dev_data *data = dev->data; |
| |
| /* Mutex for flash controller */ |
| k_sem_init(&data->sem, 1, 1); |
| #endif |
| return ret != 0 ? -EIO : 0; |
| } |
| |
| static const struct flash_driver_api flash_max32_driver_api = { |
| .read = api_read, |
| .write = api_write, |
| .erase = api_erase, |
| .get_parameters = api_get_parameters, |
| #ifdef CONFIG_FLASH_PAGE_LAYOUT |
| .page_layout = api_page_layout, |
| #endif |
| }; |
| |
| #if CONFIG_FLASH_PAGE_LAYOUT |
| #define FLASH_MAX32_CONFIG_PAGE_LAYOUT(n) \ |
| .pages_layouts = { \ |
| .pages_count = DT_INST_FOREACH_CHILD(n, GET_FLASH_SIZE) / \ |
| DT_INST_FOREACH_CHILD(n, GET_ERASE_BLOCK_SIZE), \ |
| .pages_size = DT_INST_FOREACH_CHILD(n, GET_ERASE_BLOCK_SIZE), \ |
| }, |
| #else |
| #define FLASH_MAX32_CONFIG_PAGE_LAYOUT(n) |
| #endif |
| |
| #define GET_WRITE_BLOCK_SIZE(n) DT_PROP(n, write_block_size) |
| #define GET_ERASE_BLOCK_SIZE(n) DT_PROP(n, erase_block_size) |
| #define GET_FLASH_BASE(n) DT_REG_ADDR(n) |
| #define GET_FLASH_SIZE(n) DT_REG_SIZE(n) |
| |
| #define DEFINE_FLASH_MAX32(_num) \ |
| static const struct max32_flash_dev_config max32_flash_dev_cfg_##_num = { \ |
| .flash_base = DT_INST_FOREACH_CHILD(_num, GET_FLASH_BASE), \ |
| .flash_erase_blk_sz = DT_INST_FOREACH_CHILD(_num, GET_ERASE_BLOCK_SIZE), \ |
| .parameters = \ |
| { \ |
| .write_block_size = \ |
| DT_INST_FOREACH_CHILD(_num, GET_WRITE_BLOCK_SIZE), \ |
| .erase_value = 0xFF, \ |
| }, \ |
| FLASH_MAX32_CONFIG_PAGE_LAYOUT(_num)}; \ |
| static struct max32_flash_dev_data max32_flash_dev_data_##_num; \ |
| DEVICE_DT_INST_DEFINE(_num, flash_max32_init, NULL, &max32_flash_dev_data_##_num, \ |
| &max32_flash_dev_cfg_##_num, POST_KERNEL, \ |
| CONFIG_FLASH_INIT_PRIORITY, &flash_max32_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(DEFINE_FLASH_MAX32) |