diff --git a/drivers/mspi/Kconfig.dw b/drivers/mspi/Kconfig.dw index 06d2e09c8af..cff148c138c 100644 --- a/drivers/mspi/Kconfig.dw +++ b/drivers/mspi/Kconfig.dw @@ -12,6 +12,15 @@ config MSPI_DW if MSPI_DW +config MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE + bool "Handle FIFO in system workqueue" + help + When the driver does not use DMA for transferring data to/from the + SSI FIFOs, handling of those may take a significant amount of time. + It may be destructive for some applications if that time is spent + in the interrupt context. This option allows offloading the job to + the system workqueue. + config MSPI_DW_TXD_DIV int "Designware SSI TX Drive edge divisor" default 4 diff --git a/drivers/mspi/mspi_dw.c b/drivers/mspi/mspi_dw.c index 66f257839aa..f1195314c6c 100644 --- a/drivers/mspi/mspi_dw.c +++ b/drivers/mspi/mspi_dw.c @@ -71,6 +71,12 @@ struct mspi_dw_data { /* For locking of controller configuration. */ struct k_sem cfg_lock; struct mspi_xfer xfer; + +#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE) + struct k_work fifo_work; + const struct device *dev; + uint32_t imr; +#endif }; struct mspi_dw_config { @@ -111,7 +117,7 @@ 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_WR(imr, 0x2c) DEFINE_MM_REG_RD(isr, 0x30) DEFINE_MM_REG_RD(risr, 0x34) DEFINE_MM_REG_RD_WR(dr, 0x60) @@ -218,6 +224,8 @@ static bool tx_dummy_bytes(const struct device *dev) write_dr(dev, dummy_val); } while (--dummy_bytes); + dev_data->dummy_bytes = 0; + /* Set the TX start level to 0, so that the transmission will be * started now if it hasn't been yet. The threshold value is also * set to 0 here, but it doesn't really matter, as the interrupt @@ -280,7 +288,18 @@ static bool read_rx_fifo(const struct device *dev, return false; } -static void mspi_dw_isr(const struct device *dev) +static inline void set_imr(const struct device *dev, uint32_t imr) +{ +#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE) + struct mspi_dw_data *dev_data = dev->data; + + dev_data->imr = imr; +#else + write_imr(dev, imr); +#endif +} + +static void handle_fifos(const struct device *dev) { struct mspi_dw_data *dev_data = dev->data; const struct mspi_xfer_packet *packet = @@ -302,42 +321,82 @@ static void mspi_dw_isr(const struct device *dev) finished = true; } } else { - uint32_t int_status = read_isr(dev); + for (;;) { + /* Use RISR, not ISR, because when this function is + * executed through the system workqueue, all interrupts + * are masked (IMR is 0). + */ + uint32_t int_status = read_risr(dev); - do { - if (int_status & ISR_RXFIS_BIT) { + if (int_status & RISR_RXFIR_BIT) { if (read_rx_fifo(dev, packet)) { finished = true; break; } - if (read_risr(dev) & RISR_RXOIR_BIT) { + if (int_status & RISR_RXOIR_BIT) { finished = true; break; } - int_status = read_isr(dev); + /* Refresh interrupt status, as during reading + * from the RX FIFO, the TX FIFO status might + * have changed. + */ + int_status = read_risr(dev); } - if (int_status & ISR_TXEIS_BIT) { - if (tx_dummy_bytes(dev)) { - /* All the required dummy bytes were - * written to the FIFO; disable the TXE - * interrupt, as it's no longer needed. - */ - write_imr(dev, IMR_RXFIM_BIT); - } + if (dev_data->dummy_bytes == 0 || + !(int_status & RISR_TXEIR_BIT)) { + break; + } - int_status = read_isr(dev); + if (tx_dummy_bytes(dev)) { + /* All the required dummy bytes were written + * to the FIFO; disable the TXE interrupt, + * as it's no longer needed. + */ + set_imr(dev, IMR_RXFIM_BIT); } - } while (int_status != 0); + } } if (finished) { - write_imr(dev, 0); + set_imr(dev, 0); k_sem_give(&dev_data->finished); } +} + +#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE) +static void fifo_work_handler(struct k_work *work) +{ + struct mspi_dw_data *dev_data = + CONTAINER_OF(work, struct mspi_dw_data, fifo_work); + const struct device *dev = dev_data->dev; + + handle_fifos(dev); + + write_imr(dev, dev_data->imr); +} +#endif + +static void mspi_dw_isr(const struct device *dev) +{ +#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE) + struct mspi_dw_data *dev_data = dev->data; + int rc; + + dev_data->imr = read_imr(dev); + write_imr(dev, 0); + + rc = k_work_submit(&dev_data->fifo_work); + if (rc < 0) { + LOG_ERR("k_work_submit failed: %d\n", rc); + } +#else + handle_fifos(dev); +#endif vendor_specific_irq_clear(dev); } @@ -1426,6 +1485,11 @@ static int dev_init(const struct device *dev) k_sem_init(&dev_data->cfg_lock, 1, 1); k_sem_init(&dev_data->ctx_lock, 1, 1); +#if defined(CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE) + dev_data->dev = dev; + k_work_init(&dev_data->fifo_work, fifo_work_handler); +#endif + for (ce_gpio = dev_config->ce_gpios; ce_gpio < &dev_config->ce_gpios[dev_config->ce_gpios_len]; ce_gpio++) { diff --git a/tests/drivers/mspi/flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/mspi/flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000..6e0d84e07cd --- /dev/null +++ b/tests/drivers/mspi/flash/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + aliases { + mspi0 = &exmif; + }; +}; + +&gpio6 { + status = "okay"; +}; + +&exmif { + status = "okay"; +}; + +&mx25uw63 { + status = "okay"; +}; diff --git a/tests/drivers/mspi/flash/testcase.yaml b/tests/drivers/mspi/flash/testcase.yaml index 72755fd2aa4..dba610a4023 100644 --- a/tests/drivers/mspi/flash/testcase.yaml +++ b/tests/drivers/mspi/flash/testcase.yaml @@ -1,16 +1,18 @@ # Copyright (c) 2024 Ambiq Micro Inc. # SPDX-License-Identifier: Apache-2.0 +common: + tags: + - drivers + - mspi + - flash + harness: ztest + tests: drivers.mspi.flash: - tags: - - drivers - - mspi - - flash filter: dt_compat_enabled("zephyr,mspi-emul-flash") or dt_compat_enabled("jedec,spi-nor") or dt_compat_enabled("jedec,mspi-nor") or dt_compat_enabled("mspi-atxp032") or dt_compat_enabled("mspi-is25xX0xx") - harness: ztest platform_allow: - native_sim - apollo3p_evb @@ -20,15 +22,17 @@ tests: - apollo3p_evb - apollo510_evb drivers.mspi.flash.xip_read: - tags: - - drivers - - mspi - - flash filter: dt_compat_enabled("mspi-is25xX0xx") - harness: ztest platform_allow: - apollo510_evb integration_platforms: - apollo510_evb extra_configs: - CONFIG_FLASH_MSPI_XIP_READ=y + drivers.mspi.flash.mspi_dw_system_workqueue: + filter: dt_alias_exists("mspi0") and dt_compat_enabled("jedec,mspi-nor") + and CONFIG_MSPI_DW + integration_platforms: + - nrf54h20dk/nrf54h20/cpuapp + extra_configs: + - CONFIG_MSPI_DW_HANDLE_FIFOS_IN_SYSTEM_WORKQUEUE=y