You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
204 lines
5.2 KiB
204 lines
5.2 KiB
/* |
|
* Copyright 2023 Cypress Semiconductor Corporation (an Infineon company) or |
|
* an affiliate of Cypress Semiconductor Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT infineon_cat1_flash_controller |
|
#define SOC_NV_FLASH_NODE DT_PARENT(DT_INST(0, fixed_partitions)) |
|
|
|
#define PAGE_LEN DT_PROP(SOC_NV_FLASH_NODE, erase_block_size) |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/devicetree.h> |
|
#include <zephyr/drivers/flash.h> |
|
#include <zephyr/logging/log.h> |
|
|
|
#include "cyhal_flash.h" |
|
|
|
LOG_MODULE_REGISTER(flash_infineon_cat1, CONFIG_FLASH_LOG_LEVEL); |
|
|
|
/* Device config structure */ |
|
struct ifx_cat1_flash_config { |
|
uint32_t base_addr; |
|
uint32_t max_addr; |
|
}; |
|
|
|
/* Data structure */ |
|
struct ifx_cat1_flash_data { |
|
cyhal_flash_t flash_obj; |
|
struct k_sem sem; |
|
}; |
|
|
|
static struct flash_parameters ifx_cat1_flash_parameters = { |
|
.write_block_size = DT_PROP(SOC_NV_FLASH_NODE, write_block_size), |
|
.erase_value = 0x00, |
|
}; |
|
|
|
static inline void flash_ifx_sem_take(const struct device *dev) |
|
{ |
|
struct ifx_cat1_flash_data *data = dev->data; |
|
|
|
k_sem_take(&data->sem, K_FOREVER); |
|
} |
|
|
|
static inline void flash_ifx_sem_give(const struct device *dev) |
|
{ |
|
struct ifx_cat1_flash_data *data = dev->data; |
|
|
|
k_sem_give(&data->sem); |
|
} |
|
|
|
static int ifx_cat1_flash_read(const struct device *dev, off_t offset, void *data, size_t data_len) |
|
{ |
|
struct ifx_cat1_flash_data *dev_data = dev->data; |
|
const struct ifx_cat1_flash_config *dev_config = dev->config; |
|
uint32_t read_offset = dev_config->base_addr + offset; |
|
cy_rslt_t rslt = CY_RSLT_SUCCESS; |
|
int ret = 0; |
|
|
|
flash_ifx_sem_take(dev); |
|
|
|
rslt = cyhal_flash_read(&dev_data->flash_obj, read_offset, (uint8_t *)data, data_len); |
|
if (rslt != CY_RSLT_SUCCESS) { |
|
LOG_ERR("Error reading @ 0x%x (Err:0x%x)", read_offset, rslt); |
|
ret = -EIO; |
|
goto out; |
|
} |
|
|
|
out: |
|
flash_ifx_sem_give(dev); |
|
return ret; |
|
} |
|
|
|
static int ifx_cat1_flash_write(const struct device *dev, off_t offset, const void *data, |
|
size_t data_len) |
|
{ |
|
struct ifx_cat1_flash_data *dev_data = dev->data; |
|
const struct ifx_cat1_flash_config *dev_config = dev->config; |
|
uint32_t write_offset = dev_config->base_addr + (uint32_t)offset; |
|
const uint8_t *data_ptr = (const uint8_t *)data; |
|
cy_rslt_t rslt = CY_RSLT_SUCCESS; |
|
int ret = 0; |
|
|
|
if (data_len == 0) { |
|
return 0; |
|
} |
|
|
|
if ((offset < 0) || (data_len % PAGE_LEN != 0) || (offset % PAGE_LEN != 0)) { |
|
return -EINVAL; |
|
} |
|
|
|
flash_ifx_sem_take(dev); |
|
|
|
while (data_len) { |
|
rslt = cyhal_flash_write(&dev_data->flash_obj, write_offset, |
|
(const uint32_t *)data_ptr); |
|
if (rslt != CY_RSLT_SUCCESS) { |
|
LOG_ERR("Error in writing @ 0x%x (Err:0x%x)", write_offset, rslt); |
|
ret = -EIO; |
|
goto out; |
|
} |
|
|
|
data_ptr += PAGE_LEN; |
|
write_offset += PAGE_LEN; |
|
data_len -= PAGE_LEN; |
|
} |
|
|
|
out: |
|
flash_ifx_sem_give(dev); |
|
return ret; |
|
} |
|
|
|
static int ifx_cat1_flash_erase(const struct device *dev, off_t offset, size_t size) |
|
{ |
|
struct ifx_cat1_flash_data *data = dev->data; |
|
const struct ifx_cat1_flash_config *config = dev->config; |
|
uint32_t erase_offset = config->base_addr + (uint32_t)offset; |
|
cy_rslt_t rslt; |
|
|
|
if ((offset < 0) || ((offset % PAGE_LEN) != 0)) { |
|
return -EINVAL; |
|
} |
|
|
|
if (((erase_offset + size) > config->max_addr) || ((size % PAGE_LEN) != 0)) { |
|
return -EINVAL; |
|
} |
|
|
|
while (size) { |
|
rslt = cyhal_flash_erase(&data->flash_obj, erase_offset); |
|
if (rslt != CY_RSLT_SUCCESS) { |
|
LOG_ERR("Error in erasing : 0x%x", rslt); |
|
return -EIO; |
|
} |
|
|
|
size -= PAGE_LEN; |
|
erase_offset += PAGE_LEN; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#if CONFIG_FLASH_PAGE_LAYOUT |
|
static const struct flash_pages_layout ifx_cat1_flash_pages_layout = { |
|
.pages_count = DT_REG_SIZE(SOC_NV_FLASH_NODE) / PAGE_LEN, |
|
.pages_size = PAGE_LEN, |
|
}; |
|
|
|
static void ifx_cat1_flash_page_layout(const struct device *dev, |
|
const struct flash_pages_layout **layout, |
|
size_t *layout_size) |
|
{ |
|
*layout = &ifx_cat1_flash_pages_layout; |
|
|
|
/* |
|
* For flash memories which have uniform page sizes, this routine |
|
* returns an array of length 1, which specifies the page size and |
|
* number of pages in the memory. |
|
*/ |
|
*layout_size = 1; |
|
} |
|
#endif /* CONFIG_FLASH_PAGE_LAYOUT */ |
|
|
|
static const struct flash_parameters *ifx_cat1_flash_get_parameters(const struct device *dev) |
|
{ |
|
ARG_UNUSED(dev); |
|
|
|
return &ifx_cat1_flash_parameters; |
|
} |
|
|
|
static int ifx_cat1_flash_init(const struct device *dev) |
|
{ |
|
struct ifx_cat1_flash_data *data = dev->data; |
|
cy_rslt_t rslt = CY_RSLT_SUCCESS; |
|
|
|
rslt = cyhal_flash_init(&data->flash_obj); |
|
if (rslt != CY_RSLT_SUCCESS) { |
|
LOG_ERR("Failed to init flash hal driver (Err:0x%x)", rslt); |
|
return -EIO; |
|
} |
|
|
|
k_sem_init(&data->sem, 1, 1); |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(flash, ifx_cat1_flash_driver_api) = { |
|
.read = ifx_cat1_flash_read, |
|
.write = ifx_cat1_flash_write, |
|
.erase = ifx_cat1_flash_erase, |
|
.get_parameters = ifx_cat1_flash_get_parameters, |
|
#ifdef CONFIG_FLASH_PAGE_LAYOUT |
|
.page_layout = ifx_cat1_flash_page_layout, |
|
#endif |
|
}; |
|
|
|
static struct ifx_cat1_flash_data flash_data; |
|
|
|
static const struct ifx_cat1_flash_config ifx_cat1_flash_config = { |
|
.base_addr = DT_REG_ADDR(SOC_NV_FLASH_NODE), |
|
.max_addr = DT_REG_ADDR(SOC_NV_FLASH_NODE) + DT_REG_SIZE(SOC_NV_FLASH_NODE)}; |
|
|
|
DEVICE_DT_INST_DEFINE(0, ifx_cat1_flash_init, NULL, &flash_data, &ifx_cat1_flash_config, |
|
POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY, &ifx_cat1_flash_driver_api);
|
|
|