blob: 148c1d337914e4906e151f1cb8f109849ceb46fa [file] [log] [blame]
/*
* 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 const struct flash_driver_api 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);