diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 5607ccd055852..c5569f7b6db89 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -1,3 +1,5 @@ +# Copyright (c) 2025 Siemens Mobility GmbH +# # SPDX-License-Identifier: Apache-2.0 zephyr_library_sources(flash_util.c) @@ -38,6 +40,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_NOR flash_mcux_flexspi_no zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_XSPI flash_mcux_xspi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_ATXP032 flash_mspi_atxp032.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_EMUL_DEVICE flash_mspi_emul_device.c) +zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_INFINEON_S25H flash_mspi_infineon_s25h.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_IS25XX0XX flash_mspi_is25xX0xx.c) zephyr_library_sources_ifdef(CONFIG_FLASH_MSPI_NOR flash_mspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_NOR flash_npcx_fiu_nor.c) diff --git a/drivers/flash/Kconfig.mspi b/drivers/flash/Kconfig.mspi index b48b89d05ed41..c23ec3e2834b7 100644 --- a/drivers/flash/Kconfig.mspi +++ b/drivers/flash/Kconfig.mspi @@ -1,4 +1,5 @@ # Copyright (c) 2025 Ambiq Micro Inc. +# Copyright (c) 2025 Siemens Mobility GmbH # SPDX-License-Identifier: Apache-2.0 menu "MSPI flash device driver" @@ -81,6 +82,14 @@ config FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE endif # FLASH_MSPI_NOR +config FLASH_MSPI_INFINEON_S25H + bool "MSPI Infineon S25H driver" + default y + depends on DT_HAS_INFINEON_S25H_FLASH_ENABLED + select FLASH_MSPI + select FLASH_HAS_PAGE_LAYOUT + select FLASH_HAS_EXPLICIT_ERASE + endmenu if FLASH_MSPI diff --git a/drivers/flash/flash_mspi_infineon_s25h.c b/drivers/flash/flash_mspi_infineon_s25h.c new file mode 100644 index 0000000000000..0b47417603b51 --- /dev/null +++ b/drivers/flash/flash_mspi_infineon_s25h.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 2025 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT infineon_s25h_flash + +#include "flash_mspi_infineon_s25h.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(flash_mspi_infineon_s25h, CONFIG_FLASH_LOG_LEVEL); + +struct flash_mspi_infineon_s25h_cfg { + DEVICE_MMIO_ROM; + const struct device *bus; + const struct pinctrl_dev_config *pinctrl; + k_timeout_t reset_startup_duration; + + const struct mspi_dev_cfg mspi_dev_cfg; + + const struct flash_pages_layout page_layout; + const struct flash_parameters parameters; + + const struct mspi_dev_id dev_id; + + uint16_t jedec_device_id; + uint8_t jedec_manufacturer_id; + + bool stay_in_startup_mspi_config; +}; + +struct flash_mspi_infineon_s25h_data { + struct mspi_dev_cfg mspi_dev_cfg; + uint8_t read_jedec_cmd; + uint8_t read_flash_cmd; + uint8_t read_flash_dummy_cycles; +}; + +static int flash_mspi_infineon_s25h_prepare_mspi_bus(const struct device *dev) +{ + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *data = dev->data; + + return mspi_dev_config(config->bus, &config->dev_id, + MSPI_DEVICE_CONFIG_CE_NUM | MSPI_DEVICE_CONFIG_IO_MODE | + MSPI_DEVICE_CONFIG_CPP | MSPI_DEVICE_CONFIG_CE_POL | + MSPI_DEVICE_CONFIG_DQS | MSPI_DEVICE_CONFIG_DATA_RATE | + MSPI_DEVICE_CONFIG_ENDIAN, + &data->mspi_dev_cfg); +} + +static int flash_mspi_infineon_s25h_reset(const struct device *dev) +{ + int ret = 0; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + + const struct mspi_xfer_packet reset_packets[] = { + { + .dir = MSPI_TX, + .cmd = INF_MSPI_S25H_OPCODE_RESET_ENABLE, + .num_bytes = 0, + }, + { + .dir = MSPI_TX, + .cmd = INF_MSPI_S25H_OPCODE_RESET, + .num_bytes = 0, + }}; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .rx_dummy = 0, + .addr_length = 0, + .num_packet = 2, + .packets = reset_packets, + .timeout = INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT, + }; + + ret = mspi_transceive(config->bus, &config->dev_id, &xfer); + if (ret < 0) { + return ret; + } + + k_sleep(config->reset_startup_duration); + + return 0; +} + +static int flash_mspi_infineon_s25h_set_writing_forbidden(const struct device *dev, + bool writing_forbidden) +{ + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + uint8_t cmd = writing_forbidden ? INF_MSPI_S25H_OPCODE_WRITE_DISABLE + : INF_MSPI_S25H_OPCODE_WRITE_ENABLE; + const struct mspi_xfer_packet packet = { + .dir = MSPI_TX, + .cmd = cmd, + .num_bytes = 0, + }; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA_SINGLE_CMD, + .packets = &packet, + .timeout = INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT, + }; + + return mspi_transceive(config->bus, &config->dev_id, &xfer); +} + +static int flash_mspi_infineon_s25h_rw_any_register(const struct device *dev, uint32_t address, + uint8_t *value, uint32_t delay, + enum mspi_xfer_direction dir) +{ + int ret; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *dev_data = dev->data; + uint32_t cmd; + uint32_t rx_dummy; + + if (dir == MSPI_TX) { + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, false); + if (ret < 0) { + LOG_ERR("Error disabling write protection before changing configuration"); + return ret; + } + } + + if (dir == MSPI_RX) { + cmd = INF_MSPI_S25H_OPCODE_READ_ANY_REGISTER; + rx_dummy = delay; + } else { + cmd = INF_MSPI_S25H_OPCODE_WRITE_ANY_REGISTER; + rx_dummy = 0; + } + + const struct mspi_xfer_packet packet = { + .dir = dir, + .cmd = cmd, + .num_bytes = 1, + .data_buf = value, + .address = address, + }; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .addr_length = dev_data->mspi_dev_cfg.addr_length, + .rx_dummy = rx_dummy, + .packets = &packet, + .num_packet = 1, + .timeout = INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT, + }; + + return mspi_transceive(config->bus, &config->dev_id, &xfer); +} + +static int flash_mspi_infineon_s25h_is_write_protection_enabled(const struct device *dev, + uint8_t *is_enabled) +{ + int ret = 0; + uint8_t val = 0; + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_1, + &val, 0, MSPI_RX); + if (ret < 0) { + return ret; + } + *is_enabled = (val & INF_MSPI_S25H_STATUS_1_WRPGEN_BIT); + return 0; +} + +static int flash_mspi_infineon_s25h_wait_for_idle(const struct device *dev, uint32_t timeout_ms) +{ + int ret = 0; + uint8_t status_1 = 0; + uint32_t retries = timeout_ms / INF_MSPI_S25H_TIMEOUT_IDLE_RETRY_INTERVAL_MS; + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_1, + &status_1, 0, MSPI_RX); + if (ret < 0) { + return ret; + } + + while ((status_1 & INF_MSPI_S25H_STATUS_1_RDYBSY_BIT) != 0 && retries > 0) { + k_sleep(INF_MSPI_S25H_TIMEOUT_IDLE_RETRY_INTERVAL); + ret = flash_mspi_infineon_s25h_rw_any_register( + dev, INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_1, &status_1, 0, MSPI_RX); + if (ret < 0) { + return ret; + } + --retries; + } + + if (retries == 0) { + LOG_ERR("Waiting for flash to enter idle. Status 1 register: 0x%X", status_1); + return -EIO; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_read_jedec_id(const struct device *dev, uint8_t *buf) +{ + int ret = 0; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *data = dev->data; + + const struct mspi_xfer_packet packet = { + .dir = MSPI_RX, + .cmd = data->read_jedec_cmd, + .num_bytes = 3, + .data_buf = buf, + .address = 0, + }; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .addr_length = 0, + .rx_dummy = 0, + .packets = &packet, + .num_packet = 1, + .timeout = INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT, + }; + + ret = mspi_transceive(config->bus, &config->dev_id, &xfer); + if (ret < 0) { + LOG_ERR("Error reading JEDEC id"); + return ret; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_read(const struct device *dev, off_t addr, void *data, + size_t size) +{ + int ret = 0; + bool requires_cleanup = false; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *dev_data = dev->data; + + /* The S25H allows for continuous read operations which happen by sending + * 0xAX after an address. The driver doesn't implement this which is why we + * don't want this and instead wait for 2 cycles. However since the flash + * pins could be in high impedance state from the MSPI controller after the + * address was sent an address ending with 0xA could put the flash into a + * continuous read mode. To prevent this the driver will read the jedec id, + * if the address wasn't aligned to prevent accidentally fulfilling the + * requirement. + */ + if (dev_data->mspi_dev_cfg.io_mode == MSPI_IO_MODE_QUAD && (addr % 16 != 0)) { + requires_cleanup = true; + } + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error setting up the MSPI bus for the flash device"); + return ret; + } + + const struct mspi_xfer_packet packet = { + .address = addr, + .cmd = dev_data->read_flash_cmd, + .data_buf = data, + .dir = MSPI_RX, + .num_bytes = size, + }; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .addr_length = dev_data->mspi_dev_cfg.addr_length, + .rx_dummy = dev_data->read_flash_dummy_cycles, + .packets = &packet, + .num_packet = 1, + /* 20 milliseconds + 10 ms per 4KiB; for 256 KiB this results in 660 ms */ + .timeout = (size * 10 / 4096) + 20, + }; + + ret = mspi_transceive(config->bus, &config->dev_id, &xfer); + if (ret < 0) { + return ret; + } + + if (requires_cleanup) { + uint8_t unused[3]; + (void)unused; + return flash_mspi_infineon_s25h_read_jedec_id(dev, unused); + } + + return ret; +} + +static int flash_mspi_infineon_s25h_single_block_write(const struct device *dev, + const struct mspi_xfer *xfer_write) +{ + int ret; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + uint8_t status_1; + + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, false); + if (ret < 0) { + LOG_ERR("Error disabling write protection before trying to write data into " + "flash"); + return ret; + } + + ret = mspi_transceive(config->bus, &config->dev_id, xfer_write); + if (ret < 0) { + LOG_ERR("Error writing flash memory"); + return ret; + } + + ret = flash_mspi_infineon_s25h_wait_for_idle(dev, + INF_MSPI_S25H_TIMEOUT_IDLE_WRITE_BLOCK_MS); + if (ret < 0) { + LOG_ERR("Error waiting for flash to enter idle after writing"); + return ret; + } + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_1, + &status_1, 0, MSPI_RX); + + if (ret < 0) { + LOG_ERR("Error reading back status 1 register to confirm valid write"); + return ret; + } + + if (status_1 & INF_MSPI_S25H_STATUS_1_PRGERR_BIT) { + LOG_ERR("Last programming transaction wasn't successful"); + return -EIO; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_write(const struct device *dev, off_t addr, + const void *transmission_data, size_t size) +{ + int ret = 0; + struct flash_mspi_infineon_s25h_data *dev_data = dev->data; + uint8_t old_write_protection; + uint8_t *data_buf = (uint8_t *)transmission_data; + + /* Check whether we are not aligned and would write over a block boundary */ + if ((addr % INF_MSPI_S25H_WRITE_BLOCK_SIZE) != 0 && + (addr % INF_MSPI_S25H_WRITE_BLOCK_SIZE) + size >= INF_MSPI_S25H_WRITE_BLOCK_SIZE) { + LOG_ERR("Non-aligned write that goes above another block isn't supported"); + return -ENOSYS; + } + + struct mspi_xfer_packet packet_write = { + .cmd = INF_MSPI_S25H_OPCODE_WRITE_FLASH, + .dir = MSPI_TX, + }; + + struct mspi_xfer xfer_write = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .addr_length = dev_data->mspi_dev_cfg.addr_length, + .rx_dummy = 0, + .packets = &packet_write, + .num_packet = 1, + /* 20 milliseconds + 10 ms per 4KiB; for 256 KiB this results in 660 ms */ + .timeout = (size * 10 / 4096) + 20, + }; + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error setting up the MSPI bus for the flash device"); + return ret; + } + + ret = flash_mspi_infineon_s25h_is_write_protection_enabled(dev, &old_write_protection); + if (ret < 0) { + LOG_ERR("Error querying whether write protection is enabled"); + return ret; + } + + uint32_t remaining_bytes = size; + uint32_t write_index = 0; + uint32_t write_block_count = size / INF_MSPI_S25H_WRITE_BLOCK_SIZE; + uint32_t current_transaction_transfer_size; + + if (size % INF_MSPI_S25H_WRITE_BLOCK_SIZE != 0) { + ++write_block_count; + } + + do { + current_transaction_transfer_size = + MIN(remaining_bytes, INF_MSPI_S25H_WRITE_BLOCK_SIZE); + packet_write.num_bytes = current_transaction_transfer_size; + packet_write.address = addr + (write_index * INF_MSPI_S25H_WRITE_BLOCK_SIZE); + packet_write.data_buf = &data_buf[write_index * INF_MSPI_S25H_WRITE_BLOCK_SIZE]; + + ret = flash_mspi_infineon_s25h_single_block_write(dev, &xfer_write); + if (ret < 0) { + return ret; + } + + remaining_bytes -= current_transaction_transfer_size; + ++write_index; + + } while (remaining_bytes > 0); + + if (old_write_protection) { + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, false); + if (ret < 0) { + LOG_ERR("Error re-enabling write protection after writing data into flash"); + return ret; + } + } + + return 0; +} + +static int flash_mspi_infineon_s25h_erase(const struct device *dev, off_t addr, size_t size) +{ + int ret = 0; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *dev_data = dev->data; + uint8_t old_write_protection; + + struct mspi_xfer_packet packet_erase = { + .cmd = INF_MSPI_S25H_OPCODE_ERASE_256K, + .data_buf = NULL, + .num_bytes = 0, + .dir = MSPI_TX, + }; + + const struct mspi_xfer xfer_erase = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .addr_length = dev_data->mspi_dev_cfg.addr_length, + .rx_dummy = 0, + .packets = &packet_erase, + .num_packet = 1, + /* 20 milliseconds + 4 ms per 16KiB; for 256 KiB this results in 84 ms */ + .timeout = (size * 4 / 16384) + 20, + }; + + if (addr % INF_MSPI_S25H_ERASE_SECTOR_SIZE != 0) { + LOG_WRN("Erase sector is not aligned! This might erase data you don't want to " + "erase"); + } + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error setting up the MSPI bus for the flash device"); + return ret; + } + + ret = flash_mspi_infineon_s25h_is_write_protection_enabled(dev, &old_write_protection); + if (ret < 0) { + LOG_ERR("Error querying whether write protection is enabled"); + return ret; + } + + uint32_t count = size / INF_MSPI_S25H_ERASE_SECTOR_SIZE; + + if (size % INF_MSPI_S25H_ERASE_SECTOR_SIZE != 0) { + ++count; + } + + for (uint32_t i = 0; i < count; ++i) { + packet_erase.address = addr + (i * INF_MSPI_S25H_ERASE_SECTOR_SIZE); + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, false); + if (ret < 0) { + LOG_ERR("Error disabling write protection before flash erase"); + return ret; + } + + ret = mspi_transceive(config->bus, &config->dev_id, &xfer_erase); + if (ret) { + LOG_ERR("Error sending erase command"); + return ret; + } + + ret = flash_mspi_infineon_s25h_wait_for_idle( + dev, INF_MSPI_S25H_TIMEOUT_IDLE_ERASE_SECTOR_MS); + if (ret < 0) { + LOG_ERR("Error waiting for flash to enter idle after erasing"); + return ret; + } + } + + if (old_write_protection) { + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, false); + if (ret < 0) { + LOG_ERR("Error re-enabling write protection after flash erase"); + return ret; + } + } + + return 0; +} + +static const struct flash_parameters * +flash_mspi_infineon_s25h_get_parameters(const struct device *dev) +{ + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + + return &config->parameters; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static void flash_mspi_infineon_s25h_pages_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + + *layout = &config->page_layout; + *layout_size = 1; +} +#endif + +static int flash_mspi_infineon_s25h_verify_jedec_id(const struct device *dev) +{ + const struct flash_mspi_infineon_s25h_cfg *cfg = dev->config; + uint8_t id[3]; + int ret; + + ret = flash_mspi_infineon_s25h_read_jedec_id(dev, id); + if (ret < 0) { + LOG_ERR("Error reading JEDEC ids from flash"); + return ret; + } + + uint8_t manufacturer_id = id[0]; + uint16_t device_id = (id[1] << 8) | id[2]; + + if (manufacturer_id != cfg->jedec_manufacturer_id || device_id != cfg->jedec_device_id) { + LOG_ERR("Read JEDEC ids don't match expected ids. The communication is possibly " + "broken or the non-volatile flash configuration is something unexpected"); + LOG_ERR("Read manufacturer id: 0x%02X. Expected: 0x%02X", manufacturer_id, + cfg->jedec_manufacturer_id); + LOG_ERR("Read device id: 0x%04X. Expected: 0x%04X", device_id, + cfg->jedec_device_id); + return -EIO; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_switch_to_quad_transfer(const struct device *dev) +{ + int ret; + struct flash_mspi_infineon_s25h_data *data = dev->data; + + uint8_t cfg_value; + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_1, + &cfg_value, 0, MSPI_RX); + if (ret < 0) { + LOG_ERR("Error reading flash register"); + return ret; + } + + cfg_value |= INF_MSPI_S25H_CFG_1_QUADIT_BIT; + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_1, + &cfg_value, 0, MSPI_TX); + if (ret < 0) { + LOG_ERR("Error writing flash register"); + return ret; + } + + /* set address + data to 4 lanes */ + data->mspi_dev_cfg.io_mode = MSPI_IO_MODE_QUAD_1_4_4; + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + + if (ret < 0) { + LOG_ERR("Error switching MSPI mode to 4 lane data width"); + return ret; + } + + data->read_flash_cmd = INF_MSPI_S25H_OPCODE_READ_FLASH_QUAD; + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_1, + &cfg_value, 0, MSPI_RX); + if (ret < 0) { + LOG_ERR("Error reading flash register"); + return ret; + } + + ret = flash_mspi_infineon_s25h_verify_jedec_id(dev); + if (ret < 0) { + LOG_ERR("JEDEC ID mismatch after switching to 4 lane MSPI. Communication is " + "broken"); + return ret; + } + + /* set command to 4 lanes */ + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_2, + &cfg_value, 0, MSPI_RX); + if (ret < 0) { + LOG_ERR("Error reading flash register"); + return ret; + } + + cfg_value |= INF_MSPI_S25H_CFG_2_QPI_IT_BIT; + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_2, + &cfg_value, 0, MSPI_TX); + if (ret < 0) { + LOG_ERR("Error writing flash register"); + return ret; + } + + data->mspi_dev_cfg.io_mode = MSPI_IO_MODE_QUAD; + data->read_jedec_cmd = INF_MSPI_S25H_OPCODE_READ_JEDEC_ID_QUAD; + data->read_flash_dummy_cycles = INF_MSPI_S25H_DELAY_READ_QUADSPI; + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error switching bus mode to full quad MSPI mode"); + return ret; + } + + ret = flash_mspi_infineon_s25h_verify_jedec_id(dev); + if (ret < 0) { + LOG_ERR("JEDEC ID mismatch after switching to full quad MSPI mode. Communication " + "is broken"); + return ret; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_disable_hybrid_sector_mode(const struct device *dev) +{ + /* This driver needs the hybrid sector mode to be disabled. So if it's found to be turned on + * it gets changed. This requires changing the non-volatile configuration and also a reset + */ + int ret = 0; + uint8_t conf3 = 0; + + ret = flash_mspi_infineon_s25h_rw_any_register(dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_3, + &conf3, 0, MSPI_RX); + if (ret < 0) { + LOG_ERR("Error reading volatile configuration register 3"); + return ret; + } + + if ((conf3 & INF_MSPI_S25H_CFG_3_UNHYSA_BIT) == 0) { + LOG_INF("Flash is in hybrid sector mode. Changing non-volatile config to correct " + "this"); + + conf3 |= INF_MSPI_S25H_CFG_3_UNHYSA_BIT; + + ret = flash_mspi_infineon_s25h_rw_any_register( + dev, INF_MSPI_S25H_ADDRESS_NON_VOLATILE_CFG_3, &conf3, 0, MSPI_TX); + if (ret < 0) { + LOG_ERR("Error changing non-volatile configuration of flash"); + return ret; + } + + ret = flash_mspi_infineon_s25h_wait_for_idle(dev, + INF_MSPI_S25H_TIMEOUT_IDLE_STARTUP); + if (ret < 0) { + LOG_ERR("Error waiting for flash to enter idle after disabling hybrid " + "sector mode"); + return ret; + } + + ret = flash_mspi_infineon_s25h_reset(dev); + if (ret < 0) { + LOG_ERR("Error resetting flash via reset command"); + return ret; + } + + conf3 = 0; + ret = flash_mspi_infineon_s25h_rw_any_register( + dev, INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_3, &conf3, 0, MSPI_RX); + if (ret < 0) { + LOG_ERR("Error reading volatile config 3 register of flash"); + return ret; + } + + if ((conf3 & INF_MSPI_S25H_CFG_3_UNHYSA_BIT) == 0) { + LOG_ERR("Changing the flash configuration to Uniform mode didn't work"); + return -EIO; + } + + ret = flash_mspi_infineon_s25h_set_writing_forbidden(dev, true); + if (ret < 0) { + LOG_ERR("Error re-enabling the write protection"); + return ret; + } + } + + return 0; +} + +static int flash_mspi_infineon_s25h_enter_4_byte_address_mode(const struct device *dev) +{ + int ret = 0; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + struct flash_mspi_infineon_s25h_data *data = dev->data; + + const struct mspi_xfer_packet enter_4_byte_cmd = { + .dir = MSPI_TX, + .cmd = INF_MSPI_S25H_OPCODE_ENABLE_4_BYTE_ADDR_MODE, + .num_bytes = 0, + }; + + struct mspi_xfer xfer = { + INF_MSPI_S25H_DEFAULT_XFER_DATA, + .rx_dummy = 0, + .addr_length = 0, + .num_packet = 1, + .packets = &enter_4_byte_cmd, + .timeout = INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT, + }; + + ret = mspi_transceive(config->bus, &config->dev_id, &xfer); + if (ret < 0) { + LOG_ERR("Error sending command to enter 4 byte address mode"); + return ret; + } + + data->mspi_dev_cfg.addr_length = 4; + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error setting up MSPI bus after changing address length"); + return ret; + } + + ret = flash_mspi_infineon_s25h_verify_jedec_id(dev); + if (ret < 0) { + LOG_ERR("Error verifying JEDEC id after entering 4 byte address mode"); + return ret; + } + + return 0; +} + +static int flash_mspi_infineon_s25h_init(const struct device *dev) +{ + int ret = 0; + const struct flash_mspi_infineon_s25h_cfg *config = dev->config; + + ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl"); + return ret; + } + + ret = flash_mspi_infineon_s25h_prepare_mspi_bus(dev); + if (ret < 0) { + LOG_ERR("Error switching MSPI configuration to the requirements of the flash " + "device"); + return ret; + } + + ret = flash_mspi_infineon_s25h_reset(dev); + if (ret < 0) { + LOG_ERR("Error resetting flash device"); + return ret; + } + + ret = flash_mspi_infineon_s25h_verify_jedec_id(dev); + if (ret < 0) { + return ret; + } + + ret = flash_mspi_infineon_s25h_disable_hybrid_sector_mode(dev); + if (ret < 0) { + return ret; + } + + ret = flash_mspi_infineon_s25h_enter_4_byte_address_mode(dev); + if (ret < 0) { + return ret; + } + + /* Switch into 4S-4S-4S mode, if not deactivated */ + if (!config->stay_in_startup_mspi_config) { + ret = flash_mspi_infineon_s25h_switch_to_quad_transfer(dev); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static DEVICE_API(flash, flash_mspi_infineon_s25h_driver_api) = { + .read = flash_mspi_infineon_s25h_read, + .write = flash_mspi_infineon_s25h_write, + .erase = flash_mspi_infineon_s25h_erase, + .get_parameters = flash_mspi_infineon_s25h_get_parameters, +#if defined(CONFIG_FLASH_JESD216_API) + .read_jedec_id = flash_mspi_infineon_s25h_read_jedec_id, +#endif +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = flash_mspi_infineon_s25h_pages_layout, +#endif +}; + +#define INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, prop) \ + BUILD_ASSERT(DT_NODE_HAS_PROP(DT_DRV_INST(n), prop) == 0, \ + "The Infineon S25H driver ignores the property " #prop ". Don't use it") + +/* Check for ignored/wrong values in the devicetree */ +#define INFINEON_MSPI_FLASH_S25H_CHECK_DEVICETREE_CONFIG(n) \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, rx_dummy); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, tx_dummy); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, read_command); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, write_command); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, xip_config); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, scramble_config); \ + INFINEON_MSPI_FLASH_S25H_CHECK_PROP_IS_UNDEFINED(n, ce_break_config) + +#define INFINFEON_MSPI_FLASH_S25H_DEFINE(n) \ + INFINEON_MSPI_FLASH_S25H_CHECK_DEVICETREE_CONFIG(n); \ + PINCTRL_DT_DEFINE(DT_DRV_INST(n)); \ + static const struct flash_mspi_infineon_s25h_cfg flash_mspi_infineon_s25h_cfg_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .bus = DEVICE_DT_GET(DT_BUS(DT_DRV_INST(n))), \ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .reset_startup_duration = K_USEC(DT_INST_PROP(n, reset_startup_time_us)), \ + .dev_id = MSPI_DEVICE_ID_DT_INST(n), \ + .page_layout = {.pages_count = DT_INST_PROP(n, flash_size) / \ + DT_INST_PROP(n, erase_block_size), \ + .pages_size = DT_INST_PROP(n, erase_block_size)}, \ + .parameters = {.erase_value = 0xFF, \ + .write_block_size = DT_INST_PROP(n, write_block_size)}, \ + .jedec_device_id = DT_PROP(DT_DRV_INST(n), device_id), \ + .jedec_manufacturer_id = DT_PROP(DT_DRV_INST(n), manufacturer_id), \ + .stay_in_startup_mspi_config = DT_PROP(DT_DRV_INST(n), keep_startup_mspi_config), \ + }; \ + static struct flash_mspi_infineon_s25h_data flash_mspi_infineon_s25h_data_##n = { \ + .mspi_dev_cfg = MSPI_DEVICE_CONFIG_DT_INST(n), \ + .read_jedec_cmd = INF_MSPI_S25H_OPCODE_READ_JEDEC_ID, \ + .read_flash_cmd = INF_MSPI_S25H_OPCODE_READ_FLASH, \ + .read_flash_dummy_cycles = 0, \ + }; \ + DEVICE_DT_INST_DEFINE(n, flash_mspi_infineon_s25h_init, NULL, \ + &flash_mspi_infineon_s25h_data_##n, \ + &flash_mspi_infineon_s25h_cfg_##n, POST_KERNEL, \ + CONFIG_FLASH_INIT_PRIORITY, &flash_mspi_infineon_s25h_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(INFINFEON_MSPI_FLASH_S25H_DEFINE) diff --git a/drivers/flash/flash_mspi_infineon_s25h.h b/drivers/flash/flash_mspi_infineon_s25h.h new file mode 100644 index 0000000000000..16975df0a4749 --- /dev/null +++ b/drivers/flash/flash_mspi_infineon_s25h.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2025 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_FLASH_MSPI_INFINEON_S25H_ +#define ZEPHYR_DRIVERS_FLASH_MSPI_INFINEON_S25H_ + +#include "zephyr/kernel.h" +#include "zephyr/sys/util_macro.h" + +/* defaults */ +#define INF_MSPI_S25H_DEFAULT_MSPI_TIMEOUT 30 + +/* opcodes 1-1-1 mode */ +#define INF_MSPI_S25H_OPCODE_WRITE_ENABLE 0x06 +#define INF_MSPI_S25H_OPCODE_WRITE_DISABLE 0x04 +#define INF_MSPI_S25H_OPCODE_ENABLE_4_BYTE_ADDR_MODE 0xB7 + +#define INF_MSPI_S25H_OPCODE_READ_ANY_REGISTER 0x65 +#define INF_MSPI_S25H_OPCODE_WRITE_ANY_REGISTER 0x71 + +#define INF_MSPI_S25H_OPCODE_ERASE_256K 0xD8 +#define INF_MSPI_S25H_OPCODE_READ_FLASH 0x03 +#define INF_MSPI_S25H_OPCODE_WRITE_FLASH 0x02 + +#define INF_MSPI_S25H_OPCODE_RESET_ENABLE 0x66 +#define INF_MSPI_S25H_OPCODE_RESET 0x99 + +#define INF_MSPI_S25H_OPCODE_READ_JEDEC_ID 0x9F + +/* opcodes 4-4-4 mode */ +#define INF_MSPI_S25H_OPCODE_READ_FLASH_QUAD 0xEB +#define INF_MSPI_S25H_OPCODE_READ_JEDEC_ID_QUAD 0xAF + +/* RX delay */ +/* 2 cycles for the mode bit to be ignored in Quad SPI mode; 8 from the default configuration */ +#define INF_MSPI_S25H_DELAY_READ_QUADSPI (2 + 8) + +/* addresses */ +#define INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_1 0x00800000 +#define INF_MSPI_S25H_ADDRESS_VOLATILE_STATUS_2 0x00800001 + +#define INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_1 0x00800002 +#define INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_2 0x00800003 +#define INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_3 0x00800004 +#define INF_MSPI_S25H_ADDRESS_VOLATILE_CFG_4 0x00800005 + +#define INF_MSPI_S25H_ADDRESS_NON_VOLATILE_STATUS_1 0x0 + +#define INF_MSPI_S25H_ADDRESS_NON_VOLATILE_CFG_1 0x2 +#define INF_MSPI_S25H_ADDRESS_NON_VOLATILE_CFG_2 0x3 +#define INF_MSPI_S25H_ADDRESS_NON_VOLATILE_CFG_3 0x4 +#define INF_MSPI_S25H_ADDRESS_NON_VOLATILE_CFG_4 0x5 + +/* device specific data */ +#define INF_MSPI_S25H_ERASE_SECTOR_SIZE 262144 +#define INF_MSPI_S25H_WRITE_BLOCK_SIZE 256 + +/* default data */ +#define INF_MSPI_S25H_DEFAULT_XFER_DATA \ + .async = false, .cmd_length = 1, .hold_ce = false, .priority = 0, .tx_dummy = 0, \ + .xfer_mode = MSPI_PIO + +#define INF_MSPI_S25H_DEFAULT_XFER_DATA_SINGLE_CMD \ + INF_MSPI_S25H_DEFAULT_XFER_DATA, .rx_dummy = 0, .addr_length = 0, .num_packet = 1 + +#define INF_MSPI_S25H_TIMEOUT_IDLE_RETRY_INTERVAL_MS 10 +#define INF_MSPI_S25H_TIMEOUT_IDLE_RETRY_INTERVAL \ + K_MSEC(INF_MSPI_S25H_TIMEOUT_IDLE_RETRY_INTERVAL_MS) +#define INF_MSPI_S25H_TIMEOUT_IDLE_ERASE_SECTOR_MS 1000 +#define INF_MSPI_S25H_TIMEOUT_IDLE_WRITE_BLOCK_MS 1000 +#define INF_MSPI_S25H_TIMEOUT_IDLE_STARTUP 100 + +/* register bits */ +#define INF_MSPI_S25H_STATUS_1_RDYBSY_BIT BIT(0) +#define INF_MSPI_S25H_STATUS_1_WRPGEN_BIT BIT(1) +#define INF_MSPI_S25H_STATUS_1_LBPROT_SHIFT 2 +#define INF_MSPI_S25H_STATUS_1_LBPROT_MASK BIT_MASK(3) +#define INF_MSPI_S25H_STATUS_1_ERSERR_BIT BIT(5) +#define INF_MSPI_S25H_STATUS_1_PRGERR_BIT BIT(6) +#define INF_MSPI_S25H_STATUS_1_STCFWR_BIT BIT(7) + +#define INF_MSPI_S25H_STATUS_2_PROGMS_BIT BIT(0) +#define INF_MSPI_S25H_STATUS_2_ERASES_BIT BIT(1) +#define INF_MSPI_S25H_STATUS_2_SESTAT_BIT BIT(2) +#define INF_MSPI_S25H_STATUS_2_DICRCA_BIT BIT(3) +#define INF_MSPI_S25H_STATUS_2_DICRCS_BIT BIT(4) + +#define INF_MSPI_S25H_CFG_1_TLPROT_BIT BIT(0) +#define INF_MSPI_S25H_CFG_1_QUADIT_BIT BIT(1) +#define INF_MSPI_S25H_CFG_1_TB4KBS_BIT BIT(2) +#define INF_MSPI_S25H_CFG_1_PLPROT_BIT BIT(4) +#define INF_MSPI_S25H_CFG_1_TBPROT_BIT BIT(5) +#define INF_MSPI_S25H_CFG_1_SP4KBS_BIT BIT(6) + +#define INF_MSPI_S25H_CFG_2_MEMLAT_SHIFT 0 +#define INF_MSPI_S25H_CFG_2_MEMLAT_MASK BIT_MASK(4) +#define INF_MSPI_S25H_CFG_2_DQ3RST_BIT BIT(5) +#define INF_MSPI_S25H_CFG_2_QPI_IT_BIT BIT(6) +#define INF_MSPI_S25H_CFG_2_ADRBYT_BIT BIT(7) + +#define INF_MSPI_S25H_CFG_3_LSFRST_BIT BIT(0) +#define INF_MSPI_S25H_CFG_3_CLSRSM_BIT BIT(2) +#define INF_MSPI_S25H_CFG_3_UNHYSA_BIT BIT(3) +#define INF_MSPI_S25H_CFG_3_PGMBUF_BIT BIT(4) +#define INF_MSPI_S25H_CFG_3_BLKCHK_BIT BIT(5) +#define INF_MSPI_S25H_CFG_3_VRGLAT_SHIFT 6 +#define INF_MSPI_S25H_CFG_3_VRGLAT_MASK BIT_MASK(2) + +#define INF_MSPI_S25H_CFG_4_RBSTWL_SHIFT 0 +#define INF_MSPI_S25H_CFG_4_RBSTWL_MASK BIT_MASK(2) +#define INF_MSPI_S25H_CFG_4_DPDPOR_BIT BIT(2) +#define INF_MSPI_S25H_CFG_4_ECC12S_BIT BIT(3) +#define INF_MSPI_S25H_CFG_4_RBSTWP_BIT BIT(4) +#define INF_MSPI_S25H_CFG_4_IOIMPD_SHIFT 5 +#define INF_MSPI_S25H_CFG_4_IOIMPD_MASK BIT_MASK(3) + +#endif /* ZEPHYR_DRIVERS_FLASH_MSPI_INFINEON_S25H_ */ diff --git a/drivers/mspi/CMakeLists.txt b/drivers/mspi/CMakeLists.txt index 821bb1d09f175..9c4004308e65e 100644 --- a/drivers/mspi/CMakeLists.txt +++ b/drivers/mspi/CMakeLists.txt @@ -1,3 +1,5 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# # SPDX-License-Identifier: Apache-2.0 zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/mspi.h) @@ -8,6 +10,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP3 mspi_ambiq_ap3.c) zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP5 mspi_ambiq_ap5.c) zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_TIMING_SCAN mspi_ambiq_timing_scan.c) +zephyr_library_sources_ifdef(CONFIG_MSPI_CDNS mspi_cadence.c) zephyr_library_sources_ifdef(CONFIG_MSPI_DW mspi_dw.c) zephyr_library_sources_ifdef(CONFIG_MSPI_EMUL mspi_emul.c) zephyr_library_sources_ifdef(CONFIG_MSPI_STM32_OSPI mspi_stm32_ospi.c mspi_stm32_common.c) diff --git a/drivers/mspi/Kconfig b/drivers/mspi/Kconfig index 9b78c57c3bd08..050b224712f68 100644 --- a/drivers/mspi/Kconfig +++ b/drivers/mspi/Kconfig @@ -1,6 +1,8 @@ # MSPI driver configuration options # Copyright (c) 2024 Ambiq Micro Inc. +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# # SPDX-License-Identifier: Apache-2.0 # @@ -68,6 +70,7 @@ source "subsys/logging/Kconfig.template.log_config" # zephyr-keep-sorted-start source "drivers/mspi/Kconfig.ambiq" +source "drivers/mspi/Kconfig.cadence" source "drivers/mspi/Kconfig.dw" source "drivers/mspi/Kconfig.mspi_emul" source "drivers/mspi/Kconfig.stm32" diff --git a/drivers/mspi/Kconfig.cadence b/drivers/mspi/Kconfig.cadence new file mode 100644 index 0000000000000..501bc8445ad5f --- /dev/null +++ b/drivers/mspi/Kconfig.cadence @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +config MSPI_CDNS + bool "Cadence MSPI driver" + default y + depends on DT_HAS_CDNS_MSPI_CONTROLLER_ENABLED + # GPIO is required for ce_gpios despite the field being ignored + select GPIO + select PINCTRL + imply MSPI_TIMING + help + Enable driver for Cadence MSPI peripheral diff --git a/drivers/mspi/mspi_cadence.c b/drivers/mspi/mspi_cadence.c new file mode 100644 index 0000000000000..bc738738ce77b --- /dev/null +++ b/drivers/mspi/mspi_cadence.c @@ -0,0 +1,1043 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT cdns_mspi_controller + +#include "mspi_cadence.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(mspi_cadence, CONFIG_MSPI_LOG_LEVEL); + +struct mspi_cadence_config { + DEVICE_MMIO_ROM; + struct mspi_cfg mspi_config; + const struct pinctrl_dev_config *pinctrl; + const uint32_t fifo_addr; + const uint32_t sram_allocated_for_read; + const uint32_t reference_frequency; +}; + +struct mspi_cadence_data { + DEVICE_MMIO_RAM; + struct k_mutex access_lock; + struct k_sem transfer_lock; + const struct mspi_dev_id *current_peripheral; +}; + +/** + * Wait for the MSPI controller to enter idle with the default timeout + */ +static int mspi_cadence_wait_for_idle(const struct device *controller) +{ + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + uint32_t idle = config_reg & CADENCE_MSPI_CONFIG_REG_IDLE_BIT; + uint32_t retries = CADENCE_MSPI_GET_NUM_RETRIES(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE); + + while (idle == 0 && retries > 0) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + idle = config_reg & CADENCE_MSPI_CONFIG_REG_IDLE_BIT; + --retries; + } + if (retries == 0) { + LOG_ERR("Timeout while waiting for MSPI to enter idle"); + return -EIO; + } + return 0; +} + +/** + * Check whether a single request package is requesting something that the driver + * doesn't implement / the hardware doesn't support + */ +static int mspi_cadence_check_transfer_package(const struct mspi_xfer *request, uint32_t index) +{ + const struct mspi_xfer_packet *packet = &request->packets[index]; + /* Check that we won't truncate the address */ + if (((uint64_t) packet->address) >> (8 * request->addr_length)) { + LOG_ERR("Address too long for amount of address bytes"); + return -EINVAL; + } + if (packet->cb_mask != MSPI_BUS_NO_CB) { + LOG_ERR("Callbacks aren't implemented"); + return -ENOSYS; + } + if (packet->cmd >> 16) { + LOG_ERR("Commands over 2 byte long aren't supported"); + return -ENOTSUP; + } + if (packet->cmd >> 8) { + LOG_ERR("Support for dual byte opcodes hasn't been implemented"); + return -ENOSYS; + } + if (packet->num_bytes) { + __ASSERT(packet->data_buf != NULL, + "Request gave a NULL buffer when bytes should be transferred"); + } + return 0; +} + +/** + * Check whether a full request has invalid / not supported parts + */ +static int mspi_cadence_check_transfer_request(const struct mspi_xfer *request) +{ + if (request->async) { + LOG_ERR("Asynchronous requests are not implemented"); + return -ENOSYS; + } + + if (request->cmd_length == 2) { + LOG_ERR("Dual byte opcode is not implemented"); + return -ENOSYS; + } else if (request->cmd_length > 2) { + LOG_ERR("Cmds over 2 bytes long aren't supported"); + return -ENOTSUP; + } else if (request->cmd_length != 1) { + LOG_ERR("Can't handle transfer without cmd"); + return -ENOSYS; + } + + if (request->addr_length > 4) { + LOG_ERR("Address too long. Only up to 32 bit are supported"); + return -ENOTSUP; + } + + if (request->priority != 0) { + LOG_WRN("Ignoring request to give transfer higher priority"); + } + + if (request->num_packet == 0) { + LOG_ERR("Got transfer requests without packages"); + return -EINVAL; + } + __ASSERT(request->packets != NULL, "Packets in transfer request are NULL"); + + if (request->xfer_mode != MSPI_PIO) { + LOG_ERR("Other modes than PIO are not supported"); + return -ENOTSUP; + } + + if (request->rx_dummy > CADENCE_MSPI_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MAX_VALUE || + request->tx_dummy > CADENCE_MSPI_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MAX_VALUE) { + return -ENOTSUP; + } + + int ret = 0; + + for (uint32_t i = 0; i < request->num_packet; ++i) { + ret = mspi_cadence_check_transfer_package(request, i); + if (ret < 0) { + return ret; + } + } + + return 0; +} + +static int mspi_cadence_init(const struct device *dev) +{ + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + const struct mspi_cadence_config *config = dev->config; + struct mspi_cadence_data *data = dev->data; + const mem_addr_t base_addr = DEVICE_MMIO_GET(dev); + int ret; + + k_mutex_init(&data->access_lock); + k_sem_init(&data->transfer_lock, 1, 1); + + ret = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + LOG_ERR("Failed to apply pinctrl"); + return ret; + } + + /* Disable MSPI */ + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + ret = mspi_cadence_wait_for_idle(dev); + if (ret < 0) { + return ret; + } + + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Disable direct access the driver always uses indirect accesses */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENB_DIR_ACC_CTRL_BIT; + + /* Disable DTR protocol */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT; + + /* Leave XIP mode */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_BIT; + + /* Only allow one CS to be active */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PERIPH_SEL_DEC_BIT; + + /* CS selection is based on "manual" pin selection */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_AHB_DECODER_BIT; + + /* DQ3 should not be used as reset pin */ + config_reg |= CADENCE_MSPI_CONFIG_REG_RESET_CFG_BIT; + + /* Set baud rate division to 32; formula: (n + 1) * 2 */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, 15); + + /* Disable dual byte opcodes */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT; + + /* Disable PHY pipeline mode */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PIPELINE_PHY_BIT; + + /* Disable PHY module generally */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PHY_MODE_ENABLE_BIT; + + /* Disable CRC */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_CRC_ENABLE_BIT; + + /* Disable DMA generally since it's not supported */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENB_DMA_IF_BIT; + + /* Disable automatic write protection disablement of MSPI peripherals */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_WR_PROT_FLASH_BIT; + + /* Disable possible reset pin */ + config_reg &= ~CADENCE_MSPI_CONFIG_REG_RESET_PIN_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Set how many FSS0 SRAM locations are allocated for read; the other ones + * are allocated for writes + */ + uint32_t sram_partition = sys_read32(base_addr + CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET); + + sram_partition &= ~CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK; + sram_partition |= FIELD_PREP(CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK, + config->sram_allocated_for_read); + + sys_write32(sram_partition, base_addr + CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET); + + /* General clock cycle delays */ + uint32_t timing_config_reg = sys_read32(base_addr + CADENCE_MSPI_DEV_DELAY_OFFSET); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + timing_config_reg &= ~CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK; + timing_config_reg |= + FIELD_PREP(CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK, CADENCE_MSPI_DEFAULT_DELAY); + + sys_write32(timing_config_reg, base_addr + CADENCE_MSPI_DEV_DELAY_OFFSET); + + /* Set trigger reg address and range to 0 */ + uint32_t ind_ahb_addr_trigger = + sys_read32(base_addr + CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET); + + ind_ahb_addr_trigger &= ~CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET; + + sys_write32(ind_ahb_addr_trigger, base_addr + CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET); + + uint32_t indirect_trigger_addr_range = + sys_read32(base_addr + CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET); + + indirect_trigger_addr_range &= + ~CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_REG_IND_RANGE_WIDTH_MASK; + + sys_write32(indirect_trigger_addr_range, + base_addr + CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET); + + /* Disable loop-back via DQS */ + uint32_t rd_data_capture = sys_read32(base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + + rd_data_capture |= CADENCE_MSPI_RD_DATA_CAPTURE_REG_BYPASS_BIT; + + sys_write32(rd_data_capture, base_addr + CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET); + + /* Disable auto polling for write completion */ + uint32_t write_completion_ctrl = + sys_read32(base_addr + CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET); + + write_completion_ctrl |= CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_DISABLE_POLLING_BIT; + + sys_write32(write_completion_ctrl, base_addr + CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET); + + /* Disable automatic write enable command before indirect write transactions */ + uint32_t device_write = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + device_write &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WEL_DIS_BIT; + + sys_write32(device_write, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + /* Reset mode bit (hardware CRC checking on read, if supported) and disable DDR mode */ + uint32_t device_read = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + device_read &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_MODE_BIT_ENABLE_BIT; + device_read &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT; + + sys_write32(device_read, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + uint32_t val; + + /* Disable all interrupts via masking */ + val = sys_read32(base_addr + CADENCE_MSPI_IRQ_MASK_OFFSET); + val &= ~CADENCE_MSPI_IRQ_MASK_REG_ALL; + sys_write32(val, base_addr + CADENCE_MSPI_IRQ_MASK_OFFSET); + + /* Clear currently pending interrupts */ + val = sys_read32(base_addr + CADENCE_MSPI_IRQ_STATUS_OFFSET); + val |= CADENCE_MSPI_IRQ_STATUS_REG_ALL; + sys_write32(val, base_addr + CADENCE_MSPI_IRQ_STATUS_OFFSET); + + /* Re-enable MSPI controller */ + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_stig(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_xfer_packet *packet = &req->packets[index]; + uint32_t dummy_cycles = 0; + + /* Reset previous command configuration completely */ + sys_write32(0, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + uint32_t flash_cmd_ctrl = 0; + + if (packet->dir == MSPI_RX) { + if (packet->num_bytes != 0) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_READ_DATA_BIT; + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_RD_DATA_BYTES_MASK, + packet->num_bytes - 1); + } + dummy_cycles = req->rx_dummy; + } else { + if (packet->num_bytes != 0) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_WRITE_DATA_BIT; + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_WR_DATA_BYTES_MASK, + packet->num_bytes - 1); + + if (packet->num_bytes > 4) { + uint32_t upper = 0; + + memcpy(&upper, &packet->data_buf[4], packet->num_bytes - 4); + sys_write32(upper, + base_address + CADENCE_MSPI_FLASH_WR_DATA_UPPER_OFFSET); + } + uint32_t lower = 0; + + memcpy(&lower, &packet->data_buf[0], MIN(packet->num_bytes, 4)); + sys_write32(lower, base_address + CADENCE_MSPI_FLASH_WR_DATA_LOWER_OFFSET); + } + dummy_cycles = req->tx_dummy; + } + + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK, packet->cmd); + flash_cmd_ctrl |= + FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_DUMMY_CYCLES_MASK, dummy_cycles); + + if (req->addr_length) { + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_COMD_ADDR_BIT; + flash_cmd_ctrl |= FIELD_PREP(CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + sys_write32(packet->address, base_address + CADENCE_MSPI_FLASH_CMD_ADDR_OFFSET); + } + + /* Start transaction */ + flash_cmd_ctrl |= CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_BIT; + sys_write32(flash_cmd_ctrl, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + uint32_t exec_status = sys_read32(base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET) & + CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT; + while (exec_status != 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + exec_status = sys_read32(base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET) & + CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT; + } + if (exec_status != 0) { + LOG_ERR("Timeout while waiting for dedicated command to finish"); + return -EIO; + } + + if (packet->dir == MSPI_RX) { + if (packet->num_bytes > 4) { + uint32_t higher = + sys_read32(base_address + CADENCE_MSPI_FLASH_RD_DATA_UPPER_OFFSET); + + memcpy(&packet->data_buf[4], &higher, packet->num_bytes - 4); + } + uint32_t lower = sys_read32(base_address + CADENCE_MSPI_FLASH_RD_DATA_LOWER_OFFSET); + + memcpy(&packet->data_buf[0], &lower, MIN(packet->num_bytes, 4)); + } + + /* + * The STIG register must be reset after the transfer or weird things like + * skipping every 2nd byte can occur + */ + sys_write32(0, base_address + CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET); + + return 0; +} + +static int mspi_cadence_indirect_read(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + const struct mspi_xfer_packet *packet = &req->packets[index]; + + uint32_t dev_instr_rd_cfg = + sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK; + dev_instr_rd_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK, + packet->cmd); + dev_instr_rd_cfg &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK; + dev_instr_rd_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK, req->rx_dummy); + + sys_write32(dev_instr_rd_cfg, base_address + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET); + sys_write32(packet->num_bytes, + base_address + CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_OFFSET); + + uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + + sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + /* Start transfer */ + uint32_t indirect_read_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_START_BIT; + + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + uint32_t remaining_bytes = packet->num_bytes; + uint8_t *write_ptr = packet->data_buf; + uint32_t num_new_words = 0; + uint32_t current_new_word; + int bytes_to_copy_from_current_word; + + while (remaining_bytes > 0) { + if (k_uptime_get() - start_time > req->timeout) { + LOG_ERR("Timeout while receiving data from MSPI device"); + goto timeout; + } + uint32_t indac_read = sys_read32(base_address + CADENCE_MSPI_SRAM_FILL_OFFSET); + + num_new_words = FIELD_GET(CADENCE_MSPI_SRAM_FILL_REG_INDAC_READ_MASK, indac_read); + while (remaining_bytes > 0 && num_new_words > 0) { + current_new_word = sys_read32(config->fifo_addr); + bytes_to_copy_from_current_word = MIN(remaining_bytes, 4); + memcpy(write_ptr, ¤t_new_word, bytes_to_copy_from_current_word); + write_ptr += bytes_to_copy_from_current_word; + remaining_bytes -= bytes_to_copy_from_current_word; + --num_new_words; + } + } + + /* Wait until official indirect read completion */ + uint32_t done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + while (done_status == 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + } + if (done_status == 0) { + LOG_ERR("Timeout waiting for official indirect read done confirmation"); + goto timeout; + } + indirect_read_ctrl = sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + + return 0; + +timeout: + indirect_read_ctrl = sys_read32(base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + indirect_read_ctrl |= CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_CANCEL_BIT; + sys_write32(indirect_read_ctrl, base_address + CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET); + return -EIO; +} + +static int mspi_cadence_indirect_write(const struct device *controller, const struct mspi_xfer *req, + uint32_t index, const uint64_t start_time) +{ + const mem_addr_t base_address = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + const struct mspi_xfer_packet *packet = &req->packets[index]; + + uint32_t dev_instr_wr_cfg = + sys_read32(base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK; + dev_instr_wr_cfg |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK, + packet->cmd); + dev_instr_wr_cfg &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK; + dev_instr_wr_cfg |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK, req->tx_dummy); + + sys_write32(dev_instr_wr_cfg, base_address + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + sys_write32(packet->address, base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET); + sys_write32(packet->num_bytes, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_OFFSET); + + uint32_t dev_size_config = sys_read32(base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + dev_size_config &= ~CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK; + dev_size_config |= FIELD_PREP(CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK, + req->addr_length - 1); + + sys_write32(dev_size_config, base_address + CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET); + + uint32_t indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_START_BIT; + + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + uint32_t read_offset = 0; + uint32_t remaining_bytes = packet->num_bytes; + uint32_t free_words = 0; + uint32_t current_word_to_write; + + while (remaining_bytes > 0) { + if (k_uptime_get() - start_time > req->timeout) { + LOG_ERR("Timeout while sending data to MSPI device"); + goto timeout; + } + uint32_t sram_fill = sys_read32(base_address + CADENCE_MSPI_SRAM_FILL_OFFSET); + + free_words = config->sram_allocated_for_read - + FIELD_GET(CADENCE_MSPI_SRAM_FILL_REG_INDAC_WRITE_MASK, sram_fill); + while (free_words > 0 && remaining_bytes > 0) { + current_word_to_write = 0; + memcpy(¤t_word_to_write, &packet->data_buf[read_offset], + MIN(remaining_bytes, 4)); + sys_write32(current_word_to_write, config->fifo_addr); + remaining_bytes = (remaining_bytes > 4 ? remaining_bytes - 4 : 0); + read_offset += 4; + --free_words; + } + } + + /* Wait for official finish */ + uint32_t done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + while (done_status == 0 && k_uptime_get() - start_time < req->timeout) { + k_sleep(CADENCE_MSPI_TIME_BETWEEN_RETRIES); + done_status = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET) & + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + } + if (done_status == 0) { + LOG_ERR("Timeout while waiting for official write done confirmation"); + goto timeout; + } + indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT; + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + + return 0; +timeout: + indirect_write_ctrl = + sys_read32(base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + indirect_write_ctrl |= CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_CANCEL_BIT; + sys_write32(indirect_write_ctrl, + base_address + CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET); + return -EIO; +} + +static int mspi_cadence_transceive(const struct device *controller, + const struct mspi_dev_id *dev_id, const struct mspi_xfer *req) +{ + uint64_t start_time = k_uptime_get(); + struct mspi_cadence_data *data = controller->data; + int ret = 0; + + if (data->current_peripheral != dev_id) { + LOG_ERR("Tried to send data over MSPI despite not having acquired the controller " + "beforehand by calling mspi_dev_config"); + return -EINVAL; + } + + ret = mspi_cadence_check_transfer_request(req); + if (ret) { + return ret; + } + + if (req->timeout > CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE) { + LOG_ERR("Request timeout exceeds configured maximum in Kconfig"); + return -EINVAL; + } + + ret = k_sem_take(&data->transfer_lock, K_MSEC(req->timeout)); + if (ret < 0) { + return ret; + } + + for (uint32_t i = 0; i < req->num_packet; ++i) { + const struct mspi_xfer_packet *packet = &req->packets[i]; + /* the FLASH_CMD_REGISTER is good for small transfers with only very little/no data + */ + if (packet->num_bytes <= 8) { + ret = mspi_cadence_stig(controller, req, i, start_time); + if (ret < 0) { + goto exit; + } + } else { + /* big transfer via indirect transfer mode */ + if (packet->dir == MSPI_RX) { + ret = mspi_cadence_indirect_read(controller, req, i, start_time); + } else { + ret = mspi_cadence_indirect_write(controller, req, i, start_time); + } + if (ret < 0) { + goto exit; + } + } + } + +exit: + k_sem_give(&data->transfer_lock); + return ret; +} + +static int mspi_cadence_set_opcode_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + rd_config |= FIELD_PREP(CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_set_addr_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + uint32_t wr_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK; + wr_config &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 0); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_2_2: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 1); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_4_4: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 2); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 3); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + sys_write32(wr_config, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_set_data_lines(const mem_addr_t base_addr, enum mspi_io_mode io_mode) +{ + uint32_t rd_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + uint32_t wr_config = sys_read32(base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + rd_config &= ~CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK; + wr_config &= ~CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK; + + switch (io_mode) { + case MSPI_IO_MODE_SINGLE: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 0); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 0); + break; + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 1); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 1); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 2); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 2); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + rd_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 3); + wr_config |= FIELD_PREP( + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK, 3); + break; + default: + return -ENOTSUP; + } + + sys_write32(rd_config, base_addr + CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET); + sys_write32(wr_config, base_addr + CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET); + + return 0; +} + +static int mspi_cadence_dev_config(const struct device *controller, + const struct mspi_dev_id *dev_id, + const enum mspi_dev_cfg_mask param_mask, + const struct mspi_dev_cfg *dev_cfg) +{ + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + const struct mspi_cadence_config *config = controller->config; + struct mspi_cadence_data *data = controller->data; + int ret = 0; + + if (data->current_peripheral != dev_id) { + ret = k_mutex_lock(&data->access_lock, + K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE)); + if (ret < 0) { + LOG_ERR("Error waiting for MSPI controller lock for changing device " + "config"); + return ret; + } + data->current_peripheral = dev_id; + } + + if (param_mask & CADENCE_MSPI_IGNORED_DEV_CONFIG_PARAMS) { + if (param_mask == MSPI_DEVICE_CONFIG_ALL) { + LOG_ERR("Device configuration includes ignored / not implemented " + "parameters. For " + "better compatibility these are ignored without returning an error " + "due to the usage " + "of MSPI_DEVICE_CONFIG_ALL. To see which " + "parameters are explicitly ignored check mspi_cadence.h"); + } else { + LOG_ERR("Device configuration includes ignored / not implemented " + "parameters. Check mspi_cadence.h to figure out what isn't " + "supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_BREAK_TIME) { + if (dev_cfg->time_to_break != 0) { + LOG_ERR("Automatically breaking up transfers is not supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_MEM_BOUND) { + if (dev_cfg->mem_boundary != 0) { + LOG_ERR("Automatically breaking up transfers is not supported"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_ENDIAN) { + if (dev_cfg->endian != MSPI_XFER_LITTLE_ENDIAN) { + LOG_ERR("Only little Endian is supported for now"); + /* There is no hardware native support for big endian but it can be + * done in software + */ + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_CE_POL) { + if (dev_cfg->ce_polarity != MSPI_CE_ACTIVE_LOW) { + LOG_ERR("Non active low chip enable polarities haven't been implemented " + "yet"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DQS) { + if (dev_cfg->dqs_enable) { + LOG_ERR("DQS is not implemented yet"); + ret = -ENOSYS; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { + if (dev_cfg->data_rate != MSPI_DATA_RATE_SINGLE) { + LOG_ERR("Only single data rate is supported for now"); + ret = -ENOSYS; + goto exit; + } + } + + /* Disable MSPI during configuration */ + uint32_t config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + ret = mspi_cadence_wait_for_idle(controller); + if (ret < 0) { + goto exit; + } + + if (param_mask & MSPI_DEVICE_CONFIG_CE_NUM) { + uint32_t num; + + if (dev_cfg->ce_num > 3) { + LOG_ERR("Non implemented chip select. Only hardware CS 0 to 3 are " + "implemented"); + ret = -ENOSYS; + goto exit; + } + num = ~BIT(dev_cfg->ce_num) & BIT_MASK(4); + + config_reg &= ~CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK; + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK, num); + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + } + if (param_mask & MSPI_DEVICE_CONFIG_IO_MODE) { + ret = mspi_cadence_set_opcode_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + ret = mspi_cadence_set_data_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + ret = mspi_cadence_set_addr_lines(base_addr, dev_cfg->io_mode); + if (ret) { + goto exit; + } + } + if (param_mask & MSPI_DEVICE_CONFIG_CPP) { + config_reg &= ~CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT; + config_reg &= ~CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT; + switch (dev_cfg->cpp) { + case MSPI_CPP_MODE_0: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 0); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 0); + break; + case MSPI_CPP_MODE_1: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 0); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 1); + break; + case MSPI_CPP_MODE_2: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 1); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 0); + break; + case MSPI_CPP_MODE_3: + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT, 1); + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT, 1); + break; + default: + LOG_ERR("Invalid clock polarity/phase configuration"); + ret = -ENOTSUP; + goto exit; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { + config_reg &= ~CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK; + + /* + * This is underflow-safe (assuming the reference frequency is set) + * since required_div will be at least 1 leading to the second + * DIV_ROUND_UP also returning at least 1 before being subtracted 1 + * resulting in 0. + */ + uint32_t required_div = DIV_ROUND_UP(config->reference_frequency, dev_cfg->freq); + uint32_t actual_div = DIV_ROUND_UP(required_div, 2) - 1; + + if (actual_div > 15) { + LOG_ERR("Requested a frequency that is too low to achieve by dividing the " + "reference clock"); + ret = -ENOTSUP; + goto exit; + } + config_reg |= FIELD_PREP(CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK, actual_div); + } + + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + +exit: + /* Re-enable MSPI */ + config_reg = sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET); + config_reg |= CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT; + sys_write32(config_reg, base_addr + CADENCE_MSPI_CONFIG_OFFSET); + + /* Unlock in case of an error */ + if (ret != 0) { + k_mutex_unlock(&data->access_lock); + } + + return ret; +} + +static int mspi_cadence_get_channel_status(const struct device *controller, uint8_t channel) +{ + ARG_UNUSED(channel); + + const mem_addr_t base_addr = DEVICE_MMIO_GET(controller); + struct mspi_cadence_data *data = controller->data; + + if ((sys_read32(base_addr + CADENCE_MSPI_CONFIG_OFFSET) & + CADENCE_MSPI_CONFIG_REG_IDLE_BIT) == 0) { + return -EBUSY; + } + + if (k_sem_count_get(&data->transfer_lock) == 0) { + return -EBUSY; + } + + data->current_peripheral = NULL; + + k_mutex_unlock(&data->access_lock); + return 0; +} + +static DEVICE_API(mspi, mspi_cadence_driver_api) = { + .config = NULL, + .dev_config = mspi_cadence_dev_config, + .xip_config = NULL, + .scramble_config = NULL, + .timing_config = NULL, + .get_channel_status = mspi_cadence_get_channel_status, + .register_callback = NULL, + .transceive = mspi_cadence_transceive, +}; + +#define CADENCE_CHECK_MULTIPERIPHERAL(n) \ + BUILD_ASSERT(DT_PROP_OR(DT_DRV_INST(n), software_multiperipheral, 0) == 0, \ + "Multiperipherals arent's supported by the driver as of now") + +#define MSPI_CONFIG(n) \ + {.op_mode = DT_INST_ENUM_IDX_OR(n, op_mode, MSPI_OP_MODE_CONTROLLER), \ + .sw_multi_periph = DT_INST_PROP(n, software_multiperipheral)} + +#define CADENCE_MSPI_DEFINE(n) \ + CADENCE_CHECK_MULTIPERIPHERAL(n); \ + PINCTRL_DT_DEFINE(DT_DRV_INST(n)); \ + static const struct mspi_cadence_config mspi_cadence_config_##n = { \ + DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \ + .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + .mspi_config = MSPI_CONFIG(n), \ + .fifo_addr = DT_REG_ADDR_BY_IDX(DT_DRV_INST(n), 1), \ + .sram_allocated_for_read = DT_PROP(DT_DRV_INST(n), read_buffer_size), \ + .reference_frequency = DT_PROP(DT_DRV_INST(n), clock_frequency), \ + }; \ + static struct mspi_cadence_data mspi_cadence_data_##n = {}; \ + DEVICE_DT_INST_DEFINE(n, mspi_cadence_init, NULL, &mspi_cadence_data_##n, \ + &mspi_cadence_config_##n, PRE_KERNEL_2, CONFIG_MSPI_INIT_PRIORITY, \ + &mspi_cadence_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(CADENCE_MSPI_DEFINE) diff --git a/drivers/mspi/mspi_cadence.h b/drivers/mspi/mspi_cadence.h new file mode 100644 index 0000000000000..ebba39a18e178 --- /dev/null +++ b/drivers/mspi/mspi_cadence.h @@ -0,0 +1,360 @@ +/* + * SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors + * SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_MSPI_CDNS_H_ +#define ZEPHYR_DRIVERS_MSPI_CDNS_H_ + +#include +#include + +/* + * Implemented dev_cfg_bits. Implemented means that the value is at least + * checked for undesireable values. Changing any of these values might not be + * actually supported in the end! + */ +#define CADENCE_MSPI_IMPLEMENTED_DEV_CONFIG_PARAMS \ + (MSPI_DEVICE_CONFIG_BREAK_TIME | MSPI_DEVICE_CONFIG_MEM_BOUND | \ + MSPI_DEVICE_CONFIG_ENDIAN | MSPI_DEVICE_CONFIG_CE_POL | MSPI_DEVICE_CONFIG_DQS | \ + MSPI_DEVICE_CONFIG_DATA_RATE | MSPI_DEVICE_CONFIG_CE_NUM | MSPI_DEVICE_CONFIG_IO_MODE | \ + MSPI_DEVICE_CONFIG_CPP | MSPI_DEVICE_CONFIG_FREQUENCY | MSPI_DEVICE_CONFIG_CE_POL) + +/* + * Non-implemented dev_cfg_bits + * + * These configuration requests are not implemented by the driver. For + * compatibility no error is returned, if especially MSPI_DEVICE_CONFIG_ALL was + * requested. + */ +#define CADENCE_MSPI_IGNORED_DEV_CONFIG_PARAMS \ + (MSPI_DEVICE_CONFIG_ALL & ~CADENCE_MSPI_IMPLEMENTED_DEV_CONFIG_PARAMS) + +/* Default delay for time between clock enablement and chip select and other */ +#define CADENCE_MSPI_DEFAULT_DELAY 10 + +/* Timeout calculations and default timeout values */ +#define CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS 10 +#define CADENCE_MSPI_TIME_BETWEEN_RETRIES K_MSEC(CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS) +#define CADENCE_MSPI_GET_NUM_RETRIES(timeout_ms) (timeout_ms / CADENCE_MSPI_TIME_BETWEEN_RETRIES_MS) + +/* General register offsets */ +#define CADENCE_MSPI_CONFIG_OFFSET 0x0u +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_OFFSET 0x4u +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_OFFSET 0x8u +#define CADENCE_MSPI_DEV_DELAY_OFFSET 0xcu +#define CADENCE_MSPI_RD_DATA_CAPTURE_OFFSET 0x10u +#define CADENCE_MSPI_DEV_SIZE_CONFIG_OFFSET 0x14u +#define CADENCE_MSPI_SRAM_PARTITION_CFG_OFFSET 0x18u +#define CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_OFFSET 0x1cu +#define CADENCE_MSPI_DMA_PERIPH_CONFIG_OFFSET 0x20u +#define CADENCE_MSPI_REMAP_ADDR_OFFSET 0x24u +#define CADENCE_MSPI_MODE_BIT_CONFIG_OFFSET 0x28u +#define CADENCE_MSPI_SRAM_FILL_OFFSET 0x2cu +#define CADENCE_MSPI_TX_THRESH_OFFSET 0x30u +#define CADENCE_MSPI_RX_THRESH_OFFSET 0x34u +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_OFFSET 0x38u +#define CADENCE_MSPI_NO_OF_POLLS_BEF_EXP_OFFSET 0x3cu +#define CADENCE_MSPI_IRQ_STATUS_OFFSET 0x40u +#define CADENCE_MSPI_IRQ_MASK_OFFSET 0x44u +#define CADENCE_MSPI_LOWER_WR_PROT_OFFSET 0x50u +#define CADENCE_MSPI_UPPER_WR_PROT_OFFSET 0x54u +#define CADENCE_MSPI_WR_PROT_CTRL_OFFSET 0x58u +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_OFFSET 0x60u +#define CADENCE_MSPI_INDIRECT_READ_XFER_WATERMARK_OFFSET 0x64u +#define CADENCE_MSPI_INDIRECT_READ_XFER_START_OFFSET 0x68u +#define CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_OFFSET 0x6cu +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_OFFSET 0x70u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_WATERMARK_OFFSET 0x74u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_START_OFFSET 0x78u +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_OFFSET 0x7cu +#define CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_OFFSET 0x80u +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_OFFSET 0x8cu +#define CADENCE_MSPI_FLASH_CMD_CTRL_OFFSET 0x90u +#define CADENCE_MSPI_FLASH_CMD_ADDR_OFFSET 0x94u +#define CADENCE_MSPI_FLASH_RD_DATA_LOWER_OFFSET 0xa0u +#define CADENCE_MSPI_FLASH_RD_DATA_UPPER_OFFSET 0xa4u +#define CADENCE_MSPI_FLASH_WR_DATA_LOWER_OFFSET 0xa8u +#define CADENCE_MSPI_FLASH_WR_DATA_UPPER_OFFSET 0xacu +#define CADENCE_MSPI_POLLING_FLASH_STATUS_OFFSET 0xb0u +#define CADENCE_MSPI_PHY_CONFIGURATION_OFFSET 0xb4u +#define CADENCE_MSPI_PHY_MASTER_CONTROL_OFFSET 0xb8u +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_OFFSET 0xbcu +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_OFFSET 0xc0u +#define CADENCE_MSPI_OPCODE_EXT_LOWER_OFFSET 0xe0u +#define CADENCE_MSPI_OPCODE_EXT_UPPER_OFFSET 0xe4u +#define CADENCE_MSPI_MODULE_ID_OFFSET 0xfcu + +/* CONFIG */ +#define CADENCE_MSPI_CONFIG_REG_IDLE_BIT BIT(31) +#define CADENCE_MSPI_CONFIG_REG_DUAL_BYTE_OPCODE_EN_BIT BIT(30) +#define CADENCE_MSPI_CONFIG_REG_CRC_ENABLE_BIT BIT(29) +#define CADENCE_MSPI_CONFIG_REG_PIPELINE_PHY_BIT BIT(25) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_DTR_PROTOCOL_BIT BIT(24) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_AHB_DECODER_BIT BIT(23) +#define CADENCE_MSPI_CONFIG_REG_MSTR_BAUD_DIV_MASK GENMASK(22, 19) +#define CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_IMM_BIT BIT(18) +#define CADENCE_MSPI_CONFIG_REG_ENTER_XIP_MODE_BIT BIT(17) +#define CADENCE_MSPI_CONFIG_REG_ENB_AHB_ADDR_REMAP_BIT BIT(16) +#define CADENCE_MSPI_CONFIG_REG_ENB_DMA_IF_BIT BIT(15) +#define CADENCE_MSPI_CONFIG_REG_WR_PROT_FLASH_BIT BIT(14) +#define CADENCE_MSPI_CONFIG_REG_PERIPH_CS_LINES_MASK GENMASK(13, 10) +#define CADENCE_MSPI_CONFIG_REG_PERIPH_SEL_DEC_BIT BIT(9) +#define CADENCE_MSPI_CONFIG_REG_ENB_LEGACY_IP_MODE_BIT BIT(8) +#define CADENCE_MSPI_CONFIG_REG_ENB_DIR_ACC_CTRL_BIT BIT(7) +#define CADENCE_MSPI_CONFIG_REG_RESET_CFG_BIT BIT(6) +#define CADENCE_MSPI_CONFIG_REG_RESET_PIN_BIT BIT(5) +#define CADENCE_MSPI_CONFIG_REG_HOLD_PIN_BIT BIT(4) +#define CADENCE_MSPI_CONFIG_REG_PHY_MODE_ENABLE_BIT BIT(3) +#define CADENCE_MSPI_CONFIG_REG_SEL_CLK_PHASE_BIT BIT(2) +#define CADENCE_MSPI_CONFIG_REG_SEL_CLK_POL_BIT BIT(1) +#define CADENCE_MSPI_CONFIG_REG_ENABLE_SPI_BIT BIT(0) + +/* DEV_INSTR_RD_CONFIG */ +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MASK GENMASK(28, 24) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_MODE_BIT_ENABLE_BIT BIT(20) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK GENMASK(17, 16) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK GENMASK(13, 12) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_DDR_EN_BIT BIT(10) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_INSTR_TYPE_MASK GENMASK(9, 8) +#define CADENCE_MSPI_DEV_INSTR_RD_CONFIG_REG_RD_OPCODE_NON_XIP_MASK GENMASK(7, 0) + +/* DEV_INSTR_WR_CONFIG */ +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MASK GENMASK(28, 24) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_DATA_XFER_TYPE_EXT_MODE_MASK GENMASK(17, 16) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_ADDR_XFER_TYPE_STD_MODE_MASK GENMASK(13, 12) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WEL_DIS_BIT BIT(8) +#define CADENCE_MSPI_DEV_INSTR_WR_CONFIG_REG_WR_OPCODE_NON_XIP_MASK GENMASK(7, 0) + +/* Max value of DUMMY_RD_CLK_CYCLES_FLD / DUMMY_WR_CLK_CYCLES_FLD */ +#define CADENCE_MSPI_INSTR_RD_CONFIG_REG_DUMMY_RD_CLK_CYCLES_MAX_VALUE 0x1F +#define CADENCE_MSPI_INSTR_WR_CONFIG_REG_DUMMY_WR_CLK_CYCLES_MAX_VALUE 0x1F + +/* DEV_DELAY */ +#define CADENCE_MSPI_DEV_DELAY_REG_D_NSS_MASK GENMASK(31, 24) +#define CADENCE_MSPI_DEV_DELAY_REG_D_BTWN_MASK GENMASK(23, 16) +#define CADENCE_MSPI_DEV_DELAY_REG_D_AFTER_MASK GENMASK(15, 8) +#define CADENCE_MSPI_DEV_DELAY_REG_D_INIT_MASK GENMASK(7, 0) + +/* RD_DATA_CAPTURE */ +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DDR_READ_DELAY_MASK GENMASK(19, 16) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DQS_ENABLE_BIT BIT(8) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_SAMPLE_EDGE_SEL_BIT BIT(5) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_DELAY_MASK GENMASK(4, 1) +#define CADENCE_MSPI_RD_DATA_CAPTURE_REG_BYPASS_BIT BIT(0) + +/* DEV_SIZE_CONFIG */ +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS3_MASK GENMASK(28, 27) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS2_MASK GENMASK(26, 25) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS1_MASK GENMASK(24, 23) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_MEM_SIZE_ON_CS0_MASK GENMASK(22, 21) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_BYTES_PER_SUBSECTOR_MASK GENMASK(20, 16) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_BYTES_PER_DEVICE_PAGE_MASK GENMASK(15, 4) +#define CADENCE_MSPI_DEV_SIZE_CONFIG_REG_NUM_ADDR_BYTES_MASK GENMASK(3, 0) + +/* SRAM_PARTITION_CFG */ +#define CADENCE_MSPI_SRAM_PARTITION_CFG_REG_ADDR_MASK GENMASK(7, 0) + +/* INDIRECT_TRIGGER_ADDR_RANGE */ +#define CADENCE_MSPI_INDIRECT_TRIGGER_ADDR_RANGE_REG_IND_RANGE_WIDTH_MASK GENMASK(3, 0) + +/* REMAP_ADDR */ +#define CADENCE_MSPI_REMAP_ADDR_REG_VALUE_MASK GENMASK(31, 0) + +/* SRAM_FILL */ +#define CADENCE_MSPI_SRAM_FILL_REG_INDAC_WRITE_MASK GENMASK(31, 16) +#define CADENCE_MSPI_SRAM_FILL_REG_INDAC_READ_MASK GENMASK(15, 0) + +/* TX_THRESH */ +#define CADENCE_MSPI_TX_THRESH_REG_LEVEL_MASK GENMASK(4, 0) + +/* RX_THRESH */ +#define CADENCE_MSPI_RX_THRESH_REG_LEVEL_MASK GENMASK(4, 0) + +/* WRITE_COMPLETION_CTRL */ +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLL_REP_DELAY_MASK GENMASK(31, 24) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLL_COUNT_MASK GENMASK(23, 16) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_ENABLE_POLLING_EXP_BIT BIT(15) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_DISABLE_POLLING_BIT BIT(14) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLLING_POLARITY_BIT BIT(13) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_POLLING_BIT_INDEX_MASK GENMASK(10, 8) +#define CADENCE_MSPI_WRITE_COMPLETION_CTRL_REG_OPCODE_MASK GENMASK(7, 0) + +/* NO_OF_POLLS_BEF_EXP */ +#define CADENCE_MSPI_NO_OF_POLLS_BEF_EXP_REG_NO_OF_POLLS_BEF_EXP_MASK GENMASK(31, 0) + +/* IRQ_STATUS */ +#define CADENCE_MSPI_IRQ_STATUS_REG_ECC_FAIL_BIT BIT(19) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_CRC_CHUNK_BRK_BIT BIT(18) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_CRC_DATA_VAL_BIT BIT(17) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_CRC_DATA_ERR_BIT BIT(16) +#define CADENCE_MSPI_IRQ_STATUS_REG_STIG_REQ_INT_BIT BIT(14) +#define CADENCE_MSPI_IRQ_STATUS_REG_POLL_EXP_INT_BIT BIT(13) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDRD_SRAM_FULL_BIT BIT(12) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_FIFO_FULL_BIT BIT(11) +#define CADENCE_MSPI_IRQ_STATUS_REG_RX_FIFO_NOT_EMPTY_BIT BIT(10) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_FIFO_FULL_BIT BIT(9) +#define CADENCE_MSPI_IRQ_STATUS_REG_TX_FIFO_NOT_FULL_BIT BIT(8) +#define CADENCE_MSPI_IRQ_STATUS_REG_RECV_OVERFLOW_BIT BIT(7) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_XFER_LEVEL_BREACH_BIT BIT(6) +#define CADENCE_MSPI_IRQ_STATUS_REG_ILLEGAL_ACCESS_DET_BIT BIT(5) +#define CADENCE_MSPI_IRQ_STATUS_REG_PROT_WR_ATTEMPT_BIT BIT(4) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_READ_REJECT_BIT BIT(3) +#define CADENCE_MSPI_IRQ_STATUS_REG_INDIRECT_OP_DONE_BIT BIT(2) +#define CADENCE_MSPI_IRQ_STATUS_REG_UNDERFLOW_DET_BIT BIT(1) +#define CADENCE_MSPI_IRQ_STATUS_REG_MODE_M_FAIL_BIT BIT(0) + +#define CADENCE_MSPI_IRQ_STATUS_REG_ALL (BIT_MASK(19) & ~BIT(15)) + +/* IRQ_MASK */ +#define CADENCE_MSPI_IRQ_BIT_REG_ECC_FAIL_MASK_MASK BIT(19) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_CRC_CHUNK_BRK_MASK_MASK BIT(18) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_CRC_DATA_VAL_MASK_MASK BIT(17) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_CRC_DATA_ERR_MASK_MASK BIT(16) +#define CADENCE_MSPI_IRQ_BIT_REG_STIG_REQ_INT_MASK_MASK BIT(14) +#define CADENCE_MSPI_IRQ_BIT_REG_POLL_EXP_INT_MASK_MASK BIT(13) +#define CADENCE_MSPI_IRQ_BIT_REG_INDRD_SRAM_FULL_MASK_MASK BIT(12) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_FIFO_FULL_MASK_MASK BIT(11) +#define CADENCE_MSPI_IRQ_BIT_REG_RX_FIFO_NOT_EMPTY_MASK_MASK BIT(10) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_FIFO_FULL_MASK_MASK BIT(9) +#define CADENCE_MSPI_IRQ_BIT_REG_TX_FIFO_NOT_FULL_MASK_MASK BIT(8) +#define CADENCE_MSPI_IRQ_BIT_REG_RECV_OVERFLOW_MASK_MASK BIT(7) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_XFER_LEVEL_BREACH_MASK_MASK BIT(6) +#define CADENCE_MSPI_IRQ_BIT_REG_ILLEGAL_ACCESS_DET_MASK_MASK BIT(5) +#define CADENCE_MSPI_IRQ_BIT_REG_PROT_WR_ATTEMPT_MASK_MASK BIT(4) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_READ_REJECT_MASK_MASK BIT(3) +#define CADENCE_MSPI_IRQ_BIT_REG_INDIRECT_OP_DONE_MASK_MASK BIT(2) +#define CADENCE_MSPI_IRQ_BIT_REG_UNDERFLOW_DET_MASK_MASK BIT(1) +#define CADENCE_MSPI_IRQ_BIT_REG_MODE_M_FAIL_MASK_MASK BIT(0) + +#define CADENCE_MSPI_IRQ_MASK_REG_ALL (BIT_MASK(19) & ~BIT(15)) + +/* LOWER_WR_PROT */ +#define CADENCE_MSPI_LOWER_WR_PROT_REG_SUBSECTOR_MASK GENMASK(31, 0) + +/* UPPER_WR_PROT */ +#define CADENCE_MSPI_UPPER_WR_PROT_REG_SUBSECTOR_MASK GENMASK(31, 0) + +/* WR_PROT_CTRL */ +#define CADENCE_MSPI_WR_PROT_CTRL_REG_ENB_BIT BIT(1) +#define CADENCE_MSPI_WR_PROT_CTRL_REG_INV_BIT BIT(0) + +/* INDIRECT_READ_XFER_CTRL */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_NUM_IND_OPS_DONE_MASK GENMASK(7, 6) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT BIT(5) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_RD_QUEUED_BIT BIT(4) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_SRAM_FULL_BIT BIT(3) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_RD_STATUS_BIT BIT(2) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_CANCEL_BIT BIT(1) +#define CADENCE_MSPI_INDIRECT_READ_XFER_CTRL_REG_START_BIT BIT(0) + +/* INDIRECT_READ_XFER_WATERMARK */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_WATERMARK_REG_LEVEL_MASK GENMASK(31, 0) + +/* INDIRECT_READ_XFER_START */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_START_REG_ADDR_MASK GENMASK(31, 0) + +/* INDIRECT_READ_XFER_NUM_BYTES */ +#define CADENCE_MSPI_INDIRECT_READ_XFER_NUM_BYTES_REG_VALUE_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_CTRL_REG */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_NUM_IND_OPS_DONE_MASK GENMASK(7, 6) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_IND_OPS_DONE_STATUS_BIT BIT(5) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_WR_QUEUED_BIT BIT(4) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_WR_STATUS_BIT BIT(2) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_CANCEL_BIT BIT(1) +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_CTRL_REG_START_BIT BIT(0) + +/* INDIRECT_WRITE_XFER_WATERMARK */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_WATERMARK_REG_LEVEL_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_START */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_START_REG_ADDR_MASK GENMASK(31, 0) + +/* INDIRECT_WRITE_XFER_NUM_BYTES */ +#define CADENCE_MSPI_INDIRECT_WRITE_XFER_NUM_BYTES_REG_VALUE_MASK GENMASK(31, 0) + +/* IND_AHB_ADDR_TRIGGER */ +#define CADENCE_MSPI_IND_AHB_ADDR_TRIGGER_REG_ADDR_MASK GENMASK(31, 0) + +/* FLASH_COMMAND_CTRL_MEM */ +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_ADDR_MASK GENMASK(28, 20) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_NB_OF_STIG_READ_BYTES_MASK GENMASK(18, 16) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_READ_DATA_MASK GENMASK(15, 8) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_MEM_BANK_REQ_IN_PROGRESS_BIT BIT(1) +#define CADENCE_MSPI_FLASH_COMMAND_CTRL_MEM_REG_TRIGGER_MEM_BANK_REQ_BIT BIT(0) + +/* FLASH_CMD_CTRL */ +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_READ_DATA_BIT BIT(23) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_RD_DATA_BYTES_MASK GENMASK(22, 20) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_COMD_ADDR_BIT BIT(19) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_MODE_BIT_BIT BIT(18) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_ADDR_BYTES_MASK GENMASK(17, 16) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_ENB_WRITE_DATA_BIT BIT(15) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_WR_DATA_BYTES_MASK GENMASK(14, 12) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_NUM_DUMMY_CYCLES_MASK GENMASK(11, 7) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_STIG_MEM_BANK_EN_BIT BIT(2) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_STATUS_BIT BIT(1) +#define CADENCE_MSPI_FLASH_CMD_CTRL_REG_CMD_EXEC_BIT BIT(0) + +/* FLASH_CMD_ADDR */ +#define CADENCE_MSPI_FLASH_CMD_ADDR_REG_ADDR_MASK GENMASK(31, 0) + +/* FLASH_RD_DATA_LOWER */ +#define CADENCE_MSPI_FLASH_RD_DATA_LOWER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_RD_DATA_UPPER */ +#define CADENCE_MSPI_FLASH_RD_DATA_UPPER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_WR_DATA_LOWER */ +#define CADENCE_MSPI_FLASH_WR_DATA_LOWER_REG_DATA_MASK GENMASK(31, 0) + +/* FLASH_WR_DATA_UPPER */ +#define CADENCE_MSPI_FLASH_WR_DATA_UPPER_REG_DATA_MASK GENMASK(31, 0) + +/* POLLING_FLASH_STATUS */ +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_NB_DUMMY_MASK GENMASK(19, 16) +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_VALID_BIT BIT(8) +#define CADENCE_MSPI_POLLING_FLASH_STATUS_REG_DEVICE_STATUS_MASK GENMASK(7, 0) + +/* PHY_CONFIGURATION */ +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RESYNC_BIT BIT(31) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RESET_BIT BIT(30) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RX_DLL_BYPASS_BIT BIT(29) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_TX_DLL_DELAY_MASK GENMASK(22, 16) +#define CADENCE_MSPI_PHY_REG_CONFIGURATION_RX_DLL_DELAY_MASK GENMASK(6, 0) + +/* PHY_MASTER_CONTROL */ +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_LOCK_MODE_BIT BIT(24) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_BYPASS_MODE_BIT BIT(23) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_PHASE_DETECT_SELECTOR_MASK GENMASK(22, 20) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_NB_INDICATIONS_MASK GENMASK(18, 16) +#define CADENCE_MSPI_PHY_MASTER_CONTROL_REG_INITIAL_DELAY_MASK GENMASK(6, 0) + +/* DLL_OBSERVABLE_LOWER */ +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_INC_MASK GENMASK(31, 24) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_DEC_MASK GENMASK(23, 16) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOOPBACK_LOCK_BIT BIT(15) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOCK_VALUE_MASK GENMASK(14, 8) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_UNLOCK_COUNTER_MASK GENMASK(7, 3) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_LOCK_MODE_MASK GENMASK(2, 1) +#define CADENCE_MSPI_DLL_OBSERVABLE_LOWER_REG_DLL_LOCK_BIT BIT(0) + +/* DLL_OBSERVABLE_UPPER */ +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_REG_TX_DECODER_OUTPUT_MASK GENMASK(22, 16) +#define CADENCE_MSPI_DLL_OBSERVABLE_UPPER_REG_RX_DECODER_OUTPUT_MASK GENMASK(6, 0) + +/* OPCODE_EXT_LOWER */ +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_READ_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_WRITE_OPCODE_MASK GENMASK(23, 16) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_POLL_OPCODE_MASK GENMASK(15, 8) +#define CADENCE_MSPI_OPCODE_EXT_LOWER_REG_EXT_STIG_OPCODE_MASK GENMASK(7, 0) + +/* OPCODE_EXT_UPPER */ +#define CADENCE_MSPI_OPCODE_EXT_UPPER_REG_WEL_OPCODE_MASK GENMASK(31, 24) +#define CADENCE_MSPI_OPCODE_EXT_UPPER_REG_EXT_WEL_OPCODE_MASK GENMASK(23, 16) + +#endif /* ZEPHYR_DRIVERS_MSPI_CDNS_H_ */ diff --git a/dts/bindings/mspi/cdns,mspi-controller.yaml b/dts/bindings/mspi/cdns,mspi-controller.yaml new file mode 100644 index 0000000000000..74123a15412ad --- /dev/null +++ b/dts/bindings/mspi/cdns,mspi-controller.yaml @@ -0,0 +1,35 @@ +# SPDX-FileCopyrightText: Copyright The Zephyr Project Contributors +# SPDX-FileCopyrightText: Copyright (c) 2025 - 2026 Siemens Mobility GmbH +# +# SPDX-License-Identifier: Apache-2.0 + +description: Cadence MSPI Controller + +compatible: "cdns,mspi-controller" + +include: [mspi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + description: | + Address and length of the MSPI configuration register and the indirect + write FIFO location + + clock-frequency: + required: true + + read-buffer-size: + required: true + type: int + description: | + Amount of address bits of the internal peripheral SRAM bus that are used + for indirects reads. The other address bits are used for indirect write + operations. The amount of available bits is dependent on the SRAM depth + configuration used inside the peripheral and the resulting amount of bytes + depends on the bus width, which may be specified in the SoC datasheet. + + It is recommended to look at the SoC datasheet/TRM and set this to half of + the maximum of the "SRAM Partition Configuration Register" value (which is + also its reset value), leading to half of the locations being used for + indirect reads and the other half for indirect writes diff --git a/dts/bindings/mtd/infineon,s25h-flash.yaml b/dts/bindings/mtd/infineon,s25h-flash.yaml new file mode 100644 index 0000000000000..ff783b348800b --- /dev/null +++ b/dts/bindings/mtd/infineon,s25h-flash.yaml @@ -0,0 +1,45 @@ +# Copyright (c) 2025 - 2026 Siemens Mobility GmbH +# SPDX-License-Identifier: Apache-2.0 + +description: MSPI Infineon S25H Flash + +compatible: "infineon,s25h-flash" + +include: ["soc-nv-flash.yaml", "pinctrl-device.yaml", "mspi-device.yaml"] + +properties: + reg: + required: true + + reset-startup-time-us: + required: true + type: int + description: | + Time the device requires to recover after a reset in microseconds + + flash-size: + required: true + type: int + description: | + Size of the flash in byte + + device-id: + required: true + type: int + description: | + 16 bit JEDEC device id of the flash device that will be read to verify the + communication is working + + manufacturer-id: + required: true + type: int + description: | + 8 bit JEDEC manufacturer id of the flash device that will be read to + verify the communication is working + + keep-startup-mspi-config: + type: boolean + description: | + Don't change the MSPI config to 4S-4S-4S during startup and stay at the + configuration specified in the devicetree. This allows communication with + slow but very similar flash device that aren't fully supported yet