zephyr/drivers/mspi/mspi_dw.c
Andrzej Głąbek e78729609d drivers: mspi_dw: Add support for RX dummy cycles in single line mode
Support for 8 dummy cycles in a single line RX transaction is required
for the standard JEDEC Read SFDP command. The SSI controller does not
support dummy cycles in Standard SPI mode, but the driver can simulate
those by just sending a dummy data byte.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
2025-06-23 13:42:07 +02:00

1445 lines
38 KiB
C

/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT snps_designware_ssi
#include <zephyr/drivers/mspi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/sys/util.h>
#include "mspi_dw.h"
LOG_MODULE_REGISTER(mspi_dw, CONFIG_MSPI_LOG_LEVEL);
#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(risr, 0x34)
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
#include "mspi_dw_vendor_specific.h"
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) {
/* Set the threshold to 0 to get the next interrupt
* when the FIFO is completely emptied.
*/
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 tx_dummy_bytes(const struct device *dev)
{
struct mspi_dw_data *dev_data = dev->data;
const struct mspi_dw_config *dev_config = dev->config;
uint8_t fifo_room = dev_config->tx_fifo_depth_minus_1 + 1
- FIELD_GET(TXFLR_TXTFL_MASK, read_txflr(dev));
uint16_t dummy_bytes = dev_data->dummy_bytes;
const uint8_t dummy_val = 0;
if (dummy_bytes > fifo_room) {
dev_data->dummy_bytes = dummy_bytes - fifo_room;
do {
write_dr(dev, dummy_val);
} while (--fifo_room);
return false;
}
do {
write_dr(dev, dummy_val);
} while (--dummy_bytes);
/* Set the threshold to 0 to get the next interrupt when the FIFO is
* completely emptied.
*/
write_txftlr(dev, 0);
return true;
}
static bool 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->buf_pos = buf_pos;
return true;
}
}
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;
return false;
}
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];
bool finished = false;
if (packet->dir == MSPI_TX) {
if (dev_data->buf_pos < dev_data->buf_end) {
tx_data(dev, packet);
} else {
/* 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) {
}
finished = true;
}
} else {
uint32_t int_status = read_isr(dev);
do {
if (int_status & ISR_RXFIS_BIT) {
if (read_rx_fifo(dev, packet)) {
finished = true;
break;
}
if (read_risr(dev) & RISR_RXOIR_BIT) {
finished = true;
break;
}
int_status = read_isr(dev);
}
if (int_status & ISR_TXEIS_BIT) {
if (tx_dummy_bytes(dev)) {
write_imr(dev, IMR_RXFIM_BIT);
}
int_status = read_isr(dev);
}
} while (int_status);
}
if (finished) {
write_imr(dev, 0);
k_sem_give(&dev_data->finished);
}
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 %u not supported", cmd_length);
return false;
}
return true;
}
static bool apply_addr_length(struct mspi_dw_data *dev_data,
uint32_t addr_length)
{
if (addr_length > 4) {
LOG_ERR("Address length %u not supported", addr_length);
return false;
}
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 %u 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;
if (addr_length > 4) {
LOG_ERR("Address length %u not supported", addr_length);
return false;
}
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;
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->bytes_to_discard = 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);
} 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;
uint32_t dummy_cycles = dev_data->xfer.rx_dummy;
dev_data->bytes_to_discard = dev_data->xfer.cmd_length
+ dev_data->xfer.addr_length
+ dummy_cycles / 8;
rx_total_bytes = dev_data->bytes_to_discard
+ packet->num_bytes;
dev_data->dummy_bytes = dummy_cycles / 8
+ packet->num_bytes;
imr = IMR_TXEIM_BIT | IMR_RXFIM_BIT;
tmod = CTRLR0_TMOD_TX_RX;
/* 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;
rx_fifo_threshold = MIN(packet_frames - 1,
dev_config->rx_fifo_threshold);
dev_data->spi_ctrlr0 |=
FIELD_PREP(SPI_CTRLR0_WAIT_CYCLES_MASK,
dev_data->xfer.rx_dummy);
}
dev_data->ctrlr0 |= FIELD_PREP(CTRLR0_TMOD_MASK, tmod);
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];
/* Set the TX FIFO threshold and its transmit start level. */
if (packet->num_bytes) {
/* If there is some data to send/receive, set the threshold to
* the value configured for the driver instance and the start
* level to the maximum possible value (it will be updated later
* in tx_fifo() or tx_dummy_bytes() when TX is to be finished).
* This helps avoid a situation when the TX FIFO becomes empty
* before the transfer is complete and the SSI core finishes the
* transaction and deactivates the CE line. This could occur
* right before the data phase in enhanced SPI modes, when the
* clock stretching feature does not work yet, or in Standard
* SPI mode, where the clock stretching is not available at all.
*/
write_txftlr(dev, FIELD_PREP(TXFTLR_TXFTHR_MASK,
dev_config->tx_fifo_depth_minus_1) |
FIELD_PREP(TXFTLR_TFT_MASK,
dev_config->tx_fifo_threshold));
} else {
uint32_t total_tx_entries = 0;
/* It the whole transfer is to contain only the command and/or
* address, set up the transfer to start right after entries
* for those appear in the TX FIFO, and the threshold to 0,
* so that the interrupt occurs when the TX FIFO gets emptied.
*/
if (dev_data->xfer.cmd_length) {
if (dev_data->standard_spi) {
total_tx_entries += dev_data->xfer.cmd_length;
} else {
total_tx_entries += 1;
}
}
if (dev_data->xfer.addr_length) {
if (dev_data->standard_spi) {
total_tx_entries += dev_data->xfer.addr_length;
} else {
total_tx_entries += 1;
}
}
write_txftlr(dev, FIELD_PREP(TXFTLR_TXFTHR_MASK,
total_tx_entries - 1));
}
/* 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);
/* Since the FIFO depth in SSI is always at least 8, it can be safely
* assumed that the command and address fields (max. 2 and 4 bytes,
* respectively) can be written here before the TX FIFO gets filled up.
*/
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);
}
}
/* Enable interrupts now and wait until the packet is done. */
write_imr(dev, imr);
rc = k_sem_take(&dev_data->finished, timeout);
if (read_risr(dev) & RISR_RXOIR_BIT) {
LOG_ERR("RX FIFO overflow occurred");
rc = -EIO;
} else if (rc < 0) {
LOG_ERR("Transfer timed out");
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_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) {
if (req->tx_dummy) {
LOG_ERR("TX dummy cycles unsupported in single line mode");
return -EINVAL;
}
if (req->rx_dummy % 8) {
LOG_ERR("Unsupported RX (%u) dummy cycles", req->rx_dummy);
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 > XIP_CTRL_WAIT_CYCLES_MAX ||
params->tx_dummy > XIP_WRITE_CTRL_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)