From e1aad03a3298951acbb3cdfc7941deb131a6bc9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Thu, 17 Oct 2024 14:22:45 +0200 Subject: [PATCH 1/7] drivers: mspi: Add driver for DesignWare SSI based controllers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a generic driver for MSPI controllers based on the DesignWare SSI core. With small vendor-specific adaptations covering integration details, it should be possible to use the driver for various devices. Signed-off-by: Andrzej Głąbek --- drivers/mspi/CMakeLists.txt | 1 + drivers/mspi/Kconfig | 1 + drivers/mspi/Kconfig.dw | 9 + drivers/mspi/mspi_dw.c | 1374 ++++++++++++++++++++ drivers/mspi/mspi_dw.h | 239 ++++ drivers/mspi/mspi_dw_vendor_specific.h | 88 ++ dts/bindings/mspi/snps,designware-ssi.yaml | 45 + 7 files changed, 1757 insertions(+) create mode 100644 drivers/mspi/Kconfig.dw create mode 100644 drivers/mspi/mspi_dw.c create mode 100644 drivers/mspi/mspi_dw.h create mode 100644 drivers/mspi/mspi_dw_vendor_specific.h create mode 100644 dts/bindings/mspi/snps,designware-ssi.yaml diff --git a/drivers/mspi/CMakeLists.txt b/drivers/mspi/CMakeLists.txt index f248cb0b53337..78f704393f428 100644 --- a/drivers/mspi/CMakeLists.txt +++ b/drivers/mspi/CMakeLists.txt @@ -4,4 +4,5 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/mspi.h) zephyr_library() zephyr_library_sources_ifdef(CONFIG_MSPI_AMBIQ_AP3 mspi_ambiq_ap3.c) +zephyr_library_sources_ifdef(CONFIG_MSPI_DW mspi_dw.c) zephyr_library_sources_ifdef(CONFIG_MSPI_EMUL mspi_emul.c) diff --git a/drivers/mspi/Kconfig b/drivers/mspi/Kconfig index 0adb2a3439338..269d8d16f04ac 100644 --- a/drivers/mspi/Kconfig +++ b/drivers/mspi/Kconfig @@ -60,6 +60,7 @@ module-str = mspi source "subsys/logging/Kconfig.template.log_config" source "drivers/mspi/Kconfig.ambiq" +source "drivers/mspi/Kconfig.dw" source "drivers/mspi/Kconfig.mspi_emul" endif # MSPI diff --git a/drivers/mspi/Kconfig.dw b/drivers/mspi/Kconfig.dw new file mode 100644 index 0000000000000..1ab82da0e8547 --- /dev/null +++ b/drivers/mspi/Kconfig.dw @@ -0,0 +1,9 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config MSPI_DW + bool "DesignWare SSI controller driver" + default y + depends on DT_HAS_SNPS_DESIGNWARE_SSI_ENABLED + select PINCTRL if $(dt_compat_any_has_prop,$(DT_COMPAT_SNPS_DESIGNWARE_SSI),pinctrl-0) + imply MSPI_XIP diff --git a/drivers/mspi/mspi_dw.c b/drivers/mspi/mspi_dw.c new file mode 100644 index 0000000000000..39cf463f4beec --- /dev/null +++ b/drivers/mspi/mspi_dw.c @@ -0,0 +1,1374 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT snps_designware_ssi + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mspi_dw.h" +#include "mspi_dw_vendor_specific.h" + +LOG_MODULE_REGISTER(mspi_dw, CONFIG_MSPI_LOG_LEVEL); + +#define DUMMY_BYTE 0xAA + +#if defined(CONFIG_MSPI_XIP) +struct xip_params { + uint32_t read_cmd; + uint32_t write_cmd; + uint16_t rx_dummy; + uint16_t tx_dummy; + uint8_t cmd_length; + uint8_t addr_length; + enum mspi_io_mode io_mode; +}; + +struct xip_ctrl { + uint32_t read; + uint32_t write; +}; +#endif + +struct mspi_dw_data { + const struct mspi_dev_id *dev_id; + uint32_t packets_done; + uint8_t *buf_pos; + const uint8_t *buf_end; + + uint32_t ctrlr0; + uint32_t spi_ctrlr0; + uint32_t baudr; + +#if defined(CONFIG_MSPI_XIP) + uint32_t xip_freq; + struct xip_params xip_params_stored; + struct xip_params xip_params_active; + uint16_t xip_enabled; + enum mspi_cpp_mode xip_cpp; +#endif + + uint16_t dummy_bytes; + uint8_t bytes_to_discard; + uint8_t bytes_per_frame_exp; + bool standard_spi; + bool suspended; + + struct k_sem finished; + /* For synchronization of API calls made from different contexts. */ + struct k_sem ctx_lock; + /* For locking of controller configuration. */ + struct k_sem cfg_lock; + struct mspi_xfer xfer; +}; + +struct mspi_dw_config { + DEVICE_MMIO_ROM; + void (*irq_config)(void); + uint32_t clock_frequency; +#if defined(CONFIG_PINCTRL) + const struct pinctrl_dev_config *pcfg; +#endif + const struct gpio_dt_spec *ce_gpios; + uint8_t ce_gpios_len; + uint8_t tx_fifo_depth_minus_1; + uint8_t tx_fifo_threshold; + uint8_t rx_fifo_threshold; + DECLARE_REG_ACCESS(); + bool sw_multi_periph; +}; + +/* Register access helpers. */ +#define DEFINE_MM_REG_RD_WR(reg, off) \ + DEFINE_MM_REG_RD(reg, off) \ + DEFINE_MM_REG_WR(reg, off) + +DEFINE_MM_REG_WR(ctrlr0, 0x00) +DEFINE_MM_REG_WR(ctrlr1, 0x04) +DEFINE_MM_REG_WR(ssienr, 0x08) +DEFINE_MM_REG_WR(ser, 0x10) +DEFINE_MM_REG_WR(baudr, 0x14) +DEFINE_MM_REG_RD_WR(txftlr, 0x18) +DEFINE_MM_REG_RD_WR(rxftlr, 0x1c) +DEFINE_MM_REG_RD(txflr, 0x20) +DEFINE_MM_REG_RD(rxflr, 0x24) +DEFINE_MM_REG_RD(sr, 0x28) +DEFINE_MM_REG_WR(imr, 0x2c) +DEFINE_MM_REG_RD(isr, 0x30) +DEFINE_MM_REG_RD_WR(dr, 0x60) +DEFINE_MM_REG_WR(spi_ctrlr0, 0xf4) + +#if defined(CONFIG_MSPI_XIP) +DEFINE_MM_REG_WR(xip_incr_inst, 0x100) +DEFINE_MM_REG_WR(xip_wrap_inst, 0x104) +DEFINE_MM_REG_WR(xip_ctrl, 0x108) +DEFINE_MM_REG_WR(xip_write_incr_inst, 0x140) +DEFINE_MM_REG_WR(xip_write_wrap_inst, 0x144) +DEFINE_MM_REG_WR(xip_write_ctrl, 0x148) +#endif + +static void tx_data(const struct device *dev, + const struct mspi_xfer_packet *packet) +{ + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_dw_config *dev_config = dev->config; + const uint8_t *buf_pos = dev_data->buf_pos; + const uint8_t *buf_end = dev_data->buf_end; + /* When the function is called, it is known that at least one item + * can be written to the FIFO. The loop below writes to the FIFO + * the number of items that is known to fit and then updates that + * number basing on the actual FIFO level (because some data may get + * sent while the FIFO is written; especially for high frequencies + * this may often occur) and continues until the FIFO is filled up + * or the buffer end is reached. + */ + uint32_t room = 1; + uint8_t bytes_per_frame_exp = dev_data->bytes_per_frame_exp; + uint8_t tx_fifo_depth = dev_config->tx_fifo_depth_minus_1 + 1; + uint32_t data; + + do { + if (bytes_per_frame_exp == 2) { + data = sys_get_be32(buf_pos); + buf_pos += 4; + } else if (bytes_per_frame_exp == 1) { + data = sys_get_be16(buf_pos); + buf_pos += 2; + } else { + data = *buf_pos; + buf_pos += 1; + } + write_dr(dev, data); + + if (buf_pos >= buf_end) { + write_txftlr(dev, 0); + break; + } + + if (--room == 0) { + room = tx_fifo_depth + - FIELD_GET(TXFLR_TXTFL_MASK, read_txflr(dev)); + } + } while (room); + + dev_data->buf_pos = (uint8_t *)buf_pos; +} + +static bool make_rx_cycles(const struct device *dev) +{ + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_dw_config *dev_config = dev->config; + uint16_t dummy_bytes = dev_data->dummy_bytes; + /* See tx_data(). */ + uint32_t room = 1; + uint8_t tx_fifo_depth = dev_config->tx_fifo_depth_minus_1 + 1; + + do { + write_dr(dev, DUMMY_BYTE); + + --dummy_bytes; + if (!dummy_bytes) { + dev_data->dummy_bytes = 0; + return true; + } + + if (--room == 0) { + room = tx_fifo_depth + - FIELD_GET(TXFLR_TXTFL_MASK, read_txflr(dev)); + } + } while (room); + + dev_data->dummy_bytes = dummy_bytes; + return false; +} + +static void read_rx_fifo(const struct device *dev, + const struct mspi_xfer_packet *packet) +{ + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_dw_config *dev_config = dev->config; + uint8_t bytes_to_discard = dev_data->bytes_to_discard; + uint8_t *buf_pos = dev_data->buf_pos; + const uint8_t *buf_end = &packet->data_buf[packet->num_bytes]; + uint8_t bytes_per_frame_exp = dev_data->bytes_per_frame_exp; + /* See `room` in tx_data(). */ + uint32_t in_fifo = 1; + uint32_t remaining_frames; + + do { + uint32_t data = read_dr(dev); + + if (bytes_to_discard) { + --bytes_to_discard; + } else { + if (bytes_per_frame_exp == 2) { + sys_put_be32(data, buf_pos); + buf_pos += 4; + } else if (bytes_per_frame_exp == 1) { + sys_put_be16(data, buf_pos); + buf_pos += 2; + } else { + *buf_pos = (uint8_t)data; + buf_pos += 1; + } + + if (buf_pos >= buf_end) { + dev_data->bytes_to_discard = bytes_to_discard; + dev_data->buf_pos = buf_pos; + return; + } + } + + if (--in_fifo == 0) { + in_fifo = FIELD_GET(RXFLR_RXTFL_MASK, read_rxflr(dev)); + } + } while (in_fifo); + + remaining_frames = (bytes_to_discard + buf_end - buf_pos) + >> bytes_per_frame_exp; + if (remaining_frames - 1 < dev_config->rx_fifo_threshold) { + write_rxftlr(dev, remaining_frames - 1); + } + + dev_data->bytes_to_discard = bytes_to_discard; + dev_data->buf_pos = buf_pos; +} + +static void mspi_dw_isr(const struct device *dev) +{ + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_xfer_packet *packet = + &dev_data->xfer.packets[dev_data->packets_done]; + uint32_t int_status = read_isr(dev); + + if (int_status & ISR_RXFIS_BIT) { + read_rx_fifo(dev, packet); + } + + if (dev_data->buf_pos >= dev_data->buf_end) { + write_imr(dev, 0); + /* It may happen that at this point the controller is still + * shifting out the last frame (the last interrupt occurs when + * the TX FIFO is empty). Wait if it signals that it is busy. + */ + while (read_sr(dev) & SR_BUSY_BIT) { + } + + k_sem_give(&dev_data->finished); + } else { + if (int_status & ISR_TXEIS_BIT) { + if (dev_data->dummy_bytes) { + if (make_rx_cycles(dev)) { + write_imr(dev, IMR_RXFIM_BIT); + } + } else { + tx_data(dev, packet); + } + } + } + + vendor_specific_irq_clear(dev); +} + +static int api_config(const struct mspi_dt_spec *spec) +{ + ARG_UNUSED(spec); + + return -ENOTSUP; +} + +static bool apply_io_mode(struct mspi_dw_data *dev_data, + enum mspi_io_mode io_mode) +{ + dev_data->ctrlr0 &= ~CTRLR0_SPI_FRF_MASK; + dev_data->spi_ctrlr0 &= ~SPI_CTRLR0_TRANS_TYPE_MASK; + + /* Frame format used for transferring data. */ + + if (io_mode == MSPI_IO_MODE_SINGLE) { + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SPI_FRF_MASK, + CTRLR0_SPI_FRF_STANDARD); + dev_data->standard_spi = true; + return true; + } + + dev_data->standard_spi = false; + + switch (io_mode) { + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SPI_FRF_MASK, + CTRLR0_SPI_FRF_DUAL); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SPI_FRF_MASK, + CTRLR0_SPI_FRF_QUAD); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SPI_FRF_MASK, + CTRLR0_SPI_FRF_OCTAL); + break; + default: + LOG_ERR("IO mode %d not supported", io_mode); + return false; + } + + /* Transfer format used for Address and Instruction: */ + + switch (io_mode) { + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + /* - both sent in Standard SPI mode */ + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_TRANS_TYPE_MASK, + SPI_CTRLR0_TRANS_TYPE_TT0); + break; + case MSPI_IO_MODE_DUAL_1_2_2: + case MSPI_IO_MODE_QUAD_1_4_4: + case MSPI_IO_MODE_OCTAL_1_8_8: + /* - Instruction sent in Standard SPI mode, + * Address sent the same way as data + */ + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_TRANS_TYPE_MASK, + SPI_CTRLR0_TRANS_TYPE_TT1); + break; + default: + /* - both sent the same way as data. */ + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_TRANS_TYPE_MASK, + SPI_CTRLR0_TRANS_TYPE_TT2); + break; + } + + return true; +} + +static bool apply_cmd_length(struct mspi_dw_data *dev_data, uint32_t cmd_length) +{ + switch (cmd_length) { + case 0: + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_INST_L_MASK, + SPI_CTRLR0_INST_L0); + break; + case 1: + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_INST_L_MASK, + SPI_CTRLR0_INST_L8); + break; + case 2: + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_INST_L_MASK, + SPI_CTRLR0_INST_L16); + break; + default: + LOG_ERR("Command length %d not supported", cmd_length); + return false; + } + + return true; +} + +static bool apply_addr_length(struct mspi_dw_data *dev_data, + uint32_t addr_length) +{ + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_ADDR_L_MASK, + addr_length * 2); + + return true; +} + +#if defined(CONFIG_MSPI_XIP) +static bool apply_xip_io_mode(const struct mspi_dw_data *dev_data, + struct xip_ctrl *ctrl) +{ + enum mspi_io_mode io_mode = dev_data->xip_params_active.io_mode; + + /* Frame format used for transferring data. */ + + if (io_mode == MSPI_IO_MODE_SINGLE) { + LOG_ERR("XIP not available in single line mode"); + return false; + } + + switch (io_mode) { + case MSPI_IO_MODE_DUAL: + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_DUAL_1_2_2: + ctrl->read |= FIELD_PREP(XIP_CTRL_FRF_MASK, + XIP_CTRL_FRF_DUAL); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_FRF_MASK, + XIP_WRITE_CTRL_FRF_DUAL); + break; + case MSPI_IO_MODE_QUAD: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_QUAD_1_4_4: + ctrl->read |= FIELD_PREP(XIP_CTRL_FRF_MASK, + XIP_CTRL_FRF_QUAD); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_FRF_MASK, + XIP_WRITE_CTRL_FRF_QUAD); + break; + case MSPI_IO_MODE_OCTAL: + case MSPI_IO_MODE_OCTAL_1_1_8: + case MSPI_IO_MODE_OCTAL_1_8_8: + ctrl->read |= FIELD_PREP(XIP_CTRL_FRF_MASK, + XIP_CTRL_FRF_OCTAL); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_FRF_MASK, + XIP_WRITE_CTRL_FRF_OCTAL); + break; + default: + LOG_ERR("IO mode %d not supported", io_mode); + return false; + } + + /* Transfer format used for Address and Instruction: */ + + switch (io_mode) { + case MSPI_IO_MODE_DUAL_1_1_2: + case MSPI_IO_MODE_QUAD_1_1_4: + case MSPI_IO_MODE_OCTAL_1_1_8: + /* - both sent in Standard SPI mode */ + ctrl->read |= FIELD_PREP(XIP_CTRL_TRANS_TYPE_MASK, + XIP_CTRL_TRANS_TYPE_TT0); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_TRANS_TYPE_MASK, + XIP_WRITE_CTRL_TRANS_TYPE_TT0); + break; + case MSPI_IO_MODE_DUAL_1_2_2: + case MSPI_IO_MODE_QUAD_1_4_4: + case MSPI_IO_MODE_OCTAL_1_8_8: + /* - Instruction sent in Standard SPI mode, + * Address sent the same way as data + */ + ctrl->read |= FIELD_PREP(XIP_CTRL_TRANS_TYPE_MASK, + XIP_CTRL_TRANS_TYPE_TT1); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_TRANS_TYPE_MASK, + XIP_WRITE_CTRL_TRANS_TYPE_TT1); + break; + default: + /* - both sent the same way as data. */ + ctrl->read |= FIELD_PREP(XIP_CTRL_TRANS_TYPE_MASK, + XIP_CTRL_TRANS_TYPE_TT2); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_TRANS_TYPE_MASK, + XIP_WRITE_CTRL_TRANS_TYPE_TT2); + break; + } + + return true; +} + +static bool apply_xip_cmd_length(const struct mspi_dw_data *dev_data, + struct xip_ctrl *ctrl) +{ + uint8_t cmd_length = dev_data->xip_params_active.cmd_length; + + switch (cmd_length) { + case 0: + ctrl->read |= FIELD_PREP(XIP_CTRL_INST_L_MASK, + XIP_CTRL_INST_L0); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_INST_L_MASK, + XIP_WRITE_CTRL_INST_L0); + break; + case 1: + ctrl->read |= XIP_CTRL_INST_EN_BIT + | FIELD_PREP(XIP_CTRL_INST_L_MASK, + XIP_CTRL_INST_L8); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_INST_L_MASK, + XIP_WRITE_CTRL_INST_L8); + break; + case 2: + ctrl->read |= XIP_CTRL_INST_EN_BIT + | FIELD_PREP(XIP_CTRL_INST_L_MASK, + XIP_CTRL_INST_L16); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_INST_L_MASK, + XIP_WRITE_CTRL_INST_L16); + break; + default: + LOG_ERR("Command length %d not supported", cmd_length); + return false; + } + + return true; +} + +static bool apply_xip_addr_length(const struct mspi_dw_data *dev_data, + struct xip_ctrl *ctrl) +{ + uint8_t addr_length = dev_data->xip_params_active.addr_length; + + ctrl->read |= FIELD_PREP(XIP_CTRL_ADDR_L_MASK, addr_length * 2); + ctrl->write |= FIELD_PREP(XIP_WRITE_CTRL_ADDR_L_MASK, addr_length * 2); + + return true; +} +#endif /* defined(CONFIG_MSPI_XIP) */ + +static int _api_dev_config(const struct device *dev, + const enum mspi_dev_cfg_mask param_mask, + const struct mspi_dev_cfg *cfg) +{ + const struct mspi_dw_config *dev_config = dev->config; + struct mspi_dw_data *dev_data = dev->data; + + if (param_mask & MSPI_DEVICE_CONFIG_ENDIAN) { + if (cfg->endian != MSPI_XFER_BIG_ENDIAN) { + LOG_ERR("Only big endian transfers are supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_CE_POL) { + if (cfg->ce_polarity != MSPI_CE_ACTIVE_LOW) { + LOG_ERR("Only active low CE is supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_MEM_BOUND) { + if (cfg->mem_boundary) { + LOG_ERR("Auto CE break is not supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_BREAK_TIME) { + if (cfg->time_to_break) { + LOG_ERR("Auto CE break is not supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_IO_MODE) { +#if defined(CONFIG_MSPI_XIP) + dev_data->xip_params_stored.io_mode = cfg->io_mode; +#endif + + if (!apply_io_mode(dev_data, cfg->io_mode)) { + return -EINVAL; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_CPP) { +#if defined(CONFIG_MSPI_XIP) + /* Make sure the new setting is compatible with the one used + * for XIP if it is enabled. + */ + if (!dev_data->xip_enabled) { + dev_data->xip_cpp = cfg->cpp; + } else if (dev_data->xip_cpp != cfg->cpp) { + LOG_ERR("Conflict with configuration used for XIP."); + return -EINVAL; + } +#endif + + dev_data->ctrlr0 &= ~(CTRLR0_SCPOL_BIT | CTRLR0_SCPH_BIT); + + switch (cfg->cpp) { + default: + case MSPI_CPP_MODE_0: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SCPOL_BIT, 0) | + FIELD_PREP(CTRLR0_SCPH_BIT, 0); + break; + case MSPI_CPP_MODE_1: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SCPOL_BIT, 0) | + FIELD_PREP(CTRLR0_SCPH_BIT, 1); + break; + case MSPI_CPP_MODE_2: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SCPOL_BIT, 1) | + FIELD_PREP(CTRLR0_SCPH_BIT, 0); + break; + case MSPI_CPP_MODE_3: + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_SCPOL_BIT, 1) | + FIELD_PREP(CTRLR0_SCPH_BIT, 1); + break; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { + if (cfg->freq > dev_config->clock_frequency / 2 || + cfg->freq < dev_config->clock_frequency / 65534) { + LOG_ERR("Invalid frequency: %u, MIN: %u, MAX: %u", + cfg->freq, dev_config->clock_frequency / 65534, + dev_config->clock_frequency / 2); + return -EINVAL; + } + +#if defined(CONFIG_MSPI_XIP) + /* Make sure the new setting is compatible with the one used + * for XIP if it is enabled. + */ + if (!dev_data->xip_enabled) { + dev_data->xip_freq = cfg->freq; + } else if (dev_data->xip_freq != cfg->freq) { + LOG_ERR("Conflict with configuration used for XIP."); + return -EINVAL; + } +#endif + + dev_data->baudr = dev_config->clock_frequency / cfg->freq; + } + + if (param_mask & MSPI_DEVICE_CONFIG_DATA_RATE) { + /* TODO: add support for DDR */ + if (cfg->data_rate != MSPI_DATA_RATE_SINGLE) { + LOG_ERR("Only single data rate is supported."); + return -ENOTSUP; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_DQS) { + /* TODO: add support for DQS */ + if (cfg->dqs_enable) { + LOG_ERR("DQS line is not supported."); + return -ENOTSUP; + } + } + +#if defined(CONFIG_MSPI_XIP) + if (param_mask & MSPI_DEVICE_CONFIG_READ_CMD) { + dev_data->xip_params_stored.read_cmd = cfg->read_cmd; + } + if (param_mask & MSPI_DEVICE_CONFIG_WRITE_CMD) { + dev_data->xip_params_stored.write_cmd = cfg->write_cmd; + } + if (param_mask & MSPI_DEVICE_CONFIG_RX_DUMMY) { + dev_data->xip_params_stored.rx_dummy = cfg->rx_dummy; + } + if (param_mask & MSPI_DEVICE_CONFIG_TX_DUMMY) { + dev_data->xip_params_stored.tx_dummy = cfg->tx_dummy; + } + if (param_mask & MSPI_DEVICE_CONFIG_CMD_LEN) { + dev_data->xip_params_stored.cmd_length = cfg->cmd_length; + } + if (param_mask & MSPI_DEVICE_CONFIG_ADDR_LEN) { + dev_data->xip_params_stored.addr_length = cfg->addr_length; + } +#endif + + /* Always use Motorola SPI frame format. */ + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_FRF_MASK, CTRLR0_FRF_SPI); + /* Enable clock stretching. */ + dev_data->spi_ctrlr0 |= SPI_CTRLR0_CLK_STRETCH_EN_BIT; + + return 0; +} + +static int api_dev_config(const struct device *dev, + const struct mspi_dev_id *dev_id, + const enum mspi_dev_cfg_mask param_mask, + const struct mspi_dev_cfg *cfg) +{ + const struct mspi_dw_config *dev_config = dev->config; + struct mspi_dw_data *dev_data = dev->data; + int rc; + + if (dev_id != dev_data->dev_id) { + rc = k_sem_take(&dev_data->cfg_lock, + K_MSEC(CONFIG_MSPI_COMPLETION_TIMEOUT_TOLERANCE)); + if (rc < 0) { + LOG_ERR("Failed to switch controller to device"); + return -EBUSY; + } + + dev_data->dev_id = dev_id; + } + + if (param_mask == MSPI_DEVICE_CONFIG_NONE && + !dev_config->sw_multi_periph) { + return 0; + } + + (void)k_sem_take(&dev_data->ctx_lock, K_FOREVER); + + rc = _api_dev_config(dev, param_mask, cfg); + + k_sem_give(&dev_data->ctx_lock); + + if (rc < 0) { + dev_data->dev_id = NULL; + k_sem_give(&dev_data->cfg_lock); + } + + return rc; +} + +static int api_get_channel_status(const struct device *dev, uint8_t ch) +{ + ARG_UNUSED(ch); + + struct mspi_dw_data *dev_data = dev->data; + + (void)k_sem_take(&dev_data->ctx_lock, K_FOREVER); + + dev_data->dev_id = NULL; + k_sem_give(&dev_data->cfg_lock); + + k_sem_give(&dev_data->ctx_lock); + + return 0; +} + +static void tx_control_field(const struct device *dev, + uint32_t field, uint8_t len) +{ + uint8_t shift = 8 * len; + + do { + shift -= 8; + write_dr(dev, field >> shift); + } while (shift); +} + +static int start_next_packet(const struct device *dev, k_timeout_t timeout) +{ + const struct mspi_dw_config *dev_config = dev->config; + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_xfer_packet *packet = + &dev_data->xfer.packets[dev_data->packets_done]; + bool xip_enabled = COND_CODE_1(CONFIG_MSPI_XIP, + (dev_data->xip_enabled != 0), + (false)); + unsigned int key; + uint8_t tx_fifo_threshold; + uint32_t packet_frames; + uint32_t imr; + int rc = 0; + + if (packet->num_bytes == 0 && + dev_data->xfer.cmd_length == 0 && + dev_data->xfer.addr_length == 0) { + return 0; + } + + dev_data->dummy_bytes = 0; + + dev_data->ctrlr0 &= ~CTRLR0_TMOD_MASK + & ~CTRLR0_DFS_MASK; + + dev_data->spi_ctrlr0 &= ~SPI_CTRLR0_WAIT_CYCLES_MASK; + + if (dev_data->standard_spi && + (dev_data->xfer.cmd_length != 0 || + dev_data->xfer.addr_length != 0)) { + dev_data->bytes_per_frame_exp = 0; + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 7); + } else { + if ((packet->num_bytes % 4) == 0) { + dev_data->bytes_per_frame_exp = 2; + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 31); + } else if ((packet->num_bytes % 2) == 0) { + dev_data->bytes_per_frame_exp = 1; + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 15); + } else { + dev_data->bytes_per_frame_exp = 0; + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_DFS_MASK, 7); + } + } + + packet_frames = packet->num_bytes >> dev_data->bytes_per_frame_exp; + + if (packet_frames > UINT16_MAX + 1) { + LOG_ERR("Packet length (%u) exceeds supported maximum", + packet->num_bytes); + return -EINVAL; + } + + if (packet->dir == MSPI_TX || packet->num_bytes == 0) { + imr = IMR_TXEIM_BIT; + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_TMOD_MASK, + CTRLR0_TMOD_TX); + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_WAIT_CYCLES_MASK, + dev_data->xfer.tx_dummy); + + write_rxftlr(dev, 0); + tx_fifo_threshold = dev_config->tx_fifo_threshold; + } else { + uint32_t tmod; + uint8_t rx_fifo_threshold; + + /* In Standard SPI Mode, the controller does not support + * sending the command and address fields separately, they + * need to be sent as data; hence, for RX packets with these + * fields, the TX/RX transfer mode needs to be used and + * consequently, dummy bytes need to be transmitted so that + * clock cycles for the RX part are provided (the controller + * does not do it automatically in the TX/RX mode). + */ + if (dev_data->standard_spi && + (dev_data->xfer.cmd_length != 0 || + dev_data->xfer.addr_length != 0)) { + uint32_t rx_total_bytes; + + dev_data->bytes_to_discard = dev_data->xfer.cmd_length + + dev_data->xfer.addr_length; + rx_total_bytes = dev_data->bytes_to_discard + + packet->num_bytes; + + dev_data->dummy_bytes = packet->num_bytes; + + imr = IMR_TXEIM_BIT | IMR_RXFIM_BIT; + tmod = CTRLR0_TMOD_TX_RX; + tx_fifo_threshold = dev_config->tx_fifo_threshold; + /* For standard SPI, only 1-byte frames are used. */ + rx_fifo_threshold = MIN(rx_total_bytes - 1, + dev_config->rx_fifo_threshold); + } else { + imr = IMR_RXFIM_BIT; + tmod = CTRLR0_TMOD_RX; + tx_fifo_threshold = 0; + rx_fifo_threshold = MIN(packet_frames - 1, + dev_config->rx_fifo_threshold); + } + + dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_TMOD_MASK, tmod); + dev_data->spi_ctrlr0 |= FIELD_PREP(SPI_CTRLR0_WAIT_CYCLES_MASK, + dev_data->xfer.rx_dummy); + + write_rxftlr(dev, FIELD_PREP(RXFTLR_RFT_MASK, + rx_fifo_threshold)); + } + + if (dev_data->dev_id->ce.port) { + rc = gpio_pin_set_dt(&dev_data->dev_id->ce, 1); + if (rc < 0) { + LOG_ERR("Failed to activate CE line (%d)", rc); + return rc; + } + } + + if (xip_enabled) { + key = irq_lock(); + write_ssienr(dev, 0); + } + + /* These registers cannot be written when the controller is enabled, + * that's why it is temporarily disabled above; with locked interrupts, + * to prevent potential XIP transfers during that period. + */ + write_ctrlr0(dev, dev_data->ctrlr0); + write_ctrlr1(dev, packet_frames > 0 + ? FIELD_PREP(CTRLR1_NDF_MASK, packet_frames - 1) + : 0); + write_spi_ctrlr0(dev, dev_data->spi_ctrlr0); + write_baudr(dev, dev_data->baudr); + write_ser(dev, BIT(dev_data->dev_id->dev_idx)); + + if (xip_enabled) { + write_ssienr(dev, SSIENR_SSIC_EN_BIT); + irq_unlock(key); + } + + dev_data->buf_pos = packet->data_buf; + dev_data->buf_end = &packet->data_buf[packet->num_bytes]; + + if ((imr & IMR_TXEIM_BIT) && dev_data->buf_pos < dev_data->buf_end) { + uint32_t start_level = tx_fifo_threshold; + + if (dev_data->dummy_bytes) { + uint32_t tx_total = dev_data->bytes_to_discard + + dev_data->dummy_bytes; + + if (start_level > tx_total - 1) { + start_level = tx_total - 1; + } + } + + write_txftlr(dev, + FIELD_PREP(TXFTLR_TXFTHR_MASK, start_level) | + FIELD_PREP(TXFTLR_TFT_MASK, tx_fifo_threshold)); + } else { + write_txftlr(dev, 0); + } + + /* Ensure that there will be no interrupt from the controller yet. */ + write_imr(dev, 0); + /* Enable the controller. This must be done before DR is written. */ + write_ssienr(dev, SSIENR_SSIC_EN_BIT); + + if (dev_data->standard_spi) { + if (dev_data->xfer.cmd_length) { + tx_control_field(dev, packet->cmd, + dev_data->xfer.cmd_length); + } + + if (dev_data->xfer.addr_length) { + tx_control_field(dev, packet->address, + dev_data->xfer.addr_length); + } + } else { + if (dev_data->xfer.cmd_length) { + write_dr(dev, packet->cmd); + } + + if (dev_data->xfer.addr_length) { + write_dr(dev, packet->address); + } + } + + if (dev_data->dummy_bytes) { + if (make_rx_cycles(dev)) { + imr = IMR_RXFIM_BIT; + } + } else if (packet->dir == MSPI_TX && packet->num_bytes) { + tx_data(dev, packet); + } + + /* Enable interrupts now and wait until the packet is done. */ + write_imr(dev, imr); + + rc = k_sem_take(&dev_data->finished, timeout); + if (rc < 0) { + rc = -ETIMEDOUT; + } + + /* Disable the controller. This will immediately halt the transfer + * if it hasn't finished yet. + */ + if (xip_enabled) { + /* If XIP is enabled, the controller must be kept enabled, + * so disable it only momentarily if there's a need to halt + * a transfer that has timeout out. + */ + if (rc == -ETIMEDOUT) { + key = irq_lock(); + + write_ssienr(dev, 0); + write_ssienr(dev, SSIENR_SSIC_EN_BIT); + + irq_unlock(key); + } + } else { + write_ssienr(dev, 0); + } + + if (dev_data->dev_id->ce.port) { + int rc2; + + /* Do not use `rc` to not overwrite potential timeout error. */ + rc2 = gpio_pin_set_dt(&dev_data->dev_id->ce, 0); + if (rc2 < 0) { + LOG_ERR("Failed to deactivate CE line (%d)", rc2); + return rc2; + } + } + + return rc; +} + +static int _api_transceive(const struct device *dev, + const struct mspi_xfer *req) +{ + struct mspi_dw_data *dev_data = dev->data; + int rc; + + dev_data->spi_ctrlr0 &= ~SPI_CTRLR0_WAIT_CYCLES_MASK + & ~SPI_CTRLR0_INST_L_MASK + & ~SPI_CTRLR0_ADDR_L_MASK; + + if (!apply_cmd_length(dev_data, req->cmd_length) || + !apply_addr_length(dev_data, req->addr_length)) { + return -EINVAL; + } + + if (dev_data->standard_spi && + (req->rx_dummy != 0 || req->tx_dummy != 0)) { + LOG_ERR("Dummy cycles unsupported in single line mode"); + return -EINVAL; + } else if (req->rx_dummy > SPI_CTRLR0_WAIT_CYCLES_MAX || + req->tx_dummy > SPI_CTRLR0_WAIT_CYCLES_MAX) { + LOG_ERR("Unsupported RX (%u) or TX (%u) dummy cycles", + req->rx_dummy, req->tx_dummy); + return -EINVAL; + } + + dev_data->xfer = *req; + + for (dev_data->packets_done = 0; + dev_data->packets_done < dev_data->xfer.num_packet; + dev_data->packets_done++) { + rc = start_next_packet(dev, K_MSEC(dev_data->xfer.timeout)); + if (rc < 0) { + return rc; + } + } + + return 0; +} + +static int api_transceive(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xfer *req) +{ + struct mspi_dw_data *dev_data = dev->data; + int rc, rc2; + + if (dev_id != dev_data->dev_id) { + LOG_ERR("Controller is not configured for this device"); + return -EINVAL; + } + + /* TODO: add support for asynchronous transfers */ + if (req->async) { + LOG_ERR("Asynchronous transfers are not supported"); + return -ENOTSUP; + } + + rc = pm_device_runtime_get(dev); + if (rc < 0) { + LOG_ERR("pm_device_runtime_get() failed: %d", rc); + return rc; + } + + (void)k_sem_take(&dev_data->ctx_lock, K_FOREVER); + + if (dev_data->suspended) { + rc = -EFAULT; + } else { + rc = _api_transceive(dev, req); + } + + k_sem_give(&dev_data->ctx_lock); + + rc2 = pm_device_runtime_put(dev); + if (rc2 < 0) { + LOG_ERR("pm_device_runtime_put() failed: %d", rc2); + rc = (rc < 0 ? rc : rc2); + } + + return rc; +} + +#if defined(CONFIG_MSPI_XIP) +static int _api_xip_config(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + struct mspi_dw_data *dev_data = dev->data; + int rc; + + if (!cfg->enable) { + rc = vendor_specific_xip_disable(dev, dev_id, cfg); + if (rc < 0) { + return rc; + } + + dev_data->xip_enabled &= ~BIT(dev_id->dev_idx); + + if (!dev_data->xip_enabled) { + write_ssienr(dev, 0); + + /* Since XIP is disabled, it is okay for the controller + * to be suspended. + */ + rc = pm_device_runtime_put(dev); + if (rc < 0) { + LOG_ERR("pm_device_runtime_put() failed: %d", rc); + return rc; + } + } + + return 0; + } + + if (!dev_data->xip_enabled) { + struct xip_params *params = &dev_data->xip_params_active; + struct xip_ctrl ctrl = {0}; + + *params = dev_data->xip_params_stored; + + if (!apply_xip_io_mode(dev_data, &ctrl) || + !apply_xip_cmd_length(dev_data, &ctrl) || + !apply_xip_addr_length(dev_data, &ctrl)) { + return -EINVAL; + } + + if (params->rx_dummy > SPI_CTRLR0_WAIT_CYCLES_MAX || + params->tx_dummy > SPI_CTRLR0_WAIT_CYCLES_MAX) { + LOG_ERR("Unsupported RX (%u) or TX (%u) dummy cycles", + params->rx_dummy, params->tx_dummy); + return -EINVAL; + } + + /* Increase usage count additionally to prevent the controller + * from being suspended as long as XIP is active. + */ + rc = pm_device_runtime_get(dev); + if (rc < 0) { + LOG_ERR("pm_device_runtime_get() failed: %d", rc); + return rc; + } + + ctrl.read |= FIELD_PREP(XIP_CTRL_WAIT_CYCLES_MASK, + params->rx_dummy); + ctrl.write |= FIELD_PREP(XIP_WRITE_CTRL_WAIT_CYCLES_MASK, + params->tx_dummy); + + /* Make sure the baud rate and serial clock phase/polarity + * registers are configured properly. They may not be if + * non-XIP transfers have not been performed yet. + */ + write_ctrlr0(dev, dev_data->ctrlr0); + write_baudr(dev, dev_data->baudr); + + write_xip_incr_inst(dev, params->read_cmd); + write_xip_wrap_inst(dev, params->read_cmd); + write_xip_ctrl(dev, ctrl.read); + write_xip_write_incr_inst(dev, params->write_cmd); + write_xip_write_wrap_inst(dev, params->write_cmd); + write_xip_write_ctrl(dev, ctrl.write); + } else if (dev_data->xip_params_active.read_cmd != + dev_data->xip_params_stored.read_cmd || + dev_data->xip_params_active.write_cmd != + dev_data->xip_params_stored.write_cmd || + dev_data->xip_params_active.cmd_length != + dev_data->xip_params_stored.cmd_length || + dev_data->xip_params_active.addr_length != + dev_data->xip_params_stored.addr_length || + dev_data->xip_params_active.rx_dummy != + dev_data->xip_params_stored.rx_dummy || + dev_data->xip_params_active.tx_dummy != + dev_data->xip_params_stored.tx_dummy) { + LOG_ERR("Conflict with configuration already used for XIP."); + return -EINVAL; + } + + rc = vendor_specific_xip_enable(dev, dev_id, cfg); + if (rc < 0) { + return rc; + } + + write_ssienr(dev, SSIENR_SSIC_EN_BIT); + + dev_data->xip_enabled |= BIT(dev_id->dev_idx); + + return 0; +} + +static int api_xip_config(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + struct mspi_dw_data *dev_data = dev->data; + int rc, rc2; + + if (cfg->enable && dev_id != dev_data->dev_id) { + LOG_ERR("Controller is not configured for this device"); + return -EINVAL; + } + + rc = pm_device_runtime_get(dev); + if (rc < 0) { + LOG_ERR("pm_device_runtime_get() failed: %d", rc); + return rc; + } + + (void)k_sem_take(&dev_data->ctx_lock, K_FOREVER); + + if (dev_data->suspended) { + rc = -EFAULT; + } else { + rc = _api_xip_config(dev, dev_id, cfg); + } + + k_sem_give(&dev_data->ctx_lock); + + rc2 = pm_device_runtime_put(dev); + if (rc2 < 0) { + LOG_ERR("pm_device_runtime_put() failed: %d", rc2); + rc = (rc < 0 ? rc : rc2); + } + + return rc; +} +#endif /* defined(CONFIG_MSPI_XIP) */ + +static int dev_pm_action_cb(const struct device *dev, + enum pm_device_action action) +{ + struct mspi_dw_data *dev_data = dev->data; + + if (action == PM_DEVICE_ACTION_RESUME) { +#if defined(CONFIG_PINCTRL) + const struct mspi_dw_config *dev_config = dev->config; + int rc = pinctrl_apply_state(dev_config->pcfg, + PINCTRL_STATE_DEFAULT); + + if (rc < 0) { + LOG_ERR("Cannot apply default pins state (%d)", rc); + return rc; + } +#endif + vendor_specific_resume(dev); + + dev_data->suspended = false; + + return 0; + } + + if (IS_ENABLED(CONFIG_PM_DEVICE) && + action == PM_DEVICE_ACTION_SUSPEND) { + bool xip_enabled = COND_CODE_1(CONFIG_MSPI_XIP, + (dev_data->xip_enabled != 0), + (false)); + +#if defined(CONFIG_PINCTRL) + const struct mspi_dw_config *dev_config = dev->config; + int rc = pinctrl_apply_state(dev_config->pcfg, + PINCTRL_STATE_SLEEP); + + if (rc < 0) { + LOG_ERR("Cannot apply sleep pins state (%d)", rc); + return rc; + } +#endif + if (xip_enabled || + k_sem_take(&dev_data->ctx_lock, K_NO_WAIT) != 0) { + LOG_ERR("Controller in use, cannot be suspended"); + return -EBUSY; + } + + dev_data->suspended = true; + + vendor_specific_suspend(dev); + + k_sem_give(&dev_data->ctx_lock); + + return 0; + } + + return -ENOTSUP; +} + +static int dev_init(const struct device *dev) +{ + struct mspi_dw_data *dev_data = dev->data; + const struct mspi_dw_config *dev_config = dev->config; + const struct gpio_dt_spec *ce_gpio; + int rc; + + DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE); + + vendor_specific_init(dev); + + dev_config->irq_config(); + + k_sem_init(&dev_data->finished, 0, 1); + k_sem_init(&dev_data->cfg_lock, 1, 1); + k_sem_init(&dev_data->ctx_lock, 1, 1); + + for (ce_gpio = dev_config->ce_gpios; + ce_gpio < &dev_config->ce_gpios[dev_config->ce_gpios_len]; + ce_gpio++) { + if (!device_is_ready(ce_gpio->port)) { + LOG_ERR("CE GPIO port %s is not ready", + ce_gpio->port->name); + return -ENODEV; + } + + rc = gpio_pin_configure_dt(ce_gpio, GPIO_OUTPUT_INACTIVE); + if (rc < 0) { + return rc; + } + } + +#if defined(CONFIG_PINCTRL) + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + rc = pinctrl_apply_state(dev_config->pcfg, PINCTRL_STATE_SLEEP); + if (rc < 0) { + LOG_ERR("Cannot apply sleep pins state (%d)", rc); + return rc; + } + } +#endif + + return pm_device_driver_init(dev, dev_pm_action_cb); +} + +static DEVICE_API(mspi, drv_api) = { + .config = api_config, + .dev_config = api_dev_config, + .get_channel_status = api_get_channel_status, + .transceive = api_transceive, +#if defined(CONFIG_MSPI_XIP) + .xip_config = api_xip_config, +#endif +}; + +#define MSPI_DW_INST_IRQ(idx, inst) \ + IRQ_CONNECT(DT_INST_IRQ_BY_IDX(inst, idx, irq), \ + DT_INST_IRQ_BY_IDX(inst, idx, priority), \ + mspi_dw_isr, DEVICE_DT_INST_GET(inst), 0); \ + irq_enable(DT_INST_IRQ_BY_IDX(inst, idx, irq)) + +#define MSPI_DW_MMIO_ROM_INIT(node_id) \ + COND_CODE_1(DT_REG_HAS_NAME(node_id, core), \ + (Z_DEVICE_MMIO_NAMED_ROM_INITIALIZER(core, node_id)), \ + (DEVICE_MMIO_ROM_INIT(node_id))) + +#define MSPI_DW_CLOCK_FREQUENCY(inst) \ + COND_CODE_1(DT_NODE_HAS_PROP(DT_INST_PHANDLE(inst, clocks), \ + clock_frequency), \ + (DT_INST_PROP_BY_PHANDLE(inst, clocks, \ + clock_frequency)), \ + (DT_INST_PROP(inst, clock_frequency))) + +#define MSPI_DW_DT_INST_PROP(inst, prop) .prop = DT_INST_PROP(inst, prop) + +#define FOREACH_CE_GPIOS_ELEM(inst) \ + DT_INST_FOREACH_PROP_ELEM_SEP(inst, ce_gpios, \ + GPIO_DT_SPEC_GET_BY_IDX, (,)) +#define MSPI_DW_CE_GPIOS(inst) \ + .ce_gpios = (const struct gpio_dt_spec []) \ + { FOREACH_CE_GPIOS_ELEM(inst) }, \ + .ce_gpios_len = DT_INST_PROP_LEN(inst, ce_gpios) + +#define TX_FIFO_DEPTH(inst) DT_INST_PROP(inst, fifo_depth) +#define RX_FIFO_DEPTH(inst) DT_INST_PROP_OR(inst, rx_fifo_depth, \ + TX_FIFO_DEPTH(inst)) +#define MSPI_DW_FIFO_PROPS(inst) \ + .tx_fifo_depth_minus_1 = TX_FIFO_DEPTH(inst) - 1, \ + .tx_fifo_threshold = \ + DT_INST_PROP_OR(inst, tx_fifo_threshold, \ + 7 * TX_FIFO_DEPTH(inst) / 8 - 1), \ + .rx_fifo_threshold = \ + DT_INST_PROP_OR(inst, rx_fifo_threshold, \ + 1 * RX_FIFO_DEPTH(inst) / 8 - 1) + +#define MSPI_DW_INST(inst) \ + PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \ + IF_ENABLED(CONFIG_PINCTRL, (PINCTRL_DT_INST_DEFINE(inst);)) \ + static void irq_config##inst(void) \ + { \ + LISTIFY(DT_INST_NUM_IRQS(inst), \ + MSPI_DW_INST_IRQ, (;), inst); \ + } \ + static struct mspi_dw_data dev##inst##_data; \ + static const struct mspi_dw_config dev##inst##_config = { \ + MSPI_DW_MMIO_ROM_INIT(DT_DRV_INST(inst)), \ + .irq_config = irq_config##inst, \ + .clock_frequency = MSPI_DW_CLOCK_FREQUENCY(inst), \ + IF_ENABLED(CONFIG_PINCTRL, \ + (.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),)) \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, ce_gpios), \ + (MSPI_DW_CE_GPIOS(inst),)) \ + MSPI_DW_FIFO_PROPS(inst), \ + DEFINE_REG_ACCESS(inst) \ + .sw_multi_periph = \ + DT_INST_PROP(inst, software_multiperipheral), \ + }; \ + DEVICE_DT_INST_DEFINE(inst, \ + dev_init, PM_DEVICE_DT_INST_GET(inst), \ + &dev##inst##_data, &dev##inst##_config, \ + POST_KERNEL, CONFIG_MSPI_INIT_PRIORITY, \ + &drv_api); + +DT_INST_FOREACH_STATUS_OKAY(MSPI_DW_INST) diff --git a/drivers/mspi/mspi_dw.h b/drivers/mspi/mspi_dw.h new file mode 100644 index 0000000000000..bd81d061e09ac --- /dev/null +++ b/drivers/mspi/mspi_dw.h @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This header is part of mspi_dw.c extracted only for clarity. + * It is not supposed to be included by any file other than mspi_dw.c. + */ + +/* CTRLR0 - Control Register 0 */ +#define CTRLR0_SPI_FRF_MASK GENMASK(23, 22) +#define CTRLR0_SPI_FRF_STANDARD 0UL +#define CTRLR0_SPI_FRF_DUAL 1UL +#define CTRLR0_SPI_FRF_QUAD 2UL +#define CTRLR0_SPI_FRF_OCTAL 3UL +#define CTRLR0_TMOD_MASK GENMASK(11, 10) +#define CTRLR0_TMOD_TX_RX 0UL +#define CTRLR0_TMOD_TX 1UL +#define CTRLR0_TMOD_RX 2UL +#define CTRLR0_TMOD_EEPROM 3UL +#define CTRLR0_SCPOL_BIT BIT(9) +#define CTRLR0_SCPH_BIT BIT(8) +#define CTRLR0_FRF_MASK GENMASK(7, 6) +#define CTRLR0_FRF_SPI 0UL +#define CTRLR0_FRF_SSP 1UL +#define CTRLR0_FRF_MICROWIRE 2UL +#define CTRLR0_DFS_MASK GENMASK(4, 0) + +/* CTRLR1- Control Register 1 */ +#define CTRLR1_NDF_MASK GENMASK(15, 0) + +/* SSIENR - SSI Enable Register */ +#define SSIENR_SSIC_EN_BIT BIT(0) + +/* TXFTLR - Transmit FIFO Threshold Level */ +#define TXFTLR_TXFTHR_MASK GENMASK(23, 16) +#define TXFTLR_TFT_MASK GENMASK(7, 0) + +/* RXFTLR - Receive FIFO Threshold Level */ +#define RXFTLR_RFT_MASK GENMASK(7, 0) + +/* TXFLR - Transmit FIFO Level Register */ +#define TXFLR_TXTFL_MASK GENMASK(7, 0) + +/* RXFLR - Receive FIFO Level Register */ +#define RXFLR_RXTFL_MASK GENMASK(7, 0) + +/* SR - Status Register */ +#define SR_BUSY_BIT BIT(0) + +/* IMR - Interrupt Mask Register */ +#define IMR_TXEIM_BIT BIT(0) +#define IMR_TXOIM_BIT BIT(1) +#define IMR_RXUIM_BIT BIT(2) +#define IMR_RXOIM_BIT BIT(3) +#define IMR_RXFIM_BIT BIT(4) +#define IMR_MSTIM_BIT BIT(5) + +/* ISR - Interrupt Status Register */ +#define ISR_TXEIS_BIT BIT(0) +#define ISR_TXOIS_BIT BIT(1) +#define ISR_RXUIS_BIT BIT(2) +#define ISR_RXOIS_BIT BIT(3) +#define ISR_RXFIS_BIT BIT(4) +#define ISR_MSTIS_BIT BIT(5) + +/* SPI_CTRLR0 - SPI Control Register */ +#define SPI_CTRLR0_CLK_STRETCH_EN_BIT BIT(30) +#define SPI_CTRLR0_XIP_PREFETCH_EN_BIT BIT(29) +#define SPI_CTRLR0_XIP_MBL_BIT BIT(26) +#define SPI_CTRLR0_SPI_RXDS_SIG_EN_BIT BIT(25) +#define SPI_CTRLR0_SPI_DM_EN_BIT BIT(24) +#define SPI_CTRLR0_RXDS_VL_EN_BIT BIT(23) +#define SPI_CTRLR0_SSIC_XIP_CONT_XFER_EN_BIT BIT(21) +#define SPI_CTRLR0_XIP_INST_EN_BIT BIT(20) +#define SPI_CTRLR0_XIP_DFS_HC_BIT BIT(19) +#define SPI_CTRLR0_SPI_RXDS_EN_BIT BIT(18) +#define SPI_CTRLR0_INST_DDR_EN_BIT BIT(17) +#define SPI_CTRLR0_SPI_DDR_EN_BIT BIT(16) +#define SPI_CTRLR0_WAIT_CYCLES_MASK GENMASK(15, 11) +#define SPI_CTRLR0_WAIT_CYCLES_MAX BIT_MASK(5) +#define SPI_CTRLR0_INST_L_MASK GENMASK(9, 8) +#define SPI_CTRLR0_INST_L0 0UL +#define SPI_CTRLR0_INST_L4 1UL +#define SPI_CTRLR0_INST_L8 2UL +#define SPI_CTRLR0_INST_L16 3UL +#define SPI_CTRLR0_XIP_MD_BIT_EN_BIT BIT(7) +#define SPI_CTRLR0_ADDR_L_MASK GENMASK(5, 2) +#define SPI_CTRLR0_TRANS_TYPE_MASK GENMASK(1, 0) +#define SPI_CTRLR0_TRANS_TYPE_TT0 0UL +#define SPI_CTRLR0_TRANS_TYPE_TT1 1UL +#define SPI_CTRLR0_TRANS_TYPE_TT2 2UL +#define SPI_CTRLR0_TRANS_TYPE_TT3 3UL + +/* XIP_CTRL - XIP Control Register */ +#define XIP_CTRL_XIP_PREFETCH_EN_BIT BIT(28) +#define XIP_CTRL_XIP_MBL_MASK GENMASK(27, 26) +#define XIP_CTRL_XIP_MBL_2 0UL +#define XIP_CTRL_XIP_MBL_4 1UL +#define XIP_CTRL_XIP_MBL_8 2UL +#define XIP_CTRL_XIP_MBL_16 3UL +#define XIP_CTRL_RXDS_SIG_EN_BIT BIT(25) +#define XIP_CTRL_XIP_HYBERBUS_EN_BIT BIT(24) +#define XIP_CTRL_CONT_XFER_EN_BIT BIT(23) +#define XIP_CTRL_INST_EN_BIT BIT(22) +#define XIP_CTRL_RXDS_EN_BIT BIT(21) +#define XIP_CTRL_INST_DDR_EN_BIT BIT(20) +#define XIP_CTRL_DDR_EN_BIT BIT(19) +#define XIP_CTRL_DFS_HC_BIT BIT(18) +#define XIP_CTRL_WAIT_CYCLES_MASK GENMASK(17, 13) +#define XIP_CTRL_WAIT_CYCLES_MAX BIT_MASK(5) +#define XIP_CTRL_MD_BITS_EN_BIT BIT(12) +#define XIP_CTRL_INST_L_MASK GENMASK(10, 9) +#define XIP_CTRL_INST_L0 0UL +#define XIP_CTRL_INST_L4 1UL +#define XIP_CTRL_INST_L8 2UL +#define XIP_CTRL_INST_L16 3UL +#define XIP_CTRL_ADDR_L_MASK GENMASK(7, 4) +#define XIP_CTRL_TRANS_TYPE_MASK GENMASK(3, 2) +#define XIP_CTRL_TRANS_TYPE_TT0 0UL +#define XIP_CTRL_TRANS_TYPE_TT1 1UL +#define XIP_CTRL_TRANS_TYPE_TT2 2UL +#define XIP_CTRL_FRF_MASK GENMASK(1, 0) +#define XIP_CTRL_FRF_DUAL 1UL +#define XIP_CTRL_FRF_QUAD 2UL +#define XIP_CTRL_FRF_OCTAL 3UL + +/* XIP_CTRL - XIP Control Register */ +#define XIP_CTRL_XIP_PREFETCH_EN_BIT BIT(28) +#define XIP_CTRL_XIP_MBL_MASK GENMASK(27, 26) +#define XIP_CTRL_XIP_MBL_2 0UL +#define XIP_CTRL_XIP_MBL_4 1UL +#define XIP_CTRL_XIP_MBL_8 2UL +#define XIP_CTRL_XIP_MBL_16 3UL +#define XIP_CTRL_XIP_HYBERBUS_EN_BIT BIT(24) +#define XIP_CTRL_CONT_XFER_EN_BIT BIT(23) +#define XIP_CTRL_INST_EN_BIT BIT(22) +#define XIP_CTRL_RXDS_EN_BIT BIT(21) +#define XIP_CTRL_INST_DDR_EN_BIT BIT(20) +#define XIP_CTRL_DDR_EN_BIT BIT(19) +#define XIP_CTRL_DFS_HC_BIT BIT(18) + +/* XIP_WRITE_CTRL - XIP Write Control Register */ +#define XIP_WRITE_CTRL_WAIT_CYCLES_MASK GENMASK(20, 16) +#define XIP_WRITE_CTRL_WAIT_CYCLES_MAX BIT_MASK(5) +#define XIP_WRITE_CTRL_RXDS_SIG_EN_BIT BIT(13) +#define XIP_WRITE_CTRL_HYBERBUS_EN_BIT BIT(12) +#define XIP_WRITE_CTRL_INST_DDR_EN_BIT BIT(11) +#define XIP_WRITE_CTRL_SPI_DDR_EN_BIT BIT(10) +#define XIP_WRITE_CTRL_INST_L_MASK GENMASK(9, 8) +#define XIP_WRITE_CTRL_INST_L0 0UL +#define XIP_WRITE_CTRL_INST_L4 1UL +#define XIP_WRITE_CTRL_INST_L8 2UL +#define XIP_WRITE_CTRL_INST_L16 3UL +#define XIP_WRITE_CTRL_ADDR_L_MASK GENMASK(7, 4) +#define XIP_WRITE_CTRL_TRANS_TYPE_MASK GENMASK(3, 2) +#define XIP_WRITE_CTRL_TRANS_TYPE_TT0 0UL +#define XIP_WRITE_CTRL_TRANS_TYPE_TT1 1UL +#define XIP_WRITE_CTRL_TRANS_TYPE_TT2 2UL +#define XIP_WRITE_CTRL_FRF_MASK GENMASK(1, 0) +#define XIP_WRITE_CTRL_FRF_DUAL 1UL +#define XIP_WRITE_CTRL_FRF_QUAD 2UL +#define XIP_WRITE_CTRL_FRF_OCTAL 3UL + +/* Register access helpers. */ +#define USES_AUX_REG(inst) + DT_INST_PROP(inst, aux_reg_enable) +#define AUX_REG_INSTANCES (0 DT_INST_FOREACH_STATUS_OKAY(USES_AUX_REG)) +#define BASE_ADDR(dev) (mm_reg_t)DEVICE_MMIO_GET(dev) + +#if AUX_REG_INSTANCES != 0 +static uint32_t aux_reg_read(const struct device *dev, uint32_t off) +{ + return sys_in32(BASE_ADDR(dev) + off/4); +} +static void aux_reg_write(uint32_t data, const struct device *dev, uint32_t off) +{ + sys_out32(data, BASE_ADDR(dev) + off/4); +} +#endif + +#if AUX_REG_INSTANCES != DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) +static uint32_t reg_read(const struct device *dev, uint32_t off) +{ + return sys_read32(BASE_ADDR(dev) + off); +} +static void reg_write(uint32_t data, const struct device *dev, uint32_t off) +{ + sys_write32(data, BASE_ADDR(dev) + off); +} +#endif + +#if AUX_REG_INSTANCES == 0 +/* If no instance uses aux-reg access. */ +#define DECLARE_REG_ACCESS() +#define DEFINE_REG_ACCESS(inst) +#define DEFINE_MM_REG_RD(reg, off) \ + static inline uint32_t read_##reg(const struct device *dev) \ + { return reg_read(dev, off); } +#define DEFINE_MM_REG_WR(reg, off) \ + static inline void write_##reg(const struct device *dev, uint32_t data) \ + { reg_write(data, dev, off); } + +#elif AUX_REG_INSTANCES == DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) +/* If all instances use aux-reg access. */ +#define DECLARE_REG_ACCESS() +#define DEFINE_REG_ACCESS(inst) +#define DEFINE_MM_REG_RD(reg, off) \ + static inline uint32_t read_##reg(const struct device *dev) \ + { return aux_reg_read(dev, off); } +#define DEFINE_MM_REG_WR(reg, off) \ + static inline void write_##reg(const struct device *dev, uint32_t data) \ + { aux_reg_write(data, dev, off); } + +#else +/* If register access varies by instance. */ +#define DECLARE_REG_ACCESS() \ + uint32_t (*read)(const struct device *dev, uint32_t off); \ + void (*write)(uint32_t data, const struct device *dev, uint32_t off) +#define DEFINE_REG_ACCESS(inst) \ + COND_CODE_1(DT_INST_PROP(inst, aux_reg_enable), \ + (.read = aux_reg_read, \ + .write = aux_reg_write,), \ + (.read = reg_read, \ + .write = reg_write,)) +#define DEFINE_MM_REG_RD(reg, off) \ + static inline uint32_t read_##reg(const struct device *dev) \ + { \ + const struct mspi_dw_config *dev_config = dev->config; \ + return dev_config->read(dev, off); \ + } +#define DEFINE_MM_REG_WR(reg, off) \ + static inline void write_##reg(const struct device *dev, uint32_t data) \ + { \ + const struct mspi_dw_config *dev_config = dev->config; \ + dev_config->write(data, dev, off); \ + } +#endif diff --git a/drivers/mspi/mspi_dw_vendor_specific.h b/drivers/mspi/mspi_dw_vendor_specific.h new file mode 100644 index 0000000000000..4913e536c92d9 --- /dev/null +++ b/drivers/mspi/mspi_dw_vendor_specific.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* + * This header is part of mspi_dw.c extracted only for clarity. + * It is not supposed to be included by any file other than mspi_dw.c. + */ + +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_exmif) + +#include + +static inline void vendor_specific_init(const struct device *dev) +{ + ARG_UNUSED(dev); + + NRF_EXMIF->EVENTS_CORE = 0; + NRF_EXMIF->INTENSET = BIT(EXMIF_INTENSET_CORE_Pos); +} + +static inline void vendor_specific_suspend(const struct device *dev) +{ + ARG_UNUSED(dev); + + NRF_EXMIF->TASKS_STOP = 1; +} + +static inline void vendor_specific_resume(const struct device *dev) +{ + ARG_UNUSED(dev); + + NRF_EXMIF->TASKS_START = 1; +} + +static inline void vendor_specific_irq_clear(const struct device *dev) +{ + ARG_UNUSED(dev); + + NRF_EXMIF->EVENTS_CORE = 0; +} + +#if defined(CONFIG_MSPI_XIP) +static inline int vendor_specific_xip_enable(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + ARG_UNUSED(dev); + + if (dev_id->dev_idx == 0) { + NRF_EXMIF->EXTCONF1.OFFSET = cfg->address_offset; + NRF_EXMIF->EXTCONF1.SIZE = cfg->address_offset + + cfg->size - 1; + NRF_EXMIF->EXTCONF1.ENABLE = 1; + } else if (dev_id->dev_idx == 1) { + NRF_EXMIF->EXTCONF2.OFFSET = cfg->address_offset; + NRF_EXMIF->EXTCONF2.SIZE = cfg->address_offset + + cfg->size - 1; + NRF_EXMIF->EXTCONF2.ENABLE = 1; + } else { + return -EINVAL; + } + + return 0; +} + +static inline int vendor_specific_xip_disable(const struct device *dev, + const struct mspi_dev_id *dev_id, + const struct mspi_xip_cfg *cfg) +{ + ARG_UNUSED(dev); + ARG_UNUSED(cfg); + + if (dev_id->dev_idx == 0) { + NRF_EXMIF->EXTCONF1.ENABLE = 0; + } else if (dev_id->dev_idx == 1) { + NRF_EXMIF->EXTCONF2.ENABLE = 0; + } else { + return -EINVAL; + } + + return 0; +} +#endif /* defined(CONFIG_MSPI_XIP) */ + +#endif /* DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_exmif) */ diff --git a/dts/bindings/mspi/snps,designware-ssi.yaml b/dts/bindings/mspi/snps,designware-ssi.yaml new file mode 100644 index 0000000000000..fb516cb783588 --- /dev/null +++ b/dts/bindings/mspi/snps,designware-ssi.yaml @@ -0,0 +1,45 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Synopsys DesignWare Synchronous Serial Interface (SSI) node + +compatible: "snps,designware-ssi" + +include: [mspi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + interrupts: + required: true + + aux-reg-enable: + type: boolean + description: | + Activates auxiliary register access that is needed on some platforms. + + fifo-depth: + required: true + type: int + description: | + Number of items that can be stored in the TX FIFO. Range: 8-256. + If the RX FIFO depth is not specified separately in the rx-fifo-depth + property, this value specifies depth of both TX and RX FIFOs. + + rx-fifo-depth: + type: int + description: | + Number of items that can be stored in the RX FIFO. Range: 8-256. + + tx-fifo-threshold: + type: int + description: | + Number of entries in the TX FIFO above which the TX transfer is started. + Maximum value is the TX FIFO depth - 1. + + rx-fifo-threshold: + type: int + description: | + Number of entries in the RX FIFO above which the controller gets an RX + interrupt. Maximum value is the RX FIFO depth - 1. From 32cb0b0f85e42b40a7c6e649735cd6b73bfdbf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Thu, 17 Oct 2024 14:23:17 +0200 Subject: [PATCH 2/7] drivers: flash: Add generic NOR flash driver for MSPI devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a flash driver intended to handle various flash devices connected over MSPI bus as long as they support JEDEC SFDP. This is an initial commit providing only basic operations in Octal I/O mode with some hard-coded values for Macronix MX25Ux series chips. Signed-off-by: Andrzej Głąbek --- drivers/flash/CMakeLists.txt | 1 + drivers/flash/Kconfig.mspi | 23 + drivers/flash/flash_mspi_nor.c | 686 +++++++++++++++++++++++++++ dts/bindings/mtd/jedec,mspi-nor.yaml | 24 + 4 files changed, 734 insertions(+) create mode 100644 drivers/flash/flash_mspi_nor.c create mode 100644 dts/bindings/mtd/jedec,mspi-nor.yaml diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index 474205e397350..925a4cf8f4242 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -34,6 +34,7 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_MX25UM51345G flash_mcux_f zephyr_library_sources_ifdef(CONFIG_FLASH_MCUX_FLEXSPI_NOR flash_mcux_flexspi_nor.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_NOR flash_mspi_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_NOR flash_npcx_fiu_nor.c) zephyr_library_sources_ifdef(CONFIG_FLASH_NPCX_FIU_QSPI flash_npcx_fiu_qspi.c) zephyr_library_sources_ifdef(CONFIG_FLASH_RPI_PICO flash_rpi_pico.c) diff --git a/drivers/flash/Kconfig.mspi b/drivers/flash/Kconfig.mspi index 158fcefb528f5..d699aa4930937 100644 --- a/drivers/flash/Kconfig.mspi +++ b/drivers/flash/Kconfig.mspi @@ -28,4 +28,27 @@ config FLASH_MSPI_ATXP032 select FLASH_JESD216 select MSPI_AMBIQ_AP3 if SOC_SERIES_APOLLO3X +menuconfig FLASH_MSPI_NOR + bool "Generic MSPI NOR Flash" + default y + depends on DT_HAS_JEDEC_MSPI_NOR_ENABLED + select FLASH_MSPI + select FLASH_HAS_EXPLICIT_ERASE + select FLASH_JESD216 + select GPIO if $(dt_compat_any_has_prop,$(DT_COMPAT_JEDEC_MSPI_NOR),reset-gpios) + +if FLASH_MSPI_NOR + +config FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE + int "Page size to use for FLASH_LAYOUT feature" + depends on FLASH_PAGE_LAYOUT + default 65536 + help + When CONFIG_FLASH_PAGE_LAYOUT is used, this driver will support that + API. By default the page size corresponds to the block size (65536). + Other options include the 32K-byte erase size (32768), the sector + size (4096), or any non-zero multiple of the sector size. + +endif # FLASH_MSPI_NOR + endmenu diff --git a/drivers/flash/flash_mspi_nor.c b/drivers/flash/flash_mspi_nor.c new file mode 100644 index 0000000000000..a5ced1e00703b --- /dev/null +++ b/drivers/flash/flash_mspi_nor.c @@ -0,0 +1,686 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT jedec_mspi_nor + +#include +#include +#include +#include +#include +#include + +#include "jesd216.h" +#include "spi_nor.h" + +LOG_MODULE_REGISTER(flash_mspi_nor, CONFIG_FLASH_LOG_LEVEL); + +#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(reset_gpios) +#define WITH_RESET_GPIO 1 +#endif + +struct flash_mspi_nor_data { + struct k_sem acquired; + struct mspi_xfer_packet packet; + struct mspi_xfer xfer; +}; + +struct flash_mspi_nor_config { + const struct device *bus; + uint32_t flash_size; + struct mspi_dev_id mspi_id; + struct mspi_dev_cfg mspi_cfg; + enum mspi_dev_cfg_mask mspi_cfg_mask; +#if defined(CONFIG_MSPI_XIP) + struct mspi_xip_cfg xip_cfg; +#endif +#if defined(WITH_RESET_GPIO) + struct gpio_dt_spec reset; + uint32_t reset_pulse_us; + uint32_t reset_recovery_us; +#endif +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + struct flash_pages_layout layout; +#endif + uint8_t jedec_id[SPI_NOR_MAX_ID_LEN]; +}; + +static int acquire(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + k_sem_take(&dev_data->acquired, K_FOREVER); + + rc = pm_device_runtime_get(dev_config->bus); + if (rc < 0) { + LOG_ERR("pm_device_runtime_get() failed: %d", rc); + } else { + /* This acquires the MSPI controller and reconfigures it + * if needed for the flash device. + */ + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + dev_config->mspi_cfg_mask, + &dev_config->mspi_cfg); + if (rc < 0) { + LOG_ERR("mspi_dev_config() failed: %d", rc); + } else { + return 0; + } + + (void)pm_device_runtime_put(dev_config->bus); + } + + k_sem_give(&dev_data->acquired); + return rc; +} + +static void release(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + + /* This releases the MSPI controller. */ + (void)mspi_get_channel_status(dev_config->bus, 0); + + (void)pm_device_runtime_put(dev_config->bus); + + k_sem_give(&dev_data->acquired); +} + +static inline uint32_t dev_flash_size(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + + return dev_config->flash_size; +} + +static inline uint16_t dev_page_size(const struct device *dev) +{ + return SPI_NOR_PAGE_SIZE; +} + +static int api_read(const struct device *dev, off_t addr, void *dest, + size_t size) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + const uint32_t flash_size = dev_flash_size(dev); + int rc; + + if (size == 0) { + return 0; + } + + if ((addr < 0) || ((addr + size) > flash_size)) { + return -EINVAL; + } + + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + /* TODO: get rid of all these hard-coded values for MX25Ux chips */ + dev_data->xfer.cmd_length = 2; + dev_data->xfer.addr_length = 4; + dev_data->xfer.rx_dummy = 20; + dev_data->packet.dir = MSPI_RX; + dev_data->packet.cmd = SPI_NOR_OCMD_RD; + dev_data->packet.address = addr; + dev_data->packet.data_buf = dest; + dev_data->packet.num_bytes = size; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + + release(dev); + + if (rc < 0) { + LOG_ERR("SPI_NOR_OCMD_RD xfer failed: %d", rc); + return rc; + } + + return 0; +} + +static int wait_until_ready(const struct device *dev, k_timeout_t poll_period) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + uint8_t status_reg; + int rc; + + while (true) { + dev_data->xfer.cmd_length = 2; + dev_data->xfer.addr_length = 4; + dev_data->xfer.rx_dummy = 4; + dev_data->packet.dir = MSPI_RX; + dev_data->packet.cmd = SPI_NOR_OCMD_RDSR; + dev_data->packet.address = 0; + dev_data->packet.data_buf = &status_reg; + dev_data->packet.num_bytes = sizeof(status_reg); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("SPI_NOR_OCMD_RDSR xfer failed: %d", rc); + return rc; + } + if (!(status_reg & SPI_NOR_WIP_BIT)) { + break; + } + + k_sleep(poll_period); + } + + return 0; +} + +static int api_write(const struct device *dev, off_t addr, const void *src, + size_t size) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + const uint32_t flash_size = dev_flash_size(dev); + const uint16_t page_size = dev_page_size(dev); + int rc; + + if (size == 0) { + return 0; + } + + if ((addr < 0) || ((addr + size) > flash_size)) { + return -EINVAL; + } + + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + while (size > 0) { + /* Split write into parts, each within one page only. */ + uint16_t page_offset = (uint16_t)(addr % page_size); + uint16_t page_left = page_size - page_offset; + uint16_t to_write = (uint16_t)MIN(size, page_left); + + dev_data->xfer.cmd_length = 2; + dev_data->xfer.tx_dummy = 0; + dev_data->packet.dir = MSPI_TX; + + dev_data->xfer.addr_length = 0; + dev_data->packet.cmd = SPI_NOR_OCMD_WREN; + dev_data->packet.num_bytes = 0; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc); + break; + } + + dev_data->xfer.addr_length = 4; + dev_data->packet.cmd = SPI_NOR_OCMD_PAGE_PRG; + dev_data->packet.address = addr; + dev_data->packet.data_buf = (uint8_t *)src; + dev_data->packet.num_bytes = to_write; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("SPI_NOR_OCMD_PAGE_PRG xfer failed: %d", rc); + break; + } + + addr += to_write; + src = (const uint8_t *)src + to_write; + size -= to_write; + + rc = wait_until_ready(dev, K_MSEC(1)); + if (rc < 0) { + break; + } + } + + release(dev); + + return rc; +} + +static int api_erase(const struct device *dev, off_t addr, size_t size) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + const uint32_t flash_size = dev_flash_size(dev); + int rc = 0; + + if ((addr < 0) || ((addr + size) > flash_size)) { + return -EINVAL; + } + + if (!SPI_NOR_IS_SECTOR_ALIGNED(addr)) { + return -EINVAL; + } + + if ((size % SPI_NOR_SECTOR_SIZE) != 0) { + return -EINVAL; + } + + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + while (size > 0) { + dev_data->xfer.cmd_length = 2; + dev_data->xfer.tx_dummy = 0; + dev_data->packet.dir = MSPI_TX; + dev_data->packet.num_bytes = 0; + + dev_data->xfer.addr_length = 0; + dev_data->packet.cmd = SPI_NOR_OCMD_WREN; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("SPI_NOR_OCMD_WREN xfer failed: %d", rc); + break; + } + + if (size == flash_size) { + /* Chip erase. */ + dev_data->xfer.addr_length = 0; + dev_data->packet.cmd = SPI_NOR_OCMD_CE; + + size -= flash_size; + } else { + /* Sector erase. */ + dev_data->xfer.addr_length = 4; + dev_data->packet.cmd = SPI_NOR_OCMD_SE; + dev_data->packet.address = addr; + + addr += SPI_NOR_SECTOR_SIZE; + size -= SPI_NOR_SECTOR_SIZE; + } + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("Erase command 0x%02x xfer failed: %d", + dev_data->packet.cmd, rc); + break; + } + + rc = wait_until_ready(dev, K_MSEC(1)); + if (rc < 0) { + break; + } + } + + release(dev); + + return rc; +} + +static const +struct flash_parameters *api_get_parameters(const struct device *dev) +{ + ARG_UNUSED(dev); + + static const struct flash_parameters parameters = { + .write_block_size = 1, + .erase_value = 0xff, + }; + + return ¶meters; +} + +static int read_jedec_id(const struct device *dev, uint8_t *id) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + dev_data->xfer.cmd_length = 2; + dev_data->xfer.addr_length = 4; + dev_data->xfer.rx_dummy = 4; + dev_data->packet.dir = MSPI_RX; + dev_data->packet.cmd = JESD216_OCMD_READ_ID; + dev_data->packet.address = 0; + dev_data->packet.data_buf = id; + dev_data->packet.num_bytes = JESD216_READ_ID_LEN; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + printk("mspi_transceive() failed: %d\n", rc); + return rc; + } + + return rc; +} + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +static void api_page_layout(const struct device *dev, + const struct flash_pages_layout **layout, + size_t *layout_size) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + + *layout = &dev_config->layout; + *layout_size = 1; +} +#endif /* CONFIG_FLASH_PAGE_LAYOUT */ + +#if defined(CONFIG_FLASH_JESD216_API) +static int api_sfdp_read(const struct device *dev, off_t addr, void *dest, + size_t size) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + if (size == 0) { + return 0; + } + + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + dev_data->xfer.cmd_length = 2; + dev_data->xfer.addr_length = 4; + dev_data->xfer.rx_dummy = 20; + dev_data->packet.dir = MSPI_RX; + dev_data->packet.cmd = JESD216_OCMD_READ_SFDP; + dev_data->packet.address = addr; + dev_data->packet.data_buf = dest; + dev_data->packet.num_bytes = size; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + printk("JESD216_OCMD_READ_SFDP xfer failed: %d\n", rc); + return rc; + } + + release(dev); + + return rc; +} + +static int api_read_jedec_id(const struct device *dev, uint8_t *id) +{ + int rc = 0; + + rc = acquire(dev); + if (rc < 0) { + return rc; + } + + rc = read_jedec_id(dev, id); + + release(dev); + + return rc; +} +#endif /* CONFIG_FLASH_JESD216_API */ + +static int dev_pm_action_cb(const struct device *dev, + enum pm_device_action action) +{ + switch (action) { + case PM_DEVICE_ACTION_SUSPEND: + break; + case PM_DEVICE_ACTION_RESUME: + break; + default: + return -ENOTSUP; + } + + return 0; +} + +static int flash_chip_init(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + struct mspi_dev_cfg init_dev_cfg = dev_config->mspi_cfg; + uint8_t id[JESD216_READ_ID_LEN] = {0}; + int rc; + + init_dev_cfg.freq = MHZ(1); + init_dev_cfg.io_mode = MSPI_IO_MODE_SINGLE; + + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_ALL, &init_dev_cfg); + if (rc < 0) { + LOG_ERR("Failed to set initial device config: %d", rc); + return rc; + } + + dev_data->xfer.xfer_mode = MSPI_PIO; + dev_data->xfer.packets = &dev_data->packet; + dev_data->xfer.num_packet = 1; + dev_data->xfer.timeout = 10; + + dev_data->xfer.cmd_length = 1; + dev_data->xfer.addr_length = 0; + dev_data->xfer.tx_dummy = 0; + dev_data->xfer.rx_dummy = 0; + + dev_data->packet.dir = MSPI_RX; + dev_data->packet.cmd = JESD216_CMD_READ_ID; + dev_data->packet.data_buf = id; + dev_data->packet.num_bytes = sizeof(id); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("Failed to read JEDEC ID in single line mode: %d", rc); + return rc; + } + + /* + * If the read ID does not match the one from DTS, assume the flash + * is already in the Octa I/O mode, so switching it is not needed. + */ + if (memcmp(id, dev_config->jedec_id, sizeof(id)) == 0) { + static const uint8_t enable_sopi[] = { 0x01 }; + + dev_data->packet.dir = MSPI_TX; + dev_data->packet.cmd = SPI_NOR_CMD_WREN; + dev_data->packet.num_bytes = 0; + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + LOG_ERR("SPI_NOR_CMD_WREN xfer failed: %d", rc); + return rc; + } + + dev_data->xfer.addr_length = 4; + dev_data->packet.cmd = SPI_NOR_CMD_WR_CFGREG2; + dev_data->packet.address = 0; + dev_data->packet.data_buf = (uint8_t *)&enable_sopi; + dev_data->packet.num_bytes = sizeof(enable_sopi); + rc = mspi_transceive(dev_config->bus, &dev_config->mspi_id, + &dev_data->xfer); + if (rc < 0) { + printk("SPI_NOR_CMD_WR_CFGREG2 xfer failed: %d\n", rc); + return rc; + } + } + + rc = mspi_dev_config(dev_config->bus, &dev_config->mspi_id, + MSPI_DEVICE_CONFIG_ALL, &dev_config->mspi_cfg); + if (rc < 0) { + LOG_ERR("Failed to set device config: %d", rc); + return rc; + } + + rc = read_jedec_id(dev, id); + if (rc < 0) { + return rc; + } + + if (memcmp(id, dev_config->jedec_id, sizeof(id)) != 0) { + LOG_ERR("JEDEC ID mismatch, read: %02x %02x %02x, " + "expected: %02x %02x %02x", + id[0], id[1], id[2], + dev_config->jedec_id[0], + dev_config->jedec_id[1], + dev_config->jedec_id[2]); + return -ENODEV; + } + +#if defined(CONFIG_MSPI_XIP) + /* Enable XIP access for this chip if specified so in DT. */ + if (dev_config->xip_cfg.enable) { + rc = mspi_xip_config(dev_config->bus, &dev_config->mspi_id, + &dev_config->xip_cfg); + if (rc < 0) { + return rc; + } + } +#endif + + return 0; +} + +static int drv_init(const struct device *dev) +{ + const struct flash_mspi_nor_config *dev_config = dev->config; + struct flash_mspi_nor_data *dev_data = dev->data; + int rc; + + if (!device_is_ready(dev_config->bus)) { + LOG_ERR("Device %s is not ready", dev_config->bus->name); + return -ENODEV; + } + +#if defined(WITH_RESET_GPIO) + if (dev_config->reset.port) { + if (!gpio_is_ready_dt(&dev_config->reset)) { + LOG_ERR("Device %s is not ready", + dev_config->reset.port->name); + return -ENODEV; + } + + rc = gpio_pin_configure_dt(&dev_config->reset, + GPIO_OUTPUT_ACTIVE); + if (rc < 0) { + LOG_ERR("Failed to activate RESET: %d", rc); + return -EIO; + } + + if (dev_config->reset_pulse_us != 0) { + k_busy_wait(dev_config->reset_pulse_us); + } + + rc = gpio_pin_set_dt(&dev_config->reset, 0); + if (rc < 0) { + LOG_ERR("Failed to deactivate RESET: %d", rc); + return -EIO; + } + + if (dev_config->reset_recovery_us != 0) { + k_busy_wait(dev_config->reset_recovery_us); + } + } +#endif + + rc = pm_device_runtime_get(dev_config->bus); + if (rc < 0) { + LOG_ERR("pm_device_runtime_get() failed: %d", rc); + return rc; + } + + rc = flash_chip_init(dev); + + /* Release the MSPI controller - it was acquired by the call to + * mspi_dev_config() in flash_chip_init(). + */ + (void)mspi_get_channel_status(dev_config->bus, 0); + + (void)pm_device_runtime_put(dev_config->bus); + + if (rc < 0) { + return rc; + } + + k_sem_init(&dev_data->acquired, 1, K_SEM_MAX_LIMIT); + + return pm_device_driver_init(dev, dev_pm_action_cb); +} + +static DEVICE_API(flash, drv_api) = { + .read = api_read, + .write = api_write, + .erase = api_erase, + .get_parameters = api_get_parameters, +#if defined(CONFIG_FLASH_PAGE_LAYOUT) + .page_layout = api_page_layout, +#endif +#if defined(CONFIG_FLASH_JESD216_API) + .sfdp_read = api_sfdp_read, + .read_jedec_id = api_read_jedec_id, +#endif +}; + +#define FLASH_SIZE_INST(inst) (DT_INST_PROP(inst, size) / 8) + +#if defined(CONFIG_FLASH_PAGE_LAYOUT) +BUILD_ASSERT((CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE % 4096) == 0, + "MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE must be multiple of 4096"); +#define FLASH_PAGE_LAYOUT_DEFINE(inst) \ + .layout = { \ + .pages_size = CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \ + .pages_count = FLASH_SIZE_INST(inst) \ + / CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE, \ + }, +#define FLASH_PAGE_LAYOUT_CHECK(inst) \ +BUILD_ASSERT((FLASH_SIZE_INST(inst) % CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE) == 0, \ + "MSPI_NOR_FLASH_LAYOUT_PAGE_SIZE incompatible with flash size, instance " #inst); +#else +#define FLASH_PAGE_LAYOUT_DEFINE(inst) +#define FLASH_PAGE_LAYOUT_CHECK(inst) +#endif + +/* MSPI bus must be initialized before this device. */ +#if (CONFIG_MSPI_INIT_PRIORITY < CONFIG_FLASH_INIT_PRIORITY) +#define INIT_PRIORITY CONFIG_FLASH_INIT_PRIORITY +#else +#define INIT_PRIORITY UTIL_INC(CONFIG_MSPI_INIT_PRIORITY) +#endif + +#define FLASH_MSPI_NOR_INST(inst) \ + BUILD_ASSERT(DT_INST_ENUM_IDX(inst, mspi_io_mode) == \ + MSPI_IO_MODE_OCTAL, \ + "Only Octal I/O mode is supported for now"); \ + PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \ + static struct flash_mspi_nor_data dev##inst##_data; \ + static const struct flash_mspi_nor_config dev##inst##_config = { \ + .bus = DEVICE_DT_GET(DT_INST_BUS(inst)), \ + .flash_size = FLASH_SIZE_INST(inst), \ + .mspi_id = MSPI_DEVICE_ID_DT_INST(inst), \ + .mspi_cfg = MSPI_DEVICE_CONFIG_DT_INST(inst), \ + .mspi_cfg_mask = DT_PROP(DT_INST_BUS(inst), \ + software_multiperipheral) \ + ? MSPI_DEVICE_CONFIG_ALL \ + : MSPI_DEVICE_CONFIG_NONE, \ + IF_ENABLED(CONFIG_MSPI_XIP, \ + (.xip_cfg = MSPI_XIP_CONFIG_DT_INST(inst),)) \ + IF_ENABLED(WITH_RESET_GPIO, \ + (.reset = GPIO_DT_SPEC_INST_GET_OR(inst, reset_gpios, {0}), \ + .reset_pulse_us = DT_INST_PROP_OR(inst, t_reset_pulse, 0) \ + / 1000, \ + .reset_recovery_us = DT_INST_PROP_OR(inst, t_reset_recovery, 0) \ + / 1000,)) \ + FLASH_PAGE_LAYOUT_DEFINE(inst) \ + .jedec_id = DT_INST_PROP(inst, jedec_id), \ + }; \ + FLASH_PAGE_LAYOUT_CHECK(inst) \ + DEVICE_DT_INST_DEFINE(inst, \ + drv_init, PM_DEVICE_DT_INST_GET(inst), \ + &dev##inst##_data, &dev##inst##_config, \ + POST_KERNEL, INIT_PRIORITY, \ + &drv_api); + +DT_INST_FOREACH_STATUS_OKAY(FLASH_MSPI_NOR_INST) diff --git a/dts/bindings/mtd/jedec,mspi-nor.yaml b/dts/bindings/mtd/jedec,mspi-nor.yaml new file mode 100644 index 0000000000000..5bad8f54b5dc6 --- /dev/null +++ b/dts/bindings/mtd/jedec,mspi-nor.yaml @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: Generic NOR flash on MSPI bus + +compatible: "jedec,mspi-nor" + +include: [mspi-device.yaml, "jedec,spi-nor-common.yaml"] + +properties: + reset-gpios: + type: phandle-array + description: | + RESET line. If specified, the flash chip will be reset at initialization. + + t-reset-pulse: + type: int + description: | + Minimum duration, in nanoseconds, of an active pulse on the RESET line. + + t-reset-recovery: + type: int + description: | + Minimum time, in nanoseconds, the flash chip needs to recover after reset. From 46b25cdc7cdb40f6ff6432afad02563279ef4064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Thu, 17 Oct 2024 14:17:54 +0200 Subject: [PATCH 3/7] drivers: pinctrl_nrf: Add support for EXMIF pins MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a follow-up to commit 45d827a51a708e54437f5681dc6c4fb77bfb4f52. Although routing for those pins is configured via UICR, pinctrl still needs to be involved so that it is possible to set desired drive mode for them etc. Add also the missing RWDS pin. Signed-off-by: Andrzej Głąbek --- drivers/pinctrl/pinctrl_nrf.c | 18 ++++++++++++++++++ .../zephyr/dt-bindings/pinctrl/nrf-pinctrl.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/drivers/pinctrl/pinctrl_nrf.c b/drivers/pinctrl/pinctrl_nrf.c index 3ede211958ac8..77387e4880661 100644 --- a/drivers/pinctrl/pinctrl_nrf.c +++ b/drivers/pinctrl/pinctrl_nrf.c @@ -383,6 +383,24 @@ int pinctrl_configure_pins(const pinctrl_soc_pin_t *pins, uint8_t pin_cnt, input = NRF_GPIO_PIN_INPUT_CONNECT; break; #endif /* DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_can) */ +#if DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_exmif) + /* Pin routing is controlled by secure domain, via UICR */ + case NRF_FUN_EXMIF_CK: + case NRF_FUN_EXMIF_DQ0: + case NRF_FUN_EXMIF_DQ1: + case NRF_FUN_EXMIF_DQ2: + case NRF_FUN_EXMIF_DQ3: + case NRF_FUN_EXMIF_DQ4: + case NRF_FUN_EXMIF_DQ5: + case NRF_FUN_EXMIF_DQ6: + case NRF_FUN_EXMIF_DQ7: + case NRF_FUN_EXMIF_CS0: + case NRF_FUN_EXMIF_CS1: + case NRF_FUN_EXMIF_RWDS: + dir = NRF_GPIO_PIN_DIR_INPUT; + input = NRF_GPIO_PIN_INPUT_DISCONNECT; + break; +#endif /* DT_HAS_COMPAT_STATUS_OKAY(nordic_nrf_exmif) */ #if defined(NRF_PSEL_TWIS) case NRF_FUN_TWIS_SCL: NRF_PSEL_TWIS(reg, SCL) = psel; diff --git a/include/zephyr/dt-bindings/pinctrl/nrf-pinctrl.h b/include/zephyr/dt-bindings/pinctrl/nrf-pinctrl.h index 51656a1507430..7d75f0d710946 100644 --- a/include/zephyr/dt-bindings/pinctrl/nrf-pinctrl.h +++ b/include/zephyr/dt-bindings/pinctrl/nrf-pinctrl.h @@ -166,6 +166,8 @@ #define NRF_FUN_TWIS_SCL 48U /** TWIS SDA */ #define NRF_FUN_TWIS_SDA 49U +/** EXMIF RWDS */ +#define NRF_FUN_EXMIF_RWDS 50U /** GRTC fast clock output */ #define NRF_FUN_GRTC_CLKOUT_FAST 55U /** GRTC slow clock output */ From ef694645ebe7f528171ee29d01c647b5f14c6ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Thu, 17 Oct 2024 17:43:48 +0200 Subject: [PATCH 4/7] dts: nordic: Change compatible property for EXMIF node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a follow-up to commit cdf45cb234077522b5cef2da084869af43d42dc1. Adjust the DTS node for the nRF EXMIF peripheral so that it is possible to handle the peripheral with the generic MSPI driver for DW SSI based controllers and use all its data lines in communication. Also adjust the related board files accordingly. Signed-off-by: Andrzej Głąbek --- .../nrf54h20dk_nrf54h20-pinctrl.dtsi | 27 ++++++++++++++++++- .../nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts | 19 ++++++++++--- .../nrf9280pdk_nrf9280-pinctrl.dtsi | 27 ++++++++++++++++++- .../nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts | 19 ++++++++++--- .../{spi => mspi}/nordic,nrf-exmif.yaml | 6 +---- dts/common/nordic/nrf54h20.dtsi | 5 ++-- dts/common/nordic/nrf9280.dtsi | 5 ++-- 7 files changed, 87 insertions(+), 21 deletions(-) rename dts/bindings/{spi => mspi}/nordic,nrf-exmif.yaml (70%) diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-pinctrl.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-pinctrl.dtsi index 9b574a18ec52c..f62df87dfe269 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-pinctrl.dtsi +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-pinctrl.dtsi @@ -73,12 +73,37 @@ /omit-if-no-ref/ exmif_default: exmif_default { group1 { psels = , + , + , , - ; + , + , + , + , + , + , + ; nordic,drive-mode = ; }; }; + /omit-if-no-ref/ exmif_sleep: exmif_sleep { + group1 { + low-power-enable; + psels = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + /omit-if-no-ref/ can120_default: can120_default { group1 { psels = , diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts index 95d557bf944de..ed53ade9d0ffe 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20_cpuapp.dts @@ -256,15 +256,15 @@ ipc0: &cpuapp_cpurad_ipc { }; &exmif { - cs-gpios = <&gpio6 3 GPIO_ACTIVE_LOW>; pinctrl-0 = <&exmif_default>; - pinctrl-names = "default"; + pinctrl-1 = <&exmif_sleep>; + pinctrl-names = "default", "sleep"; status = "okay"; + mx25uw63: mx25uw6345g@0 { - compatible = "jedec,spi-nor"; + compatible = "jedec,mspi-nor"; status = "disabled"; reg = <0>; - spi-max-frequency = ; jedec-id = [c2 84 37]; sfdp-bfp = [ e5 20 8a ff ff ff ff 03 00 ff 00 ff 00 ff 00 ff @@ -277,6 +277,17 @@ ipc0: &cpuapp_cpurad_ipc { has-dpd; t-enter-dpd = <10000>; t-exit-dpd = <30000>; + reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>; + t-reset-pulse = <10000>; + t-reset-recovery = <35000>; + + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_OCTAL"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-hardware-ce-num = <1>; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-endian = "MSPI_BIG_ENDIAN"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; }; }; diff --git a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280-pinctrl.dtsi b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280-pinctrl.dtsi index 4cae3ba580ffc..709fda74f89fb 100644 --- a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280-pinctrl.dtsi +++ b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280-pinctrl.dtsi @@ -54,12 +54,37 @@ /omit-if-no-ref/ exmif_default: exmif_default { group1 { psels = , + , + , , - ; + , + , + , + , + , + , + ; nordic,drive-mode = ; }; }; + /omit-if-no-ref/ exmif_sleep: exmif_sleep { + group1 { + low-power-enable; + psels = , + , + , + , + , + , + , + , + , + , + ; + }; + }; + /omit-if-no-ref/ pwm130_default: pwm130_default { group1 { psels = ; diff --git a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts index 11760974a11aa..31502189d3f8e 100644 --- a/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts +++ b/boards/nordic/nrf9280pdk/nrf9280pdk_nrf9280_cpuapp.dts @@ -242,15 +242,15 @@ ipc0: &cpuapp_cpurad_ipc { }; &exmif { - cs-gpios = <&gpio6 3 GPIO_ACTIVE_LOW>; pinctrl-0 = <&exmif_default>; - pinctrl-names = "default"; + pinctrl-1 = <&exmif_sleep>; + pinctrl-names = "default", "sleep"; status = "okay"; + mx25uw63: mx25uw6345g@0 { - compatible = "jedec,spi-nor"; + compatible = "jedec,mspi-nor"; status = "disabled"; reg = <0>; - spi-max-frequency = ; jedec-id = [c2 84 37]; sfdp-bfp = [ e5 20 8a ff ff ff ff 03 00 ff 00 ff 00 ff 00 ff @@ -263,6 +263,17 @@ ipc0: &cpuapp_cpurad_ipc { has-dpd; t-enter-dpd = <10000>; t-exit-dpd = <30000>; + reset-gpios = <&gpio6 12 GPIO_ACTIVE_LOW>; + t-reset-pulse = <10000>; + t-reset-recovery = <35000>; + + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_OCTAL"; + mspi-data-rate = "MSPI_DATA_RATE_SINGLE"; + mspi-hardware-ce-num = <1>; + mspi-cpp-mode = "MSPI_CPP_MODE_0"; + mspi-endian = "MSPI_BIG_ENDIAN"; + mspi-ce-polarity = "MSPI_CE_ACTIVE_LOW"; }; }; diff --git a/dts/bindings/spi/nordic,nrf-exmif.yaml b/dts/bindings/mspi/nordic,nrf-exmif.yaml similarity index 70% rename from dts/bindings/spi/nordic,nrf-exmif.yaml rename to dts/bindings/mspi/nordic,nrf-exmif.yaml index d2b8815046b5b..294254aa60efb 100644 --- a/dts/bindings/spi/nordic,nrf-exmif.yaml +++ b/dts/bindings/mspi/nordic,nrf-exmif.yaml @@ -5,8 +5,4 @@ description: Nordic External Memory Interface (EXMIF) compatible: "nordic,nrf-exmif" -include: snps,designware-spi.yaml - -properties: - reg: - required: true +include: snps,designware-ssi.yaml diff --git a/dts/common/nordic/nrf54h20.dtsi b/dts/common/nordic/nrf54h20.dtsi index 3dc8afb707189..45a3f76e45b5f 100644 --- a/dts/common/nordic/nrf54h20.dtsi +++ b/dts/common/nordic/nrf54h20.dtsi @@ -510,8 +510,8 @@ status = "disabled"; }; - exmif: spi@95000 { - compatible = "nordic,nrf-exmif"; + exmif: exmif@95000 { + compatible = "nordic,nrf-exmif", "snps,designware-ssi"; #address-cells = <1>; #size-cells = <0>; reg = <0x95000 0x500 0x95500 0xb00>; @@ -520,7 +520,6 @@ power-domains = <&gpd NRF_GPD_FAST_ACTIVE0>; clock-frequency = ; fifo-depth = <32>; - max-xfer-size = <16>; status = "disabled"; }; diff --git a/dts/common/nordic/nrf9280.dtsi b/dts/common/nordic/nrf9280.dtsi index ee201dbacfde0..55f7f8d20fcff 100644 --- a/dts/common/nordic/nrf9280.dtsi +++ b/dts/common/nordic/nrf9280.dtsi @@ -375,8 +375,8 @@ status = "disabled"; }; - exmif: spi@95000 { - compatible = "nordic,nrf-exmif"; + exmif: exmif@95000 { + compatible = "nordic,nrf-exmif", "snps,designware-ssi"; #address-cells = <1>; #size-cells = <0>; reg = <0x95000 0x500 0x95500 0xb00>; @@ -384,7 +384,6 @@ interrupts = <149 NRF_DEFAULT_IRQ_PRIORITY>; clock-frequency = ; fifo-depth = <32>; - max-xfer-size = <16>; status = "disabled"; }; From 5b432fb0e1ea7f2bf409e1810133d7afe7f39e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Fri, 18 Oct 2024 10:16:08 +0200 Subject: [PATCH 5/7] samples|tests: drivers: flash: Add support for "jedec,mspi-nor" devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend several flash samples and tests so that they can also be used with "jedec,mspi-nor" devices. Add configurations needed for the nrf54h20dk/nrf54h20/cpuapp target. Signed-off-by: Andrzej Głąbek --- .../jesd216/boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 9 +++++++++ samples/drivers/jesd216/sample.yaml | 2 +- samples/drivers/jesd216/src/main.c | 4 +++- .../spi_flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 9 +++++++++ samples/drivers/spi_flash/sample.yaml | 2 +- samples/drivers/spi_flash/src/main.c | 2 ++ .../common/boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 9 +++++++++ tests/drivers/flash/common/src/main.c | 2 ++ 8 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 samples/drivers/jesd216/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 samples/drivers/spi_flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_cpuapp.overlay diff --git a/samples/drivers/jesd216/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/drivers/jesd216/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..b8f138ad2b2e2 --- /dev/null +++ b/samples/drivers/jesd216/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&mx25uw63 { + status = "okay"; +}; diff --git a/samples/drivers/jesd216/sample.yaml b/samples/drivers/jesd216/sample.yaml index 0ea0dae331aee..4be5e5defbbe4 100644 --- a/samples/drivers/jesd216/sample.yaml +++ b/samples/drivers/jesd216/sample.yaml @@ -21,7 +21,7 @@ tests: - hifive_unmatched/fu740/u74 - mimxrt1170_evk/mimxrt1176/cm7 - mimxrt1170_evk/mimxrt1176/cm4 - filter: dt_compat_enabled("jedec,spi-nor") + filter: dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("jedec,mspi-nor") depends_on: spi sample.drivers.jesd216.nrf52840dk_spi: extra_args: diff --git a/samples/drivers/jesd216/src/main.c b/samples/drivers/jesd216/src/main.c index 480649397b6b2..b8dad485997a3 100644 --- a/samples/drivers/jesd216/src/main.c +++ b/samples/drivers/jesd216/src/main.c @@ -14,6 +14,8 @@ #if DT_HAS_COMPAT_STATUS_OKAY(jedec_spi_nor) #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(jedec_spi_nor) +#elif DT_HAS_COMPAT_STATUS_OKAY(jedec_mspi_nor) +#define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(jedec_mspi_nor) #elif DT_HAS_COMPAT_STATUS_OKAY(nordic_qspi_nor) #define FLASH_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(nordic_qspi_nor) #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_qspi_nor) @@ -201,7 +203,7 @@ static void summarize_dw15(const struct jesd216_param_header *php, printf("0-4-4 Mode methods: entry 0x%01x ; exit 0x%02x\n", dw15.entry_044, dw15.exit_044); } else { - printf("0-4-4 Mode: not supported"); + printf("0-4-4 Mode: not supported\n"); } printf("4-4-4 Mode sequences: enable 0x%02x ; disable 0x%01x\n", dw15.enable_444, dw15.disable_444); diff --git a/samples/drivers/spi_flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/drivers/spi_flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..b8f138ad2b2e2 --- /dev/null +++ b/samples/drivers/spi_flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&mx25uw63 { + status = "okay"; +}; diff --git a/samples/drivers/spi_flash/sample.yaml b/samples/drivers/spi_flash/sample.yaml index a3b8cfed380a5..351e33997a0b8 100644 --- a/samples/drivers/spi_flash/sample.yaml +++ b/samples/drivers/spi_flash/sample.yaml @@ -7,7 +7,7 @@ tests: - flash filter: dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("st,stm32-qspi-nor") or dt_compat_enabled("st,stm32-ospi-nor") or dt_compat_enabled("st,stm32-xspi-nor") - or (dt_compat_enabled("nordic,qspi-nor") and CONFIG_NORDIC_QSPI_NOR) + or dt_compat_enabled("nordic,qspi-nor") or dt_compat_enabled("jedec,mspi-nor") platform_exclude: - hifive_unmatched/fu740/s7 - hifive_unmatched/fu740/u74 diff --git a/samples/drivers/spi_flash/src/main.c b/samples/drivers/spi_flash/src/main.c index b84359ae05bb1..52e8b6062b538 100644 --- a/samples/drivers/spi_flash/src/main.c +++ b/samples/drivers/spi_flash/src/main.c @@ -34,6 +34,8 @@ #if DT_HAS_COMPAT_STATUS_OKAY(jedec_spi_nor) #define SPI_FLASH_COMPAT jedec_spi_nor +#elif DT_HAS_COMPAT_STATUS_OKAY(jedec_mspi_nor) +#define SPI_FLASH_COMPAT jedec_mspi_nor #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_qspi_nor) #define SPI_FLASH_COMPAT st_stm32_qspi_nor #elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_ospi_nor) diff --git a/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..b8f138ad2b2e2 --- /dev/null +++ b/tests/drivers/flash/common/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&mx25uw63 { + status = "okay"; +}; diff --git a/tests/drivers/flash/common/src/main.c b/tests/drivers/flash/common/src/main.c index e7552aaf177b2..54b868a923413 100644 --- a/tests/drivers/flash/common/src/main.c +++ b/tests/drivers/flash/common/src/main.c @@ -14,6 +14,8 @@ #define TEST_AREA_DEV_NODE DT_INST(0, nordic_qspi_nor) #elif defined(CONFIG_SPI_NOR) #define TEST_AREA_DEV_NODE DT_INST(0, jedec_spi_nor) +#elif defined(CONFIG_FLASH_MSPI_NOR) +#define TEST_AREA_DEV_NODE DT_INST(0, jedec_mspi_nor) #else #define TEST_AREA storage_partition #endif From 04dc530303768d20a11d33fb81f965ead743cffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Tue, 3 Dec 2024 17:51:51 +0100 Subject: [PATCH 6/7] boards: nrf54h20dk: Add XIP region definition to memory map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Access to this region must be requested through UICR by a local domain that want to use the Execute In Place (XIP) feature of the EXMIF peripheral. Signed-off-by: Andrzej Głąbek --- .../nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi index 8392389c0ad67..165fa12ba80d5 100644 --- a/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi +++ b/boards/nordic/nrf54h20dk/nrf54h20dk_nrf54h20-memory_map.dtsi @@ -174,6 +174,15 @@ zephyr,memory-region = "DMA_RAM3x_RAD"; zephyr,memory-attr = <( DT_MEM_DMA )>; }; + + xip_region: memory@60000000 { + compatible = "nordic,owned-memory"; + reg = <0x60000000 0x20000000>; + status = "disabled"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x60000000 0x20000000>; + }; }; }; From fccffbd19e198a5771d02603a95c51040e61a683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20G=C5=82=C4=85bek?= Date: Tue, 3 Dec 2024 18:02:10 +0100 Subject: [PATCH 7/7] samples: code_relocation_nocopy: Add configuration for nRF54H20 DK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add nRF54H20 DK specific entries to allow using the sample on this board. Signed-off-by: Andrzej Głąbek --- .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 1 + .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 17 +++++++++++++++++ .../code_relocation_nocopy/linker_arm_nocopy.ld | 7 +++++++ 3 files changed, 25 insertions(+) create mode 100644 samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.conf create mode 100644 samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay diff --git a/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 0000000000000..8303508138954 --- /dev/null +++ b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_FLASH=y diff --git a/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..ab53f12725d77 --- /dev/null +++ b/samples/application_development/code_relocation_nocopy/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,17 @@ +&cpuapp_ram0x_region { + nordic,access = ; +}; + +&xip_region { + status = "okay"; + nordic,access = ; +}; + +&mx25uw63 { + read-command = <0xEC13>; + command-length = "INSTR_2_BYTE"; + address-length = "ADDR_4_BYTE"; + rx-dummy = <20>; + + xip-config = <1 0 0x20000000 0>; +}; diff --git a/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld b/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld index a1467f8e26454..0a11783cca2ae 100644 --- a/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld +++ b/samples/application_development/code_relocation_nocopy/linker_arm_nocopy.ld @@ -47,6 +47,13 @@ #define EXTFLASH_ADDR DT_REG_ADDR(DT_INST(0, st_stm32_xspi_nor)) #define EXTFLASH_SIZE DT_REG_ADDR_BY_IDX(DT_INST(0, st_stm32_xspi_nor), 1) +#elif defined(CONFIG_FLASH_MSPI_NOR) && defined(CONFIG_SOC_NRF54H20_CPUAPP) + +#define EXTFLASH_NODE DT_INST(0, jedec_mspi_nor) +#define EXTFLASH_ADDR 0x60000000 +#define EXTFLASH_SIZE DT_PROP_OR(EXTFLASH_NODE, size_in_bytes, \ + DT_PROP(EXTFLASH_NODE, size) / 8) + #else /*