diff --git a/CODEOWNERS b/CODEOWNERS index a398a3e0989f..1c73238209a3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -674,7 +674,9 @@ /samples/zephyr/drivers/adc/ @nrfconnect/ncs-low-level-test /samples/zephyr/drivers/audio/dmic/ @nrfconnect/ncs-low-level-test /samples/zephyr/drivers/i2c/rtio_loopback/ @nrfconnect/ncs-low-level-test +/samples/zephyr/drivers/jesd216/ @nrfconnect/ncs-low-level-test /samples/zephyr/drivers/mbox/ @nrfconnect/ncs-low-level-test +/samples/zephyr/drivers/spi_flash/ @nrfconnect/ncs-low-level-test /samples/zephyr/drivers/watchdog/ @nrfconnect/ncs-low-level-test /samples/zephyr/sensor/accel_polling/ @nrfconnect/ncs-low-level-test /samples/zephyr/sensor/bme680/ @nrfconnect/ncs-low-level-test diff --git a/drivers/mspi/CMakeLists.txt b/drivers/mspi/CMakeLists.txt index 9e00cb992f4c..6ad06ca31f7f 100644 --- a/drivers/mspi/CMakeLists.txt +++ b/drivers/mspi/CMakeLists.txt @@ -6,3 +6,27 @@ zephyr_library_amend() zephyr_library_sources_ifdef(CONFIG_MSPI_NRFE mspi_nrfe.c) + +if(CONFIG_MSPI_NRF_SQSPI) + set(SP_DIR ${ZEPHYR_NRFXLIB_MODULE_DIR}/softperipheral) + set(SQSPI_DIR ${SP_DIR}/sQSPI) + + zephyr_library_compile_definitions( + NRFX_QSPI2_ENABLED=1 + NRFX_QSPI2_MAX_NUM_DATA_LINES=4 + ) + zephyr_library_include_directories( + ${SP_DIR}/include + ${SQSPI_DIR}/include + ) + zephyr_library_include_directories_ifdef(CONFIG_SOC_NRF54L15 + ${SQSPI_DIR}/include/nrf54l15 + ) + zephyr_library_include_directories_ifdef(CONFIG_SOC_NRF54H20 + ${SQSPI_DIR}/include/nrf54h20 + ) + zephyr_library_sources( + ${SQSPI_DIR}/src/nrfx_qspi2.c + mspi_sqspi.c + ) +endif() diff --git a/drivers/mspi/Kconfig b/drivers/mspi/Kconfig index a2f4c8979ff9..5b43d58d6ea7 100644 --- a/drivers/mspi/Kconfig +++ b/drivers/mspi/Kconfig @@ -4,5 +4,6 @@ if MSPI rsource "Kconfig.nrfe" +rsource "Kconfig.sqspi" endif # MSPI diff --git a/drivers/mspi/Kconfig.sqspi b/drivers/mspi/Kconfig.sqspi new file mode 100644 index 000000000000..e346e0011152 --- /dev/null +++ b/drivers/mspi/Kconfig.sqspi @@ -0,0 +1,9 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +config MSPI_NRF_SQSPI + bool "sQSPI driver" + default y + depends on DT_HAS_NORDIC_NRF_SQSPI_ENABLED + select PINCTRL + select GPIO if $(dt_compat_any_has_prop,$(DT_COMPAT_NORDIC_NRF_SQSPI),ce-gpios) diff --git a/drivers/mspi/mspi_sqspi.c b/drivers/mspi/mspi_sqspi.c new file mode 100644 index 000000000000..27ad835608e9 --- /dev/null +++ b/drivers/mspi/mspi_sqspi.c @@ -0,0 +1,581 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#define DT_DRV_COMPAT nordic_nrf_sqspi + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#if defined(CONFIG_SOC_SERIES_NRF54LX) +#include +#include +#endif +#if defined(CONFIG_SOC_NRF54H20_GPD) +#include +#endif +#include + +LOG_MODULE_REGISTER(mspi_sqspi, CONFIG_MSPI_LOG_LEVEL); + +#define VPR_NODE DT_NODELABEL(cpuflpr_vpr) + +struct mspi_sqspi_data { + const struct mspi_dev_id *dev_id; + nrfx_qspi2_dev_cfg_t qspi2_dev_cfg; + 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; + bool suspended; +}; + +struct mspi_sqspi_config { + nrfx_qspi2_t qspi2; + const struct pinctrl_dev_config *pcfg; + const struct gpio_dt_spec *ce_gpios; + uint8_t ce_gpios_len; + void *mem_reg; +}; + +static void done_callback(const nrfx_qspi2_t *qspi2, nrfx_qspi2_evt_t *event, + void *context) +{ + ARG_UNUSED(qspi2); + struct mspi_sqspi_data *dev_data = context; + + if (event->type == NRFX_QSPI2_EVT_XFER_DONE) { + if (event->data.xfer_done == NRFX_QSPI2_RESULT_OK) { + k_sem_give(&dev_data->finished); + } + } +} + +static int api_config(const struct mspi_dt_spec *spec) +{ + ARG_UNUSED(spec); + + return -ENOTSUP; +} + +static int _api_dev_config(const struct device *dev, + const enum mspi_dev_cfg_mask param_mask, + const struct mspi_dev_cfg *cfg) +{ + struct mspi_sqspi_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) { + switch (cfg->io_mode) { + default: + case MSPI_IO_MODE_SINGLE: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_SINGLE; + break; + case MSPI_IO_MODE_DUAL: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_DUAL_2_2_2; + break; + case MSPI_IO_MODE_DUAL_1_1_2: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_DUAL_1_1_2; + break; + case MSPI_IO_MODE_DUAL_1_2_2: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_DUAL_1_2_2; + break; + case MSPI_IO_MODE_QUAD: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_QUAD_4_4_4; + break; + case MSPI_IO_MODE_QUAD_1_1_4: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_QUAD_1_1_4; + break; + case MSPI_IO_MODE_QUAD_1_4_4: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_QUAD_1_4_4; + break; + case MSPI_IO_MODE_OCTAL: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_OCTAL_8_8_8; + break; + case MSPI_IO_MODE_OCTAL_1_1_8: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_OCTAL_1_1_8; + break; + case MSPI_IO_MODE_OCTAL_1_8_8: + dev_data->qspi2_dev_cfg.mspi_lines = NRFX_QSPI2_SPI_LINES_OCTAL_1_8_8; + break; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_CPP) { + switch (cfg->cpp) { + default: + case MSPI_CPP_MODE_0: + dev_data->qspi2_dev_cfg.spi_cpolpha = NRFX_QSPI2_SPI_CPOLPHA_0; + break; + case MSPI_CPP_MODE_1: + dev_data->qspi2_dev_cfg.spi_cpolpha = NRFX_QSPI2_SPI_CPOLPHA_1; + break; + case MSPI_CPP_MODE_2: + dev_data->qspi2_dev_cfg.spi_cpolpha = NRFX_QSPI2_SPI_CPOLPHA_2; + break; + case MSPI_CPP_MODE_3: + dev_data->qspi2_dev_cfg.spi_cpolpha = NRFX_QSPI2_SPI_CPOLPHA_3; + break; + } + } + + if (param_mask & MSPI_DEVICE_CONFIG_FREQUENCY) { + if (cfg->freq < 1 || + cfg->freq > SP_VPR_BASE_FREQ_HZ) { + LOG_ERR("Invalid frequency: %u, MIN: %u, MAX: %u", + cfg->freq, 1, SP_VPR_BASE_FREQ_HZ); + return -EINVAL; + } + + dev_data->qspi2_dev_cfg.sck_freq_khz = cfg->freq / 1000; + } + + 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; + } + } + + 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) +{ + struct mspi_sqspi_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) { + 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_sqspi_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 int perform_xfer(const struct device *dev, + const nrfx_qspi2_xfer_t *qspi2_xfer, + k_timeout_t timeout) +{ + const struct mspi_sqspi_config *dev_config = dev->config; + struct mspi_sqspi_data *dev_data = dev->data; + int rc = 0; + nrfx_err_t err; + + 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; + } + } + + err = nrfx_qspi2_xfer(&dev_config->qspi2, qspi2_xfer, 1, 0); + if (err != NRFX_SUCCESS) { + LOG_ERR("nrfx_qspi2_xfer() failed: %08x", err); + return -EIO; + } + + rc = k_sem_take(&dev_data->finished, timeout); + if (rc < 0) { + rc = -ETIMEDOUT; + } + + 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 process_packet(const struct device *dev, + const struct mspi_xfer_packet *packet, + const struct mspi_xfer *xfer, + k_timeout_t timeout) +{ + const struct mspi_sqspi_config *dev_config = dev->config; + nrfx_qspi2_xfer_t qspi2_xfer = { + .cmd = packet->cmd, + .address = packet->address, + .data_length = packet->num_bytes, + .cmd_length = 8 * xfer->cmd_length, + .addr_length = 8 * xfer->addr_length, + }; + int rc; + + if (packet->num_bytes) { + if (packet->dir == MSPI_TX) { + qspi2_xfer.dir = NRFX_QSPI2_XFER_DIR_TX; + qspi2_xfer.dummy_length = xfer->tx_dummy; + rc = dmm_buffer_out_prepare(dev_config->mem_reg, + packet->data_buf, + packet->num_bytes, + &qspi2_xfer.p_data); + } else { + qspi2_xfer.dir = NRFX_QSPI2_XFER_DIR_RX; + qspi2_xfer.dummy_length = xfer->rx_dummy; + rc = dmm_buffer_in_prepare(dev_config->mem_reg, + packet->data_buf, + packet->num_bytes, + &qspi2_xfer.p_data); + } + if (rc < 0) { + LOG_ERR("Failed to allocate DMM buffer (%d)", rc); + return rc; + } + } else if (xfer->cmd_length == 0 && + xfer->addr_length == 0) { + /* Nothing to be transferred, skip this packet. */ + return 0; + } + + rc = perform_xfer(dev, &qspi2_xfer, timeout); + + if (packet->num_bytes) { + /* No need to check the error codes here. These calls could only + * fail if an invalid memory region was specified, but since the + * same one was used in the corresponding *_prepare() call above + * and that call has succeeded, we know the region is valid. + */ + if (packet->dir == MSPI_TX) { + (void)dmm_buffer_out_release(dev_config->mem_reg, + qspi2_xfer.p_data); + } else { + (void)dmm_buffer_in_release(dev_config->mem_reg, + packet->data_buf, + packet->num_bytes, + qspi2_xfer.p_data); + } + } + + return rc; +} + +static int _api_transceive(const struct device *dev, + const struct mspi_xfer *req) +{ + const struct mspi_sqspi_config *dev_config = dev->config; + struct mspi_sqspi_data *dev_data = dev->data; + k_timeout_t timeout = K_MSEC(req->timeout); + uint32_t done; + nrfx_err_t err; + int rc; + + err = nrfx_qspi2_dev_cfg(&dev_config->qspi2, &dev_data->qspi2_dev_cfg, + done_callback, dev_data); + if (err != NRFX_SUCCESS) { + LOG_ERR("nrfx_qspi2_dev_cfg() failed: %08x", err); + return -EIO; + } + + for (done = 0; done < req->num_packet; ++done) { + rc = process_packet(dev, &req->packets[done], req, 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_sqspi_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; +} + +static int dev_pm_action_cb(const struct device *dev, + enum pm_device_action action) +{ + struct mspi_sqspi_data *dev_data = dev->data; + const struct mspi_sqspi_config *dev_config = dev->config; + int rc; + + if (action == PM_DEVICE_ACTION_RESUME) { + rc = pinctrl_apply_state(dev_config->pcfg, + PINCTRL_STATE_DEFAULT); + if (rc < 0) { + LOG_ERR("Cannot apply default pins state (%d)", rc); + return rc; + } + +#if defined(CONFIG_SOC_NRF54H20_GPD) + rc = nrf_gpd_retain_pins_set(dev_config->pcfg, false); + if (rc < 0) { + LOG_ERR("Cannot clear RETAIN for pins (%d)", rc); + return rc; + } +#endif + nrfx_qspi2_activate(&dev_config->qspi2); + + dev_data->suspended = false; + + return 0; + } + + if (IS_ENABLED(CONFIG_PM_DEVICE) && + action == PM_DEVICE_ACTION_SUSPEND) { +#if defined(CONFIG_SOC_NRF54H20_GPD) + rc = nrf_gpd_retain_pins_set(dev_config->pcfg, true); + if (rc < 0) { + LOG_ERR("Cannot set RETAIN for pins (%d)", rc); + return rc; + } +#endif + rc = pinctrl_apply_state(dev_config->pcfg, + PINCTRL_STATE_SLEEP); + if (rc < 0) { + LOG_ERR("Cannot apply sleep pins state (%d)", rc); + return rc; + } + + if (k_sem_take(&dev_data->ctx_lock, K_NO_WAIT) != 0) { + LOG_ERR("Controller in use, cannot be suspended"); + return -EBUSY; + } + + nrfx_qspi2_deactivate(&dev_config->qspi2); + + k_sem_give(&dev_data->ctx_lock); + + return 0; + } + + return -ENOTSUP; +} + +static int dev_init(const struct device *dev) +{ + struct mspi_sqspi_data *dev_data = dev->data; + const struct mspi_sqspi_config *dev_config = dev->config; + const struct gpio_dt_spec *ce_gpio; + static const nrfx_qspi2_cfg_t qspi2_cfg = { + .skip_gpio_cfg = true, + .skip_pmux_cfg = true, + }; + nrfx_qspi2_data_fmt_t qspi2_data_fmt = { + .cmd_bit_order = NRFX_QSPI2_DATA_FMT_BIT_ORDER_MSB_FIRST, + .addr_bit_order = NRFX_QSPI2_DATA_FMT_BIT_ORDER_MSB_FIRST, + .data_bit_order = NRFX_QSPI2_DATA_FMT_BIT_ORDER_MSB_FIRST, + .data_bit_reorder_unit = 0, + .data_container = 32, + .data_swap_unit = 8, + .data_padding = NRFX_QSPI2_DATA_FMT_PAD_RAW, + }; + int rc; + + k_sem_init(&dev_data->finished, 0, 1); + k_sem_init(&dev_data->ctx_lock, 1, 1); + k_sem_init(&dev_data->cfg_lock, 1, 1); + + dev_data->qspi2_dev_cfg.protocol = NRFX_QSPI2_PROTO_SPI_C; + dev_data->qspi2_dev_cfg.sample_sync = NRFX_QSPI2_SAMPLE_SYNC_DELAY; + dev_data->qspi2_dev_cfg.sample_delay_cyc = 1; + +#if defined(CONFIG_SOC_SERIES_NRF54LX) + nrf_oscillators_pll_freq_set(NRF_OSCILLATORS, + NRF_OSCILLATORS_PLL_FREQ_128M); + nrf_spu_periph_perm_secattr_set(NRF_SPU00, + nrf_address_slave_get(DT_REG_ADDR(DT_NODELABEL(cpuflpr_vpr))), + true); +#endif + + IRQ_CONNECT(DT_IRQN(VPR_NODE), DT_IRQ(VPR_NODE, priority), + nrfx_isr, nrfx_qspi2_irq_handler, 0); + + nrfx_qspi2_init(&dev_config->qspi2, &qspi2_cfg); + + nrfx_qspi2_dev_data_fmt_set(&dev_config->qspi2, &qspi2_data_fmt); + + 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 (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; + } + } + + 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, +}; + +#define FOREACH_CE_GPIOS_ELEM(inst) \ + DT_INST_FOREACH_PROP_ELEM_SEP(inst, ce_gpios, \ + GPIO_DT_SPEC_GET_BY_IDX, (,)) +#define MSPI_SQSPI_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 MSPI_SQSPI_INST(inst) \ + PM_DEVICE_DT_INST_DEFINE(inst, dev_pm_action_cb); \ + PINCTRL_DT_DEFINE(VPR_NODE); \ + static struct mspi_sqspi_data dev##inst##_data; \ + static const struct mspi_sqspi_config dev##inst##_config = { \ + .qspi2 = { \ + .p_reg = (void *)DT_INST_REG_ADDR(inst), \ + .drv_inst_idx = 0, \ + }, \ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(VPR_NODE), \ + .mem_reg = DMM_DEV_TO_REG(DT_DRV_INST(inst)), \ + IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, ce_gpios), \ + (MSPI_SQSPI_CE_GPIOS(inst))) \ + }; \ + 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); + +MSPI_SQSPI_INST(0); diff --git a/dts/bindings/mspi/nordic,nrf-sqspi.yaml b/dts/bindings/mspi/nordic,nrf-sqspi.yaml new file mode 100644 index 000000000000..61cbd48ff35b --- /dev/null +++ b/dts/bindings/mspi/nordic,nrf-sqspi.yaml @@ -0,0 +1,10 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +description: Nordic Software Defined QSPI (sQSPI) + +compatible: "nordic,nrf-sqspi" + +include: + - mspi-controller.yaml + - memory-region.yaml diff --git a/samples/zephyr/drivers/jesd216/CMakeLists.txt b/samples/zephyr/drivers/jesd216/CMakeLists.txt new file mode 100644 index 000000000000..5a6a1ba66710 --- /dev/null +++ b/samples/zephyr/drivers/jesd216/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(flash_jesd216) + +zephyr_library_include_directories(${ZEPHYR_BASE}/drivers/flash) + +target_sources(app PRIVATE ${ZEPHYR_BASE}/samples/drivers/jesd216/src/main.c) diff --git a/samples/zephyr/drivers/jesd216/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay b/samples/zephyr/drivers/jesd216/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay new file mode 100644 index 000000000000..d2d131c89070 --- /dev/null +++ b/samples/zephyr/drivers/jesd216/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay @@ -0,0 +1,93 @@ +&pinctrl { + sqspi_default: sqspi_default { + group1 { + psels = , + , + ; + nordic,drive-mode = ; + }; + group2 { + psels = , + , + ; + nordic,drive-mode = ; + bias-pull-up; + }; + }; + + sqspi_sleep: sqspi_sleep { + group1 { + low-power-enable; + psels = , + , + , + , + , + ; + }; + }; +}; + +&cpuflpr_vpr { + pinctrl-0 = <&sqspi_default>; + pinctrl-1 = <&sqspi_sleep>; + pinctrl-names = "default", "sleep"; + interrupts = <76 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + softperipheral_ram: memory@2003c000 { + reg = <0x2003c000 0x4000>; + ranges = <0 0x2003c000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + + sqspi: sqspi@3c00 { + compatible = "nordic,nrf-sqspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3c00 0x200>; + status = "okay"; + zephyr,pm-device-runtime-auto; + }; + }; + }; +}; + +/delete-node/ &mx25r64; + +&sqspi { + mx25r64: mx25r6435f@0 { + compatible = "mxicy,mx25r", "jedec,mspi-nor"; + status = "okay"; + reg = <0>; + jedec-id = [c2 28 17]; + quad-enable-requirements = "S1B6"; + sfdp-bfp = [ + e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44 + 30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff + ]; + size = <67108864>; + has-dpd; + t-enter-dpd = <10000>; + t-exit-dpd = <35000>; + t-reset-pulse = <10000>; + t-reset-recovery = <35000>; + + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_QUAD_1_4_4"; + 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/samples/zephyr/drivers/jesd216/prj.conf b/samples/zephyr/drivers/jesd216/prj.conf new file mode 100644 index 000000000000..b1b51a1e12fe --- /dev/null +++ b/samples/zephyr/drivers/jesd216/prj.conf @@ -0,0 +1,3 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y +CONFIG_FLASH_JESD216_API=y diff --git a/samples/zephyr/drivers/jesd216/sample.yaml b/samples/zephyr/drivers/jesd216/sample.yaml new file mode 100644 index 000000000000..031e6976efc6 --- /dev/null +++ b/samples/zephyr/drivers/jesd216/sample.yaml @@ -0,0 +1,20 @@ +sample: + name: JESD216 Sample +common: + tags: + - spi + - flash + harness: console + harness_config: + type: multi_line + ordered: true + regex: + - "sfdp-bfp =" + - "jedec-id =" +tests: + sample.drivers.spi.jesd216.sqspi: + filter: CONFIG_MSPI_SQSPI and dt_compat_enabled("jedec,mspi-nor") + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - DTC_OVERLAY_FILE="boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay" diff --git a/samples/zephyr/drivers/spi_flash/CMakeLists.txt b/samples/zephyr/drivers/spi_flash/CMakeLists.txt new file mode 100644 index 000000000000..465b211bd0e3 --- /dev/null +++ b/samples/zephyr/drivers/spi_flash/CMakeLists.txt @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + +cmake_minimum_required(VERSION 3.20.0) +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(spi_flash) + +target_sources(app PRIVATE ${ZEPHYR_BASE}/samples/drivers/spi_flash/src/main.c) diff --git a/samples/zephyr/drivers/spi_flash/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay b/samples/zephyr/drivers/spi_flash/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay new file mode 100644 index 000000000000..d2d131c89070 --- /dev/null +++ b/samples/zephyr/drivers/spi_flash/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay @@ -0,0 +1,93 @@ +&pinctrl { + sqspi_default: sqspi_default { + group1 { + psels = , + , + ; + nordic,drive-mode = ; + }; + group2 { + psels = , + , + ; + nordic,drive-mode = ; + bias-pull-up; + }; + }; + + sqspi_sleep: sqspi_sleep { + group1 { + low-power-enable; + psels = , + , + , + , + , + ; + }; + }; +}; + +&cpuflpr_vpr { + pinctrl-0 = <&sqspi_default>; + pinctrl-1 = <&sqspi_sleep>; + pinctrl-names = "default", "sleep"; + interrupts = <76 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + softperipheral_ram: memory@2003c000 { + reg = <0x2003c000 0x4000>; + ranges = <0 0x2003c000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + + sqspi: sqspi@3c00 { + compatible = "nordic,nrf-sqspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3c00 0x200>; + status = "okay"; + zephyr,pm-device-runtime-auto; + }; + }; + }; +}; + +/delete-node/ &mx25r64; + +&sqspi { + mx25r64: mx25r6435f@0 { + compatible = "mxicy,mx25r", "jedec,mspi-nor"; + status = "okay"; + reg = <0>; + jedec-id = [c2 28 17]; + quad-enable-requirements = "S1B6"; + sfdp-bfp = [ + e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44 + 30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff + ]; + size = <67108864>; + has-dpd; + t-enter-dpd = <10000>; + t-exit-dpd = <35000>; + t-reset-pulse = <10000>; + t-reset-recovery = <35000>; + + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_QUAD_1_4_4"; + 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/samples/zephyr/drivers/spi_flash/prj.conf b/samples/zephyr/drivers/spi_flash/prj.conf new file mode 100644 index 000000000000..d78e334e0fb6 --- /dev/null +++ b/samples/zephyr/drivers/spi_flash/prj.conf @@ -0,0 +1,2 @@ +CONFIG_STDOUT_CONSOLE=y +CONFIG_FLASH=y diff --git a/samples/zephyr/drivers/spi_flash/sample.yaml b/samples/zephyr/drivers/spi_flash/sample.yaml new file mode 100644 index 000000000000..7b4201885c8a --- /dev/null +++ b/samples/zephyr/drivers/spi_flash/sample.yaml @@ -0,0 +1,23 @@ +sample: + name: SPI Flash Sample +common: + tags: + - spi + - flash + harness: console + harness_config: + type: multi_line + ordered: true + regex: + - "Test 1: Flash erase" + - "Flash erase succeeded!" + - "Test 2: Flash write" + - "Attempting to write 4 bytes" + - "Data read matches data written. Good!!" +tests: + sample.drivers.spi.flash.sqspi: + filter: CONFIG_MSPI_SQSPI and dt_compat_enabled("jedec,mspi-nor") + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - DTC_OVERLAY_FILE="boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay" diff --git a/tests/zephyr/drivers/flash/common/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay b/tests/zephyr/drivers/flash/common/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay new file mode 100644 index 000000000000..d2d131c89070 --- /dev/null +++ b/tests/zephyr/drivers/flash/common/boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay @@ -0,0 +1,93 @@ +&pinctrl { + sqspi_default: sqspi_default { + group1 { + psels = , + , + ; + nordic,drive-mode = ; + }; + group2 { + psels = , + , + ; + nordic,drive-mode = ; + bias-pull-up; + }; + }; + + sqspi_sleep: sqspi_sleep { + group1 { + low-power-enable; + psels = , + , + , + , + , + ; + }; + }; +}; + +&cpuflpr_vpr { + pinctrl-0 = <&sqspi_default>; + pinctrl-1 = <&sqspi_sleep>; + pinctrl-names = "default", "sleep"; + interrupts = <76 NRF_DEFAULT_IRQ_PRIORITY>; + status = "okay"; +}; + +/ { + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + softperipheral_ram: memory@2003c000 { + reg = <0x2003c000 0x4000>; + ranges = <0 0x2003c000 0x4000>; + #address-cells = <1>; + #size-cells = <1>; + + sqspi: sqspi@3c00 { + compatible = "nordic,nrf-sqspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3c00 0x200>; + status = "okay"; + zephyr,pm-device-runtime-auto; + }; + }; + }; +}; + +/delete-node/ &mx25r64; + +&sqspi { + mx25r64: mx25r6435f@0 { + compatible = "mxicy,mx25r", "jedec,mspi-nor"; + status = "okay"; + reg = <0>; + jedec-id = [c2 28 17]; + quad-enable-requirements = "S1B6"; + sfdp-bfp = [ + e5 20 f1 ff ff ff ff 03 44 eb 08 6b 08 3b 04 bb + ee ff ff ff ff ff 00 ff ff ff 00 ff 0c 20 0f 52 + 10 d8 00 ff 23 72 f5 00 82 ed 04 cc 44 83 68 44 + 30 b0 30 b0 f7 c4 d5 5c 00 be 29 ff f0 d0 ff ff + ]; + size = <67108864>; + has-dpd; + t-enter-dpd = <10000>; + t-exit-dpd = <35000>; + t-reset-pulse = <10000>; + t-reset-recovery = <35000>; + + mspi-max-frequency = ; + mspi-io-mode = "MSPI_IO_MODE_QUAD_1_4_4"; + 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/tests/zephyr/drivers/flash/common/sysbuild.conf b/tests/zephyr/drivers/flash/common/sysbuild.conf index 05aa885118a3..69adb10f63b5 100644 --- a/tests/zephyr/drivers/flash/common/sysbuild.conf +++ b/tests/zephyr/drivers/flash/common/sysbuild.conf @@ -1,4 +1,2 @@ SB_CONFIG_VPR_LAUNCHER=n SB_CONFIG_PARTITION_MANAGER=n -SB_CONFIG_SDP=y -SB_CONFIG_SDP_MSPI=y diff --git a/tests/zephyr/drivers/flash/common/testcase.yaml b/tests/zephyr/drivers/flash/common/testcase.yaml index 7c50e8c53180..af6225314ec7 100644 --- a/tests/zephyr/drivers/flash/common/testcase.yaml +++ b/tests/zephyr/drivers/flash/common/testcase.yaml @@ -9,9 +9,22 @@ tests: - nrf54l15dk/nrf54l15/cpuapp integration_platforms: - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - SB_CONFIG_SDP=y + - SB_CONFIG_SDP_MSPI=y nrf.extended.drivers.flash.common.sdp.single: platform_allow: - nrf54l15dk/nrf54l15/cpuapp integration_platforms: - nrf54l15dk/nrf54l15/cpuapp - extra_args: EXTRA_DTC_OVERLAY_FILE="single.overlay" + extra_args: + - EXTRA_DTC_OVERLAY_FILE="single.overlay" + - SB_CONFIG_SDP=y + - SB_CONFIG_SDP_MSPI=y + nrf.extended.drivers.flash.common.sqspi: + platform_allow: + - nrf54l15dk/nrf54l15/cpuapp + integration_platforms: + - nrf54l15dk/nrf54l15/cpuapp + extra_args: + - DTC_OVERLAY_FILE="boards/nrf54l15dk_nrf54l15_cpuapp_sqspi.overlay" diff --git a/west.yml b/west.yml index 4f99c7102e5b..fe26280e05de 100644 --- a/west.yml +++ b/west.yml @@ -65,7 +65,7 @@ manifest: # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html - name: zephyr repo-path: sdk-zephyr - revision: 7e1daf0211b902f0aa49c47ec23746b60d750d55 + revision: 9ad6673058ccaee61f976bcb3d6d203be94bfdc7 import: # In addition to the zephyr repository itself, NCS also # imports the contents of zephyr/west.yml at the above