347 lines
11 KiB
C
347 lines
11 KiB
C
/*
|
|
* Copyright 2024 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_qspi_flash
|
|
#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 "cy_serial_flash_qspi.h"
|
|
#include "cy_smif_memslot.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 = 0xFF,
|
|
};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_write_en_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_write_dis_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_erase_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_chip_erase_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_program_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_sts_reg_qe_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_sts_reg_wip_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_write_sts_reg_qe_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_sts_reg_oe_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_write_sts_reg_oe_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_latency_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_write_latency_cmd = {0};
|
|
|
|
cy_stc_smif_mem_cmd_t sfdp_slave_slot_0_read_sfdp_cmd = {
|
|
/* The 8-bit command. 1 x I/O read command. */
|
|
.command = 0x5AU,
|
|
/* The width of the command transfer. */
|
|
.cmdWidth = CY_SMIF_WIDTH_SINGLE,
|
|
/* The width of the address transfer. */
|
|
.addrWidth = CY_SMIF_WIDTH_SINGLE,
|
|
/* The 8-bit mode byte. This value is 0xFFFFFFFF when there is no mode present. */
|
|
.mode = 0xFFFFFFFFU,
|
|
/* The width of the mode command transfer. */
|
|
.modeWidth = CY_SMIF_WIDTH_SINGLE,
|
|
/* The number of dummy cycles. A zero value suggests no dummy cycles. */
|
|
.dummyCycles = 8U,
|
|
/* The width of the data transfer. */
|
|
.dataWidth = CY_SMIF_WIDTH_SINGLE,
|
|
};
|
|
|
|
cy_stc_smif_octal_ddr_en_seq_t oe_sequence_SFDP_SlaveSlot_0 = {
|
|
.cmdSeq1Len = CY_SMIF_SFDP_ODDR_CMD_SEQ_MAX_LEN,
|
|
.cmdSeq2Len = CY_SMIF_SFDP_ODDR_CMD_SEQ_MAX_LEN,
|
|
.cmdSeq1 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
.cmdSeq2 = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
};
|
|
|
|
/* Support for memories with hybrid regions is added in the version 1.50
|
|
* Please refer to the changelog in
|
|
* https://iot-webserver.aus.cypress.com/projects/iot_release/
|
|
* ASSETS/repo/mtb-pdl-cat1/develop/Latest/deploy/docs/
|
|
* pdl_api_reference_manual/html/group__group__smif.html
|
|
* for more details
|
|
*/
|
|
#if (CY_SMIF_DRV_VERSION_MAJOR > 1) && (CY_SMIF_DRV_VERSION_MINOR >= 50)
|
|
static cy_stc_smif_hybrid_region_info_t sfdp_slave_slot_0_region_info_storage[16];
|
|
|
|
#define GENERATE_REGION_INFO_PTR(index, _) &sfdp_slave_slot_0_region_info_storage[index],
|
|
|
|
static cy_stc_smif_hybrid_region_info_t *sfdp_slave_slot_0_region_info[16] = {
|
|
LISTIFY(16, GENERATE_REGION_INFO_PTR, ())};
|
|
#endif
|
|
|
|
cy_stc_smif_mem_device_cfg_t deviceCfg_SFDP_SlaveSlot_0 = {
|
|
/* Specifies the number of address bytes used by the memory slave device. */
|
|
.numOfAddrBytes = 0x03U,
|
|
/* The size of the memory. */
|
|
.memSize = 0x0000100U,
|
|
/* Specifies the Read command. */
|
|
.readCmd = &sfdp_slave_slot_0_read_cmd,
|
|
/* Specifies the Write Enable command. */
|
|
.writeEnCmd = &sfdp_slave_slot_0_write_en_cmd,
|
|
/* Specifies the Write Disable command. */
|
|
.writeDisCmd = &sfdp_slave_slot_0_write_dis_cmd,
|
|
/* Specifies the Erase command. */
|
|
.eraseCmd = &sfdp_slave_slot_0_erase_cmd,
|
|
/* Specifies the sector size of each erase. */
|
|
.eraseSize = 0x0001000U,
|
|
/* Specifies the Chip Erase command. */
|
|
.chipEraseCmd = &sfdp_slave_slot_0_chip_erase_cmd,
|
|
/* Specifies the Program command. */
|
|
.programCmd = &sfdp_slave_slot_0_program_cmd,
|
|
/* Specifies the page size for programming. */
|
|
.programSize = 0x0000100U,
|
|
/* Specifies the command to read the QE-containing status register. */
|
|
.readStsRegQeCmd = &sfdp_slave_slot_0_read_sts_reg_qe_cmd,
|
|
/* Specifies the command to read the WIP-containing status register. */
|
|
.readStsRegWipCmd = &sfdp_slave_slot_0_read_sts_reg_wip_cmd,
|
|
/* Specifies the read SFDP command */
|
|
.readSfdpCmd = &sfdp_slave_slot_0_read_sfdp_cmd,
|
|
/* Specifies the command to write into the QE-containing status register. */
|
|
.writeStsRegQeCmd = &sfdp_slave_slot_0_write_sts_reg_qe_cmd,
|
|
/* The mask for the status register. */
|
|
.stsRegBusyMask = 0x00U,
|
|
/* The mask for the status register. */
|
|
.stsRegQuadEnableMask = 0x00U,
|
|
/* The max time for the erase type-1 cycle-time in ms. */
|
|
.eraseTime = 1U,
|
|
/* The max time for the chip-erase cycle-time in ms. */
|
|
.chipEraseTime = 16U,
|
|
/* The max time for the page-program cycle-time in us. */
|
|
.programTime = 8U,
|
|
#if (CY_SMIF_DRV_VERSION_MAJOR > 1) && (CY_SMIF_DRV_VERSION_MINOR >= 50)
|
|
/* Points to NULL or to structure with info about sectors for hybrid memory. */
|
|
.hybridRegionCount = 0U,
|
|
.hybridRegionInfo = sfdp_slave_slot_0_region_info,
|
|
#endif
|
|
/* Specifies the command to read variable latency cycles configuration register */
|
|
.readLatencyCmd = &sfdp_slave_slot_0_read_latency_cmd,
|
|
/* Specifies the command to write variable latency cycles configuration register */
|
|
.writeLatencyCmd = &sfdp_slave_slot_0_write_latency_cmd,
|
|
/* Specifies the address for variable latency cycle address */
|
|
.latencyCyclesRegAddr = 0x00U,
|
|
/* Specifies variable latency cycles Mask */
|
|
.latencyCyclesMask = 0x00U,
|
|
/* Specifies data for memory with hybrid sectors */
|
|
.octalDDREnableSeq = &oe_sequence_SFDP_SlaveSlot_0,
|
|
/* Specifies the command to read the OE-containing status register. */
|
|
.readStsRegOeCmd = &sfdp_slave_slot_0_read_sts_reg_oe_cmd,
|
|
/* Specifies the command to write the OE-containing status register. */
|
|
.writeStsRegOeCmd = &sfdp_slave_slot_0_write_sts_reg_oe_cmd,
|
|
/* QE mask for the status registers */
|
|
.stsRegOctalEnableMask = 0x00U,
|
|
/* Octal enable register address */
|
|
.octalEnableRegAddr = 0x00U,
|
|
/* Frequency of operation used in Octal mode */
|
|
.freq_of_operation = CY_SMIF_100MHZ_OPERATION,
|
|
};
|
|
|
|
cy_stc_smif_mem_config_t sfdp_slave_slot_0 = {
|
|
/* Determines the slot number where the memory device is placed. */
|
|
.slaveSelect = CY_SMIF_SLAVE_SELECT_0,
|
|
/* Flags. */
|
|
.flags = CY_SMIF_FLAG_SMIF_REV_3 | CY_SMIF_FLAG_MEMORY_MAPPED | CY_SMIF_FLAG_WR_EN |
|
|
CY_SMIF_FLAG_DETECT_SFDP | CY_SMIF_FLAG_MERGE_ENABLE,
|
|
/* The data-line selection options for a slave device. */
|
|
.dataSelect = CY_SMIF_DATA_SEL0,
|
|
/* The base address the memory slave
|
|
* Valid when the memory-mapped mode is enabled.
|
|
*/
|
|
.baseAddress = 0x60000000U,
|
|
/* The size allocated in the memory map, for the memory slave device.
|
|
* The size is allocated from the base address. Valid when the memory mapped mode is
|
|
* enabled.
|
|
*/
|
|
.memMappedSize = 0x100000U,
|
|
/* If this memory device is one of the devices in the dual quad SPI configuration.
|
|
* Valid when the memory mapped mode is enabled.
|
|
*/
|
|
.dualQuadSlots = 0,
|
|
/* The configuration of the device. */
|
|
.deviceCfg = &deviceCfg_SFDP_SlaveSlot_0,
|
|
/** Continuous transfer merge timeout.
|
|
* After this period the memory device is deselected. A later transfer, even from a
|
|
* continuous address, starts with the overhead phases (command, address, mode, dummy
|
|
* cycles). This configuration parameter is available for CAT1B devices.
|
|
*/
|
|
.mergeTimeout = CY_SMIF_MERGE_TIMEOUT_1_CYCLE,
|
|
};
|
|
|
|
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)
|
|
{
|
|
cy_rslt_t rslt = CY_RSLT_SUCCESS;
|
|
int ret = 0;
|
|
|
|
if (!data_len) {
|
|
return 0;
|
|
}
|
|
|
|
flash_ifx_sem_take(dev);
|
|
|
|
rslt = cy_serial_flash_qspi_read(offset, data_len, data);
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
LOG_ERR("Error reading @ %lu (Err:0x%x)", offset, rslt);
|
|
ret = -EIO;
|
|
}
|
|
|
|
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)
|
|
{
|
|
cy_rslt_t rslt = CY_RSLT_SUCCESS;
|
|
int ret = 0;
|
|
|
|
if (data_len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (offset < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
flash_ifx_sem_take(dev);
|
|
|
|
rslt = cy_serial_flash_qspi_write(offset, data_len, data);
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
LOG_ERR("Error in writing @ %lu (Err:0x%x)", offset, rslt);
|
|
ret = -EIO;
|
|
}
|
|
|
|
flash_ifx_sem_give(dev);
|
|
return ret;
|
|
}
|
|
|
|
static int ifx_cat1_flash_erase(const struct device *dev, off_t offset, size_t size)
|
|
{
|
|
cy_rslt_t rslt;
|
|
int ret = 0;
|
|
|
|
if (offset < 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
flash_ifx_sem_take(dev);
|
|
|
|
rslt = cy_serial_flash_qspi_erase(offset, size);
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
LOG_ERR("Error in erasing : 0x%x", rslt);
|
|
ret = -EIO;
|
|
}
|
|
|
|
flash_ifx_sem_give(dev);
|
|
return ret;
|
|
}
|
|
|
|
#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 = cy_serial_flash_qspi_init(&sfdp_slave_slot_0, NC, NC, NC, NC, NC, NC, NC, NC, NC, NC,
|
|
50000000lu);
|
|
if (rslt != CY_RSLT_SUCCESS) {
|
|
LOG_ERR("Serial Flash initialization failed [rslt: 0x%x]", rslt);
|
|
}
|
|
|
|
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 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, &flash_config, POST_KERNEL,
|
|
CONFIG_FLASH_INIT_PRIORITY, &ifx_cat1_flash_driver_api);
|