Repository: zephyrproject-rtos/zephyr
Pull request #89122: Microchip MEC5 HAL based SPI driver for QSPI controller
Author: login: scottwcpg
Labels: name: area: SPI
description: SPI bus
name: platform: Microchip MEC
description: Microchip MEC Platform
Created: 2025-04-25T21:50:18Z | Merged: 2025-05-16T19:36:50Z

Microchip MEC5 HAL based SPI driver using QSPI controller.   Driver supports async and extended SPI.  Driver is compatible with flash SPI_NOR driver.  PR includes a commit adding the board to the SPI loopback test.


Diff:
diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt
index b2eb407f9b7b2..6ed97a943ee4a 100644
--- a/drivers/spi/CMakeLists.txt
+++ b/drivers/spi/CMakeLists.txt
@@ -37,6 +37,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_DSPI spi_mcux_dspi.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_ECSPI spi_mcux_ecspi.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_FLEXCOMM spi_mcux_flexcomm.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_MCUX_FLEXIO spi_mcux_flexio.c)
+zephyr_library_sources_ifdef(CONFIG_SPI_MEC5_QSPI spi_mchp_mec5_qspi.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_NPCX_SPIP spi_npcx_spip.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPI spi_nrfx_spi.c spi_nrfx_common.c)
 zephyr_library_sources_ifdef(CONFIG_SPI_NRFX_SPIM spi_nrfx_spim.c spi_nrfx_common.c)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 7c3aaca724892..5a59e0a765cdd 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -115,6 +115,7 @@ source "drivers/spi/Kconfig.mcux_dspi"
 source "drivers/spi/Kconfig.mcux_ecspi"
 source "drivers/spi/Kconfig.mcux_flexcomm"
 source "drivers/spi/Kconfig.mcux_flexio"
+source "drivers/spi/Kconfig.mec5"
 source "drivers/spi/Kconfig.npcx"
 source "drivers/spi/Kconfig.nrfx"
 source "drivers/spi/Kconfig.numaker"
diff --git a/drivers/spi/Kconfig.mec5 b/drivers/spi/Kconfig.mec5
new file mode 100644
index 0000000000000..26e2e8b44ea16
--- /dev/null
+++ b/drivers/spi/Kconfig.mec5
@@ -0,0 +1,12 @@
+# Microchip MEC5 QSPI
+
+# Copyright (c) 2025 Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+config SPI_MEC5_QSPI
+	bool "Microchip MEC5 QSPI driver"
+	default y
+	depends on DT_HAS_MICROCHIP_MEC5_QSPI_ENABLED
+	select PINCTRL
+	help
+	  Enable support for Microchip MEC5 QSPI driver.
diff --git a/drivers/spi/spi_mchp_mec5_qspi.c b/drivers/spi/spi_mchp_mec5_qspi.c
new file mode 100644
index 0000000000000..9dd9175692148
--- /dev/null
+++ b/drivers/spi/spi_mchp_mec5_qspi.c
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/dt-bindings/spi/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	const struct mec5_spi_devices *child_devices;
+	uint8_t num_child_devices;
+	uint8_t ovrc;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if ((config->operation &
+	     (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | SPI_HALF_DUPLEX)) != 0) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if ((config->operation & SPI_CS_ACTIVE_HIGH) != 0) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if ((data->operation & SPI_MODE_CPHA) != 0) {
+		sgm |= BIT(0);
+	}
+	if ((data->operation & SPI_MODE_CPOL) != 0) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if ((data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) != 0) {
+		return -EBUSY;
+	}
+
+	if ((tx_bufs == NULL) && (rx_bufs == NULL)) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret != 0) {
+		goto do_xfr_exit;
+	}
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	data->chunk_size = 0;
+	data->total_tx_size = spi_context_total_tx_len(ctx);
+	data->total_rx_size = spi_context_total_rx_len(ctx);
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_START;
+
+	/* trigger an empty TX FIFO interrupt to enter the ISR */
+	mec_hal_qspi_intr_ctrl_msk(regs, 1u, MEC_QSPI_IEN_TXB_EMPTY);
+
+	ret = spi_context_wait_for_completion(ctx);
+
+	if (async && (ret == 0)) {
+		return 0;
+	}
+
+	if (ret != 0) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated() == true) {
+		return -EPERM;
+	}
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret != 0) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret != 0) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated() == true) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* ISR helper */
+static void mec5_qspi_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size != 0) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size != 0) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if ((spi_context_rx_on(ctx) == true) || (spi_context_tx_on(ctx) == true)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb != NULL) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb != NULL) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	if (status == MEC_RET_ERR_HW) {
+		spi_context_complete(&data->ctx, dev, -EIO);
+		return;
+	}
+
+	if ((data->xfr_flags & MEC5_QSPI_XFR_FLAG_START) != 0) {
+		data->xfr_flags &= (uint32_t)~MEC5_QSPI_XFR_FLAG_START;
+	}
+
+	mec5_qspi_ctx_next(dev);
+}
+
+/*
+ * Called for each QSPI controller by the kernel during driver load phase
+ * specified in the device initialization structure below.
+ * Initialize QSPI controller.
+ * Initialize SPI context.
+ * QSPI will be fully configured and enabled when the transceive API
+ * is called.
+ */
+static int mec5_qspi_init(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	enum mec_qspi_cs cs = MEC_QSPI_CS_0;
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+	enum mec_qspi_signal_mode spi_mode = MEC_SPI_SIGNAL_MODE_0;
+	int ret = 0;
+
+	data->cs = 0;
+
+	ret = mec_hal_qspi_init(regs, (uint32_t)devcfg->clock_freq, spi_mode, iom, cs);
+	if (ret != MEC_RET_OK) {
+		LOG_ERR("QSPI init error (%d)", ret);
+		return -EINVAL;
+	}
+
+	data->freq = devcfg->clock_freq;
+	data->operation = SPI_WORD_SET(8) | SPI_LINES_SINGLE;
+	mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+
+	ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
+	if (ret != 0) {
+		LOG_ERR("QSPI pinctrl setup failed (%d)", ret);
+	}
+
+	ret = spi_context_cs_configure_all(&data->ctx);
+	if (ret != 0) {
+		LOG_ERR("QSPI cs config failed (%d)", ret);
+		return ret;
+	}
+
+	if ((devcfg->irq_config_func) != NULL) {
+		devcfg->irq_config_func();
+	}
+
+	spi_context_unlock_unconditionally(&data->ctx);
+
+	return ret;
+}
+
+static DEVICE_API(spi, mec5_qspi_driver_api) = {
+	.transceive = mec5_qspi_xfr_sync,
+#ifdef CONFIG_SPI_ASYNC
+	.transceive_async = mec5_qspi_xfr_async,
+#endif
+	.release = mec5_qspi_release,
+};
+
+#define MEC5_QSPI_CS_TIMING_VAL(a, b, c, d)                                                        \
+	(((a) & 0xFu) | (((b) & 0xFu) << 8) | (((c) & 0xFu) << 16) | (((d) & 0xFu) << 24))
+
+#define MEC5_QSPI_CS_TMV(node_id)                                                                  \
+	MEC5_QSPI_CS_TIMING_VAL(DT_PROP_OR(node_id, dcsckon, 6), DT_PROP_OR(node_id, dckcsoff, 4), \
+				DT_PROP_OR(node_id, dldh, 6), DT_PROP_OR(node_id, dcsda, 6))
+
+#define MEC5_QSPI_IRQ_HANDLER_FUNC(id) .irq_config_func = mec5_qspi_irq_config_##id,
+
+#define MEC5_QSPI_IRQ_HANDLER_CFG(id)                                                              \
+	static void mec5_qspi_irq_config_##id(void)                                                \
+	{                                                                                          \
+		IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), mec5_qspi_isr,            \
+			    DEVICE_DT_INST_GET(id), 0);                                            \
+		irq_enable(DT_INST_IRQN(id));                                                      \
+	}
+
+#define MEC5_QSPI_CHILD_FLAGS(node_id)                                                             \
+	((DT_PROP_OR(node_id, spi_cpol, 0) & 0x1u) |                                               \
+	 ((DT_PROP_OR(node_id, spi_cpha, 0) & 0x1u) << 1))
+
+#define MEC5_QSPI_CHILD_INFO(node_id)                                                              \
+	{                                                                                          \
+		.cs_timing = MEC5_QSPI_CS_TMV(node_id),                                            \
+		.cs = (uint8_t)(DT_REG_ADDR(node_id) & 0xffu),                                     \
+		.sck_tap = (uint8_t)(DT_PROP_OR(node_id, clock_tap, 0)),                           \
+		.ctrl_tap = (uint8_t)(DT_PROP_OR(node_id, ctrl_tap, 0)),                           \
+		.flags = MEC5_QSPI_CHILD_FLAGS(node_id),                                           \
+	},
+
+#define MEC5_QSPI_CHILD_DEVS(i)                                                                    \
+	static const struct mec5_spi_devices mec5_qspi_children_##i[] = {                          \
+		DT_INST_FOREACH_CHILD_STATUS_OKAY(i, MEC5_QSPI_CHILD_INFO)}
+
+/* The instance number, i is not related to block ID's rather the
+ * order the DT tools process all DT files in a build.
+ */
+#define MEC5_QSPI_DEVICE(i)                                                                        \
+	PINCTRL_DT_INST_DEFINE(i);                                                                 \
+	MEC5_QSPI_CHILD_DEVS(i);                                                                   \
+	MEC5_QSPI_IRQ_HANDLER_CFG(i)                                                               \
+                                                                                                   \
+	static struct mec5_qspi_data mec5_qspi_data_##i = {                                        \
+		SPI_CONTEXT_INIT_LOCK(mec5_qspi_data_##i, ctx),                                    \
+		SPI_CONTEXT_INIT_SYNC(mec5_qspi_data_##i, ctx),                                    \
+	};                                                                                         \
+	static const struct mec5_qspi_config mec5_qspi_config_##i = {                              \
+		.regs = (struct mec_qspi_regs *)DT_INST_REG_ADDR(i),                               \
+		.clock_freq = DT_INST_PROP_OR(i, clock_frequency, MHZ(12)),                        \
+		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i),                                         \
+		.ovrc = DT_INST_PROP_OR(i, overrun_character, 0),                                  \
+		MEC5_QSPI_IRQ_HANDLER_FUNC(i).child_devices = mec5_qspi_children_##i,              \
+		.num_child_devices = ARRAY_SIZE(mec5_qspi_children_##i),                           \
+	};                                                                                         \
+	DEVICE_DT_INST_DEFINE(i, &mec5_qspi_init, NULL, &mec5_qspi_data_##i,                       \
+			      &mec5_qspi_config_##i, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY,        \
+			      &mec5_qspi_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(MEC5_QSPI_DEVICE)
diff --git a/dts/arm/microchip/mec5.dtsi b/dts/arm/microchip/mec5.dtsi
index e37e86343d167..e21e470a67654 100644
--- a/dts/arm/microchip/mec5.dtsi
+++ b/dts/arm/microchip/mec5.dtsi
@@ -471,7 +471,6 @@
 		qspi0: spi@40070000 {
 			reg = <0x40070000 0x400>;
 			interrupts = <91 2>;
-			clock-frequency = <12000000>;
 			#address-cells = <1>;
 			#size-cells = <0>;
 			status = "disabled";
diff --git a/dts/bindings/spi/microchip,mec5-qspi-device.yaml b/dts/bindings/spi/microchip,mec5-qspi-device.yaml
new file mode 100644
index 0000000000000..c7ea4a6382443
--- /dev/null
+++ b/dts/bindings/spi/microchip,mec5-qspi-device.yaml
@@ -0,0 +1,44 @@
+# Copyright (c) 2025, Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+# Microchip QSPI controller SPI additional timing setting specific to SPI flash devices.
+
+include: [spi-device.yaml]
+
+properties:
+  dcsckon:
+    type: int
+    description: |
+      Delay in QSPI main clocks from CS# assertion to first clock edge.
+      If not present use hardware default value. Refer to chip documention
+      for QMSPI input clock frequency.
+
+  dckcsoff:
+    type: int
+    description: |
+      Delay in QSPI main clocks from last clock edge to CS# de-assertion.
+      If not presetn use hardware default value. Refer to chip documention
+      for QSPI input clock frequency.
+
+  dldh:
+    type: int
+    description: |
+      Delay in QSPI main clocks from CS# de-assertion to driving HOLD#
+      and WP#. If not present use hardware default value. Refer to chip
+      documention for QSPI input clock frequency.
+
+  dcsda:
+    type: int
+    description: |
+      Delay in QSPI main clocks from CS# de-assertion to CS# assertion.
+      If not present use hardware default value. Refer to chip documention
+      for QSPI input clock frequency.
+
+  ctrl-tap:
+    type: int
+    description: Select control tap point. Values are in [0, 255].
+
+  clock-tap:
+    type: int
+    description: |
+      Select clock tap point to offset any off chip delays. Values are in [0, 255].
diff --git a/dts/bindings/spi/microchip,mec5-qspi.yaml b/dts/bindings/spi/microchip,mec5-qspi.yaml
new file mode 100644
index 0000000000000..b7cff7fb8d9bf
--- /dev/null
+++ b/dts/bindings/spi/microchip,mec5-qspi.yaml
@@ -0,0 +1,18 @@
+# Copyright (c) 2024, Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+description: Microchip MEC5 series QSPI controller
+
+compatible: "microchip,mec5-qspi"
+
+include: [spi-controller.yaml, pinctrl-device.yaml]
+
+properties:
+  reg:
+    required: true
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.conf b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.conf
new file mode 100644
index 0000000000000..2acf1d31498c4
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.conf
@@ -0,0 +1 @@
+CONFIG_SPI_ASYNC=y
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.overlay b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.overlay
new file mode 100644
index 0000000000000..1cfdb4d08b36d
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.overlay
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+
+	fast@0 {
+		compatible = "test-spi-loopback-fast";
+		reg = <0>;
+		spi-max-frequency = <16000000>;
+	};
+};
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.conf b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.conf
new file mode 100644
index 0000000000000..2acf1d31498c4
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.conf
@@ -0,0 +1 @@
+CONFIG_SPI_ASYNC=y
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.overlay b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.overlay
new file mode 100644
index 0000000000000..1cfdb4d08b36d
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.overlay
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+
+	fast@0 {
+		compatible = "test-spi-loopback-fast";
+		reg = <0>;
+		spi-max-frequency = <16000000>;
+	};
+};
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.conf b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.conf
new file mode 100644
index 0000000000000..2acf1d31498c4
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.conf
@@ -0,0 +1 @@
+CONFIG_SPI_ASYNC=y
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.overlay b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.overlay
new file mode 100644
index 0000000000000..1cfdb4d08b36d
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qlj.overlay
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+
+	fast@0 {
+		compatible = "test-spi-loopback-fast";
+		reg = <0>;
+		spi-max-frequency = <16000000>;
+	};
+};
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.conf b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.conf
new file mode 100644
index 0000000000000..2acf1d31498c4
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.conf
@@ -0,0 +1 @@
+CONFIG_SPI_ASYNC=y
diff --git a/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.overlay b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.overlay
new file mode 100644
index 0000000000000..1cfdb4d08b36d
--- /dev/null
+++ b/tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1753_qsz.overlay
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+
+	fast@0 {
+		compatible = "test-spi-loopback-fast";
+		reg = <0>;
+		spi-max-frequency = <16000000>;
+	};
+};


Reviews:
user: login: nordicjm
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T06:13:34Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T15:46:11Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T15:46:22Z
user: login: tbursztyka
body: spi api does not support dual/quad/octal. Yes there are config options for it, but since it does not provide any means to actually configure everything else around ... I made a proposal to get a proper support long ago (see #39991 ) but it never caught any traction. It was assumed to be unnecessary. No one came since to request it, and instead we got a completely different api (mspi) competing with spi api... 

so drop the dual/quad/octal for now, focus on the generic, and see my comments on the overall logic.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-28T19:46:26Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T17:15:35Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T17:18:03Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-01T17:18:06Z
user: login: pdgendt
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T09:19:54Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T13:11:37Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T13:16:00Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T13:16:12Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-08T13:17:00Z
user: login: pdgendt
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-10T13:28:45Z
user: login: pdgendt
body: Some more implicit booleans.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T18:41:05Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T20:35:05Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T20:35:21Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T20:35:53Z
user: login: tbursztyka
body: just a tiny comment, rest lgtm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T18:04:39Z
user: login: scottwcpg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T19:28:12Z
user: login: tbursztyka
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T19:40:18Z
user: login: albertofloyd
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-16T17:26:08Z

Review comments:
diff_hunk: @@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+	lines = <1>;
+	chip-select = <0>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+	fast@0 {
path: tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.overlay
user: login: nordicjm
body: newline between child nodes
created_at: 2025-04-28T06:13:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+	lines = <1>;
+	chip-select = <0>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+	fast@0 {
path: tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.overlay
user: login: nordicjm
body: fix in whole PR
created_at: 2025-04-28T06:13:30Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+	lines = <1>;
+	chip-select = <0>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+	fast@0 {
path: tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qlj.overlay
user: login: scottwcpg
body: fixed and pushed
created_at: 2025-04-28T15:46:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/ {
+	aliases {
+		spi0 = &qspi0;
+	};
+};
+
+/* Loopback requires wire connecting qspi_shd_io0_gpio223 to qspi_shd_io1_gpio224 */
+
+&qspi0 {
+	status = "okay";
+	compatible = "microchip,mec5-qspi";
+	clock-frequency = <12000000>;
+	lines = <1>;
+	chip-select = <0>;
+
+	pinctrl-0 = < &qspi_shd_cs0_n_gpio055 &qspi_shd_clk_gpio056
+		      &qspi_shd_io0_gpio223 &qspi_shd_io1_gpio224 >;
+	pinctrl-names = "default";
+
+	slow@0 {
+		compatible = "test-spi-loopback-slow";
+		reg = <0>;
+		spi-max-frequency = <500000>;
+	};
+	fast@0 {
path: tests/drivers/spi/spi_loopback/boards/mec_assy6941_mec1743_qsz.overlay
user: login: scottwcpg
body: fixed and pushed
created_at: 2025-04-28T15:46:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: tbursztyka
body: If properly interrupt based, this while loop would not be needed. Here it's creating context switches back to caller's thread to fill in the controller with data, but this should be done in isr mode. So you'll need to revise the logic here.

Basically the common logic is:
- you configure
- you fire the interrupt handling
- you wait (on spi_context_wait_for_completion()) 
Rest: filling in and reading out the fifos is done while handling the interrupts.
created_at: 2025-04-28T19:37:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+		qflags = xflags;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		txlen += xlen;
+		rxlen += xlen;
+		if (!hold_cs) {
+			if ((txlen >= total_tx) && (rxlen >= total_rx)) {
+				qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+			}
+		}
+
+		if (xlen) {
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+			ret = mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+			if (ret != MEC_RET_OK) {
+				ret = -EIO;
+				break;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			ret = spi_context_wait_for_completion(ctx);
+			if (ret) {
+				break;
+			}
+		}
+
+		spi_context_update_tx(ctx, 1u, xlen);
+		spi_context_update_rx(ctx, 1u, xlen);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_SPI_EXTENDED_MODES
+/* MEC5 QSPI controller maximum number of lines is 4. In dual and quad modes all lines can only be
+ * one direction at a time (half-duplex). Other SPI drivers which support dual and quad process
+ * SPI buffers as (tx, rx) pairs. Therefore: if tx then do transmit, wait for done then if rx
+ * do rx.
+ */
+static int mec5_qspi_fdq(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			 const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	uint32_t qflags = 0, xflags = 0, qstatus = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_IOM_SET(data->iom);
+	xflags |= MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	data->rxb = NULL;
+	data->txb = NULL;
+	data->rxcnt = 0;
+	data->txcnt = 0;
+
+	if (rx_bufs) {
+		data->rxb = rx_bufs->buffers;
+		data->rxcnt = rx_bufs->count;
+	}
+
+	if (tx_bufs) {
+		data->txb = tx_bufs->buffers;
+		data->txcnt = tx_bufs->count;
+	}
+
+	spi_context_cs_control(ctx, true);
+
+	while ((data->txcnt != 0) || (data->rxcnt != 0)) {
+		uint8_t *rxbuf = NULL;
+		uint8_t const *txbuf = NULL;
+		size_t rxlen = 0, txlen = 0;
+
+		if (data->txb) {
+			txbuf = data->txb->buf;
+			txlen = data->txb->len;
+		}
+
+		if (data->rxb) {
+			rxbuf = data->rxb->buf;
+			rxlen = data->rxb->len;
+		}
+
+		qflags = xflags;
+
+		if (rxlen || txlen) {
+			if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+				if (!hold_cs) {
+					qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+				}
+			}
+
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+			ret = mec_hal_qspi_uldma(regs, txbuf, txlen, rxbuf, rxlen, qflags);
+			if (ret != MEC_RET_OK) {
+				return -EIO;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			k_sem_take(&ctx->sync, K_FOREVER);
+		}
+
+		if (data->rxcnt) {
+			data->rxcnt--;
+		}
+
+		if (data->rxcnt && data->rxb) {
+			data->rxb++;
+		} else {
+			data->rxb = NULL;
+		}
+
+		if (data->txcnt) {
+			data->txcnt--;
+		}
+
+		if (data->txcnt && data->txb) {
+			data->txb++;
+		} else {
+			data->txb = NULL;
+		}
+	}
+
+	if (!hold_cs) {
+		qstatus = mec_hal_qspi_hw_status(regs);
+		if ((qstatus & MEC_QSPI_STS_ACTIVE) || !(qflags & MEC5_QSPI_ULDMA_FLAG_CLOSE)) {
+			mec_hal_qspi_force_stop(regs);
+		}
+	}
+
+	return ret;
+}
+#endif /* CONFIG_SPI_EXTENDED_MODES */
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *devdat = dev->data;
+	struct spi_context *ctx = &devdat->ctx;
+	int ret = 0;
+
+	if (devdat->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	if (devdat->iom == MEC_QSPI_IO_FULL_DUPLEX) {
+		ret = mec5_qspi_fd(dev, tx_bufs, rx_bufs, async);
+	} else {
+#ifdef CONFIG_SPI_EXTENDED_MODES
+		ret = mec5_qspi_fdq(dev, tx_bufs, rx_bufs, async);
+#else
+		ret = -ENOTSUP;
+		goto do_xfr_exit;
+#endif
+	}
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_cs_control(ctx, false);
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_SPI_ASYNC
+
+/* Async for non-fast path */
+static void mec5_qspi_async_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+/* Async for fast path */
+static void mec5_qspi_async_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t xflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	if (!(data->xfr_flags & MEC5_QSPI_XFR_FLAG_FAST)) {
+		return mec5_qspi_async_ctx_next(dev);
+	}
+
+	if (data->rxcnt) {
+		data->rxcnt--;
+		data->rxb++;
+	}
+	if (data->txcnt) {
+		data->txcnt--;
+		data->txb++;
+	}
+
+	if (!data->rxcnt && !data->txcnt) {
+		spi_context_complete(&data->ctx, dev, 0);
+		return;
+	}
+
+	if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+		xflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+	}
+
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+	mec_hal_qspi_uldma_fd(regs, data->txb->buf, data->txb->len, data->rxb->buf, data->rxb->len,
+			      xflags);
+}
+#endif /* CONFIG_SPI_ASYNC */
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	status = (status == MEC_RET_OK) ? 0 : -EIO;
+
+	if (status == MEC_RET_OK) {
+		status = 0;
+	} else {
+		status = -EIO;
+	}
+
+#ifdef CONFIG_SPI_ASYNC
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: tbursztyka
body: Async has nothing to do about running in interrupt mode or not, it's just for the caller: 
- spi_transceive() is blocking until it's finished
- spi_transceive_signal() returns right away, and the result of the transaction will come from a callback.
created_at: 2025-04-28T19:40:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+# Copyright (c) 2024, Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+description: Microchip MEC5 series QSPI controller
+
+compatible: "microchip,mec5-qspi"
+
+include: [spi-controller.yaml, pinctrl-device.yaml]
+
+properties:
+  reg:
+    required: true
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
+
+  lines:
+    type: int
+    description: |
+      QSPI data lines 1, 2, or 4. 1 data line is full-duplex
+      MOSI and MISO or half-duplex on MOSI only. Lines set to 2
+      or 4 indicate dual or quad I/O modes.
+      Defaults to 1 for full duplex driver's support for full-duplex spi.
+    enum:
+      - 1
+      - 2
+      - 4
+
+  chip-select:
+    type: int
+    description: |
+      Use QSPI CS0# or CS1#. Shared SPI supports two chip selects.
+      All other QSPI ports support one chip select.
+
path: dts/bindings/spi/microchip,mec5-qspi.yaml
user: login: tbursztyka
body: all the attributes below are specific for the peripheral connected to the controller, so it dos not belong here.
created_at: 2025-04-28T19:44:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+# Copyright (c) 2024, Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+description: Microchip MEC5 series QSPI controller
+
+compatible: "microchip,mec5-qspi"
+
+include: [spi-controller.yaml, pinctrl-device.yaml]
+
+properties:
+  reg:
+    required: true
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
+
+  lines:
+    type: int
+    description: |
+      QSPI data lines 1, 2, or 4. 1 data line is full-duplex
+      MOSI and MISO or half-duplex on MOSI only. Lines set to 2
+      or 4 indicate dual or quad I/O modes.
+      Defaults to 1 for full duplex driver's support for full-duplex spi.
+    enum:
+      - 1
+      - 2
+      - 4
+
+  chip-select:
+    type: int
+    description: |
+      Use QSPI CS0# or CS1#. Shared SPI supports two chip selects.
+      All other QSPI ports support one chip select.
+
path: dts/bindings/spi/microchip,mec5-qspi.yaml
user: login: scottwcpg
body: @tbursztyka Hi, I need some clarification.
1. Are you saying all the YAM properties after chip-select belong to a microchip specific flash device YAML?
2. Chip-select can come from the reg property of child flash devices but the SPI DT macros are not using the reg property.  SPI DT macros are using the SPI_CS_CONTROL_INIT and then GPIO DT spec. This SPI controller can't use arbitrary GPIO pins, its HW design requires it control the CS pin.  So I still need to application to create cs-gpios DT object and my driver does not call GPIO driver but derives the cs number (0 or 1) from cs-gpios?
created_at: 2025-05-01T17:15:35Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: @tbursztyka OK. Will re-architect code putting the logic in the ISR.
I guess I need to create two more drivers for this HW block: mspi and flash for dual/quad support.
created_at: 2025-05-01T17:18:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+		qflags = xflags;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		txlen += xlen;
+		rxlen += xlen;
+		if (!hold_cs) {
+			if ((txlen >= total_tx) && (rxlen >= total_rx)) {
+				qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+			}
+		}
+
+		if (xlen) {
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+			ret = mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+			if (ret != MEC_RET_OK) {
+				ret = -EIO;
+				break;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			ret = spi_context_wait_for_completion(ctx);
+			if (ret) {
+				break;
+			}
+		}
+
+		spi_context_update_tx(ctx, 1u, xlen);
+		spi_context_update_rx(ctx, 1u, xlen);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_SPI_EXTENDED_MODES
+/* MEC5 QSPI controller maximum number of lines is 4. In dual and quad modes all lines can only be
+ * one direction at a time (half-duplex). Other SPI drivers which support dual and quad process
+ * SPI buffers as (tx, rx) pairs. Therefore: if tx then do transmit, wait for done then if rx
+ * do rx.
+ */
+static int mec5_qspi_fdq(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			 const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	uint32_t qflags = 0, xflags = 0, qstatus = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_IOM_SET(data->iom);
+	xflags |= MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	data->rxb = NULL;
+	data->txb = NULL;
+	data->rxcnt = 0;
+	data->txcnt = 0;
+
+	if (rx_bufs) {
+		data->rxb = rx_bufs->buffers;
+		data->rxcnt = rx_bufs->count;
+	}
+
+	if (tx_bufs) {
+		data->txb = tx_bufs->buffers;
+		data->txcnt = tx_bufs->count;
+	}
+
+	spi_context_cs_control(ctx, true);
+
+	while ((data->txcnt != 0) || (data->rxcnt != 0)) {
+		uint8_t *rxbuf = NULL;
+		uint8_t const *txbuf = NULL;
+		size_t rxlen = 0, txlen = 0;
+
+		if (data->txb) {
+			txbuf = data->txb->buf;
+			txlen = data->txb->len;
+		}
+
+		if (data->rxb) {
+			rxbuf = data->rxb->buf;
+			rxlen = data->rxb->len;
+		}
+
+		qflags = xflags;
+
+		if (rxlen || txlen) {
+			if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+				if (!hold_cs) {
+					qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+				}
+			}
+
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+			ret = mec_hal_qspi_uldma(regs, txbuf, txlen, rxbuf, rxlen, qflags);
+			if (ret != MEC_RET_OK) {
+				return -EIO;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			k_sem_take(&ctx->sync, K_FOREVER);
+		}
+
+		if (data->rxcnt) {
+			data->rxcnt--;
+		}
+
+		if (data->rxcnt && data->rxb) {
+			data->rxb++;
+		} else {
+			data->rxb = NULL;
+		}
+
+		if (data->txcnt) {
+			data->txcnt--;
+		}
+
+		if (data->txcnt && data->txb) {
+			data->txb++;
+		} else {
+			data->txb = NULL;
+		}
+	}
+
+	if (!hold_cs) {
+		qstatus = mec_hal_qspi_hw_status(regs);
+		if ((qstatus & MEC_QSPI_STS_ACTIVE) || !(qflags & MEC5_QSPI_ULDMA_FLAG_CLOSE)) {
+			mec_hal_qspi_force_stop(regs);
+		}
+	}
+
+	return ret;
+}
+#endif /* CONFIG_SPI_EXTENDED_MODES */
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *devdat = dev->data;
+	struct spi_context *ctx = &devdat->ctx;
+	int ret = 0;
+
+	if (devdat->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	if (devdat->iom == MEC_QSPI_IO_FULL_DUPLEX) {
+		ret = mec5_qspi_fd(dev, tx_bufs, rx_bufs, async);
+	} else {
+#ifdef CONFIG_SPI_EXTENDED_MODES
+		ret = mec5_qspi_fdq(dev, tx_bufs, rx_bufs, async);
+#else
+		ret = -ENOTSUP;
+		goto do_xfr_exit;
+#endif
+	}
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_cs_control(ctx, false);
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_SPI_ASYNC
+
+/* Async for non-fast path */
+static void mec5_qspi_async_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+/* Async for fast path */
+static void mec5_qspi_async_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t xflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	if (!(data->xfr_flags & MEC5_QSPI_XFR_FLAG_FAST)) {
+		return mec5_qspi_async_ctx_next(dev);
+	}
+
+	if (data->rxcnt) {
+		data->rxcnt--;
+		data->rxb++;
+	}
+	if (data->txcnt) {
+		data->txcnt--;
+		data->txb++;
+	}
+
+	if (!data->rxcnt && !data->txcnt) {
+		spi_context_complete(&data->ctx, dev, 0);
+		return;
+	}
+
+	if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+		xflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+	}
+
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+	mec_hal_qspi_uldma_fd(regs, data->txb->buf, data->txb->len, data->rxb->buf, data->rxb->len,
+			      xflags);
+}
+#endif /* CONFIG_SPI_ASYNC */
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	status = (status == MEC_RET_OK) ? 0 : -EIO;
+
+	if (status == MEC_RET_OK) {
+		status = 0;
+	} else {
+		status = -EIO;
+	}
+
+#ifdef CONFIG_SPI_ASYNC
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: @tbursztyka OK. Will re-architect the driver.

created_at: 2025-05-01T17:18:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!cstm) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!taps) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if (data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	data->chunk_size = 0;
+	data->total_tx_size = spi_context_total_tx_len(ctx);
+	data->total_rx_size = spi_context_total_rx_len(ctx);
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_START;
+
+	/* trigger an empty TX FIFO interrupt to enter the ISR */
+	mec_hal_qspi_intr_ctrl_msk(regs, 1u, MEC_QSPI_IEN_TXB_EMPTY);
+
+	ret = spi_context_wait_for_completion(ctx);
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* ISR helper */
+static void mec5_qspi_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	if ((data->xfr_flags & MEC5_QSPI_XFR_FLAG_START) && (hwsts & MEC_QSPI_IEN_TXB_EMPTY)) {
+		/* empty TX FIFO mec_hal_qspi_done returns BUSY. Ignore if starting transfer */
+		data->xfr_flags &= (uint32_t)~MEC5_QSPI_XFR_FLAG_START;
+	} else if (status != MEC_RET_OK) {
+		status = -EIO;
+		spi_context_complete(&data->ctx, dev, status);
+	}
+
+	mec5_qspi_ctx_next(dev);
+}
+
+/*
+ * Called for each QSPI controller by the kernel during driver load phase
+ * specified in the device initialization structure below.
+ * Initialize QSPI controller.
+ * Initialize SPI context.
+ * QSPI will be fully configured and enabled when the transceive API
+ * is called.
+ */
+static int mec5_qspi_init(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	enum mec_qspi_cs cs = MEC_QSPI_CS_0;
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+	enum mec_qspi_signal_mode spi_mode = MEC_SPI_SIGNAL_MODE_0;
+	int ret = 0;
+
+	data->cs = 0;
+
+	ret = mec_hal_qspi_init(regs, (uint32_t)devcfg->clock_freq, spi_mode, iom, cs);
+	if (ret != MEC_RET_OK) {
+		LOG_ERR("QSPI init error (%d)", ret);
+		return -EINVAL;
+	}
+
+	data->freq = devcfg->clock_freq;
+	data->operation = SPI_WORD_SET(8) | SPI_LINES_SINGLE;
+	mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+
+	ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
+	if (ret) {
+		LOG_ERR("QSPI pinctrl setup failed (%d)", ret);
+	}
+
+	ret = spi_context_cs_configure_all(&data->ctx);
+	if (ret) {
+		LOG_ERR("QSPI cs config failed (%d)", ret);
+		return ret;
+	}
+
+	if (devcfg->irq_config_func) {
+		devcfg->irq_config_func();
+	}
+
+	spi_context_unlock_unconditionally(&data->ctx);
+
+	return ret;
+}
+
+static const struct spi_driver_api mec5_qspi_driver_api = {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: Use the device API wrapper macro.

```suggestion
static DEVICE_API(spi, mec5_qspi_driver_api) = {
```
created_at: 2025-05-08T09:19:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,79 @@
+# Copyright (c) 2024, Microchip Technology Inc.
+# SPDX-License-Identifier: Apache-2.0
+
+description: Microchip MEC5 series QSPI controller
+
+compatible: "microchip,mec5-qspi"
+
+include: [spi-controller.yaml, pinctrl-device.yaml]
+
+properties:
+  reg:
+    required: true
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
+
+  lines:
+    type: int
+    description: |
+      QSPI data lines 1, 2, or 4. 1 data line is full-duplex
+      MOSI and MISO or half-duplex on MOSI only. Lines set to 2
+      or 4 indicate dual or quad I/O modes.
+      Defaults to 1 for full duplex driver's support for full-duplex spi.
+    enum:
+      - 1
+      - 2
+      - 4
+
+  chip-select:
+    type: int
+    description: |
+      Use QSPI CS0# or CS1#. Shared SPI supports two chip selects.
+      All other QSPI ports support one chip select.
+
path: dts/bindings/spi/microchip,mec5-qspi.yaml
user: login: scottwcpg
body: SPI DT macros save the SPI child device "reg" property value in the SPI configuration struct slave member. This driver will use the child device "reg" property as the chip select. MCHP QSPI is controller mode only hardware.
created_at: 2025-05-08T13:11:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+		qflags = xflags;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		txlen += xlen;
+		rxlen += xlen;
+		if (!hold_cs) {
+			if ((txlen >= total_tx) && (rxlen >= total_rx)) {
+				qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+			}
+		}
+
+		if (xlen) {
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+			ret = mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+			if (ret != MEC_RET_OK) {
+				ret = -EIO;
+				break;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			ret = spi_context_wait_for_completion(ctx);
+			if (ret) {
+				break;
+			}
+		}
+
+		spi_context_update_tx(ctx, 1u, xlen);
+		spi_context_update_rx(ctx, 1u, xlen);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_SPI_EXTENDED_MODES
+/* MEC5 QSPI controller maximum number of lines is 4. In dual and quad modes all lines can only be
+ * one direction at a time (half-duplex). Other SPI drivers which support dual and quad process
+ * SPI buffers as (tx, rx) pairs. Therefore: if tx then do transmit, wait for done then if rx
+ * do rx.
+ */
+static int mec5_qspi_fdq(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			 const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	uint32_t qflags = 0, xflags = 0, qstatus = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_IOM_SET(data->iom);
+	xflags |= MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	data->rxb = NULL;
+	data->txb = NULL;
+	data->rxcnt = 0;
+	data->txcnt = 0;
+
+	if (rx_bufs) {
+		data->rxb = rx_bufs->buffers;
+		data->rxcnt = rx_bufs->count;
+	}
+
+	if (tx_bufs) {
+		data->txb = tx_bufs->buffers;
+		data->txcnt = tx_bufs->count;
+	}
+
+	spi_context_cs_control(ctx, true);
+
+	while ((data->txcnt != 0) || (data->rxcnt != 0)) {
+		uint8_t *rxbuf = NULL;
+		uint8_t const *txbuf = NULL;
+		size_t rxlen = 0, txlen = 0;
+
+		if (data->txb) {
+			txbuf = data->txb->buf;
+			txlen = data->txb->len;
+		}
+
+		if (data->rxb) {
+			rxbuf = data->rxb->buf;
+			rxlen = data->rxb->len;
+		}
+
+		qflags = xflags;
+
+		if (rxlen || txlen) {
+			if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+				if (!hold_cs) {
+					qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+				}
+			}
+
+			data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+			ret = mec_hal_qspi_uldma(regs, txbuf, txlen, rxbuf, rxlen, qflags);
+			if (ret != MEC_RET_OK) {
+				return -EIO;
+			}
+
+			if (async) {
+				return 0;
+			}
+
+			k_sem_take(&ctx->sync, K_FOREVER);
+		}
+
+		if (data->rxcnt) {
+			data->rxcnt--;
+		}
+
+		if (data->rxcnt && data->rxb) {
+			data->rxb++;
+		} else {
+			data->rxb = NULL;
+		}
+
+		if (data->txcnt) {
+			data->txcnt--;
+		}
+
+		if (data->txcnt && data->txb) {
+			data->txb++;
+		} else {
+			data->txb = NULL;
+		}
+	}
+
+	if (!hold_cs) {
+		qstatus = mec_hal_qspi_hw_status(regs);
+		if ((qstatus & MEC_QSPI_STS_ACTIVE) || !(qflags & MEC5_QSPI_ULDMA_FLAG_CLOSE)) {
+			mec_hal_qspi_force_stop(regs);
+		}
+	}
+
+	return ret;
+}
+#endif /* CONFIG_SPI_EXTENDED_MODES */
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *devdat = dev->data;
+	struct spi_context *ctx = &devdat->ctx;
+	int ret = 0;
+
+	if (devdat->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	if (devdat->iom == MEC_QSPI_IO_FULL_DUPLEX) {
+		ret = mec5_qspi_fd(dev, tx_bufs, rx_bufs, async);
+	} else {
+#ifdef CONFIG_SPI_EXTENDED_MODES
+		ret = mec5_qspi_fdq(dev, tx_bufs, rx_bufs, async);
+#else
+		ret = -ENOTSUP;
+		goto do_xfr_exit;
+#endif
+	}
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_cs_control(ctx, false);
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_SPI_ASYNC
+
+/* Async for non-fast path */
+static void mec5_qspi_async_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+/* Async for fast path */
+static void mec5_qspi_async_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t xflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	if (!(data->xfr_flags & MEC5_QSPI_XFR_FLAG_FAST)) {
+		return mec5_qspi_async_ctx_next(dev);
+	}
+
+	if (data->rxcnt) {
+		data->rxcnt--;
+		data->rxb++;
+	}
+	if (data->txcnt) {
+		data->txcnt--;
+		data->txb++;
+	}
+
+	if (!data->rxcnt && !data->txcnt) {
+		spi_context_complete(&data->ctx, dev, 0);
+		return;
+	}
+
+	if ((data->txcnt <= 1) && (data->rxcnt <= 1)) {
+		xflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+	}
+
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA | MEC5_QSPI_XFR_FLAG_FAST;
+	mec_hal_qspi_uldma_fd(regs, data->txb->buf, data->txb->len, data->rxb->buf, data->rxb->len,
+			      xflags);
+}
+#endif /* CONFIG_SPI_ASYNC */
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	status = (status == MEC_RET_OK) ? 0 : -EIO;
+
+	if (status == MEC_RET_OK) {
+		status = 0;
+	} else {
+		status = -EIO;
+	}
+
+#ifdef CONFIG_SPI_ASYNC
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Done
created_at: 2025-05-08T13:16:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,729 @@
+/*
+ * Copyright (c) 2024 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+/* microseconds for busy wait and total wait interval */
+#define MEC5_QSPI_WAIT_INTERVAL  8
+#define MEC5_QSPI_WAIT_COUNT     64
+#define MEC5_QSPI_WAIT_FULL_FIFO 1024
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	uint32_t cs1_freq;
+	uint32_t cs_timing;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t chip_sel;
+	uint8_t width; /* 0(half), 1(single), 2(dual), 4(quad) */
+	uint8_t ovrc;
+	uint8_t ctrl_tap;
+	uint8_t clock_tap;
+};
+
+#define MEC5_QSPI_XFR_FLAG_BUSY BIT(0)
+#define MEC5_QSPI_XFR_FLAG_LDMA BIT(1)
+#define MEC5_QSPI_XFR_FLAG_FAST BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	enum mec_qspi_io iom;
+	uint8_t cs;
+};
+
+#define SPI_CFG_MODE_MSK (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_CFG_MODE_0   0
+#define SPI_CFG_MODE_3   (SPI_MODE_CPOL | SPI_MODE_CPHA)
+#define SPI_NORM_FREQ_HZ MHZ(24)
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+static enum mec_qspi_io lines_to_io(uint32_t opw)
+{
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+#if defined(CONFIG_SPI_EXTENDED_MODES)
+	switch (opw & SPI_LINES_MASK) {
+	case SPI_LINES_DUAL:
+		iom = MEC_QSPI_IO_DUAL;
+		break;
+	case SPI_LINES_QUAD:
+		iom = MEC_QSPI_IO_QUAD;
+		break;
+	default:
+		break;
+	}
+#endif
+	return iom;
+}
+
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	data->iom = MEC_QSPI_IO_FULL_DUPLEX;
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	data->iom = lines_to_io(data->operation);
+
+	ret = mec_hal_qspi_io(regs, data->iom);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_fd(const struct device *dev, const struct spi_buf_set *tx_bufs,
+			const struct spi_buf_set *rx_bufs, bool async)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0, txlen = 0, rxlen = 0, total_tx = 0, total_rx = 0;
+	uint32_t qflags = 0, xflags = 0;
+	int ret = 0;
+	bool hold_cs = (data->operation & SPI_HOLD_ON_CS) ? true : false;
+
+	xflags = MEC_QSPI_ULDMA_FLAG_TX_OVR_VAL_SET(xflags, devcfg->ovrc);
+	xflags |= MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	total_tx = spi_context_total_tx_len(ctx);
+	total_rx = spi_context_total_rx_len(ctx);
+	data->total_tx_size = total_tx;
+	data->total_rx_size = total_rx;
+
+	spi_context_cs_control(ctx, true);
+
+	while (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Done
created_at: 2025-05-08T13:16:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!cstm) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!taps) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if (data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	data->chunk_size = 0;
+	data->total_tx_size = spi_context_total_tx_len(ctx);
+	data->total_rx_size = spi_context_total_rx_len(ctx);
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_START;
+
+	/* trigger an empty TX FIFO interrupt to enter the ISR */
+	mec_hal_qspi_intr_ctrl_msk(regs, 1u, MEC_QSPI_IEN_TXB_EMPTY);
+
+	ret = spi_context_wait_for_completion(ctx);
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
+}
+#endif
+
+static int mec5_qspi_release(const struct device *dev, const struct spi_config *config)
+{
+	struct mec5_qspi_data *qdata = dev->data;
+	const struct mec5_qspi_config *cfg = dev->config;
+	int ret = 0;
+
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	ret = mec_hal_qspi_force_stop(cfg->regs);
+
+	/* increments lock semphare in ctx up to initial limit */
+	spi_context_unlock_unconditionally(&qdata->ctx);
+
+	if (ret != MEC_RET_OK) {
+		return -EIO;
+	}
+
+	return 0;
+}
+
+/* ISR helper */
+static void mec5_qspi_ctx_next(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	struct spi_context *ctx = &data->ctx;
+	size_t xlen = 0;
+	uint32_t qflags = MEC5_QSPI_ULDMA_FLAG_START | MEC5_QSPI_ULDMA_FLAG_IEN;
+
+	spi_context_update_tx(ctx, 1u, data->chunk_size);
+	spi_context_update_rx(ctx, 1u, data->chunk_size);
+
+	if (data->total_tx_size) {
+		data->total_tx_size -= data->chunk_size;
+	}
+
+	if (data->total_rx_size) {
+		data->total_rx_size -= data->chunk_size;
+	}
+
+	if (spi_context_rx_on(ctx) || spi_context_tx_on(ctx)) {
+		xlen = spi_context_max_continuous_chunk(ctx);
+		data->chunk_size = xlen;
+
+		uint8_t const *txb = ctx->tx_buf;
+		uint8_t *rxb = ctx->rx_buf;
+
+		if (txb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_TX;
+		} else {
+			txb = &devcfg->ovrc;
+		}
+
+		if (rxb) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_INCR_RX;
+		} else {
+			rxb = (uint8_t *)&data->rxdb;
+		}
+
+		if ((data->total_tx_size <= xlen) && (data->total_rx_size <= xlen)) {
+			qflags |= MEC5_QSPI_ULDMA_FLAG_CLOSE;
+		}
+
+		data->xfr_flags = MEC5_QSPI_XFR_FLAG_LDMA;
+		mec_hal_qspi_uldma_fd2(regs, (const uint8_t *)txb, rxb, xlen, qflags);
+	} else {
+		spi_context_complete(&data->ctx, dev, 0);
+	}
+}
+
+static void mec5_qspi_isr(const struct device *dev)
+{
+	struct mec5_qspi_data *data = dev->data;
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	uint32_t hwsts = 0u;
+	int status = 0;
+
+	hwsts = mec_hal_qspi_hw_status(regs);
+	data->qstatus = hwsts;
+	status = mec_hal_qspi_done(regs);
+
+	mec_hal_qspi_intr_ctrl(regs, 0);
+	mec_hal_qspi_hw_status_clr(regs, hwsts);
+	mec_hal_qspi_girq_clr(regs);
+
+	if ((data->xfr_flags & MEC5_QSPI_XFR_FLAG_START) && (hwsts & MEC_QSPI_IEN_TXB_EMPTY)) {
+		/* empty TX FIFO mec_hal_qspi_done returns BUSY. Ignore if starting transfer */
+		data->xfr_flags &= (uint32_t)~MEC5_QSPI_XFR_FLAG_START;
+	} else if (status != MEC_RET_OK) {
+		status = -EIO;
+		spi_context_complete(&data->ctx, dev, status);
+	}
+
+	mec5_qspi_ctx_next(dev);
+}
+
+/*
+ * Called for each QSPI controller by the kernel during driver load phase
+ * specified in the device initialization structure below.
+ * Initialize QSPI controller.
+ * Initialize SPI context.
+ * QSPI will be fully configured and enabled when the transceive API
+ * is called.
+ */
+static int mec5_qspi_init(const struct device *dev)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	enum mec_qspi_cs cs = MEC_QSPI_CS_0;
+	enum mec_qspi_io iom = MEC_QSPI_IO_FULL_DUPLEX;
+	enum mec_qspi_signal_mode spi_mode = MEC_SPI_SIGNAL_MODE_0;
+	int ret = 0;
+
+	data->cs = 0;
+
+	ret = mec_hal_qspi_init(regs, (uint32_t)devcfg->clock_freq, spi_mode, iom, cs);
+	if (ret != MEC_RET_OK) {
+		LOG_ERR("QSPI init error (%d)", ret);
+		return -EINVAL;
+	}
+
+	data->freq = devcfg->clock_freq;
+	data->operation = SPI_WORD_SET(8) | SPI_LINES_SINGLE;
+	mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+
+	ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
+	if (ret) {
+		LOG_ERR("QSPI pinctrl setup failed (%d)", ret);
+	}
+
+	ret = spi_context_cs_configure_all(&data->ctx);
+	if (ret) {
+		LOG_ERR("QSPI cs config failed (%d)", ret);
+		return ret;
+	}
+
+	if (devcfg->irq_config_func) {
+		devcfg->irq_config_func();
+	}
+
+	spi_context_unlock_unconditionally(&data->ctx);
+
+	return ret;
+}
+
+static const struct spi_driver_api mec5_qspi_driver_api = {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: @pdgendt Hi, thanks for the review.  Change your recommended implemented and update pushed to PR.
created_at: 2025-05-08T13:17:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -145,4 +145,6 @@ source "drivers/spi/Kconfig.xmc4xxx"
 source "drivers/spi/spi_nxp_lpspi/Kconfig"
 # zephyr-keep-sorted-stop
 
+source "drivers/spi/Kconfig.mec5"
path: drivers/spi/Kconfig
user: login: pdgendt
body: Add the entry to the sorted list.
created_at: 2025-05-10T13:26:17Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!cstm) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: No implicit boolean checks (also for other functions)

```suggestion
	if (cstm == NULL) {
```
created_at: 2025-05-10T13:27:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,539 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!cstm) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (!taps) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if (data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
+		ret = -EBUSY;
+	}
+
+	if (!tx_bufs && !rx_bufs) {
+		return -EINVAL;
+	}
+
+	spi_context_lock(ctx, async, cb, userdata, config);
+
+	ret = mec5_qspi_configure(dev, config);
+	if (ret) {
+		goto do_xfr_exit;
+	}
+
+	spi_context_buffers_setup(ctx, tx_bufs, rx_bufs, 1u);
+
+	data->chunk_size = 0;
+	data->total_tx_size = spi_context_total_tx_len(ctx);
+	data->total_rx_size = spi_context_total_rx_len(ctx);
+	data->xfr_flags = MEC5_QSPI_XFR_FLAG_START;
+
+	/* trigger an empty TX FIFO interrupt to enter the ISR */
+	mec_hal_qspi_intr_ctrl_msk(regs, 1u, MEC_QSPI_IEN_TXB_EMPTY);
+
+	ret = spi_context_wait_for_completion(ctx);
+
+	if (async && !ret) {
+		return 0;
+	}
+
+	if (ret) {
+		mec_hal_qspi_force_stop(devcfg->regs);
+	}
+do_xfr_exit:
+	spi_context_release(ctx, 0);
+
+	return ret;
+}
+
+static int mec5_qspi_xfr_check1(const struct spi_config *config)
+{
+	if (mec_hal_espi_taf_is_activated()) {
+		return -EPERM;
+	}
+
+	if (!config) {
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mec5_qspi_xfr_sync(const struct device *dev, const struct spi_config *config,
+			      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
+		return ret;
+	}
+
+	return mec5_qspi_do_xfr(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
+}
+
+#ifdef CONFIG_SPI_ASYNC
+static int mec5_qspi_xfr_async(const struct device *dev, const struct spi_config *config,
+			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			       spi_callback_t cb, void *userdata)
+{
+	int ret = mec5_qspi_xfr_check1(config);
+
+	if (ret) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: ```suggestion
	if (ret < 0) {
```
created_at: 2025-05-10T13:28:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if (data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: ```suggestion
	if ((data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) != 0U) {
```
created_at: 2025-05-12T18:40:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: ```suggestion
	if ((data->operation & SPI_MODE_CPHA) != 0U) {
```
created_at: 2025-05-12T18:40:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: pdgendt
body: ```suggestion
	if ((data->operation & SPI_MODE_CPOL) != 0U) {
```
created_at: 2025-05-12T18:40:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
+		sgm |= BIT(1);
+	}
+	/* requires QSPI frequency to be programmed first */
+	ret = mec_hal_qspi_spi_signal_mode(regs, mec5_qspi_sig_mode[sgm]);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	data->ctx.config = config;
+
+	return 0;
+}
+
+static int mec5_qspi_do_xfr(const struct device *dev, const struct spi_config *config,
+			    const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
+			    bool async, spi_callback_t cb, void *userdata)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec5_qspi_data *data = dev->data;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct spi_context *ctx = &data->ctx;
+	int ret = 0;
+
+	if (data->xfr_flags & MEC5_QSPI_XFR_FLAG_BUSY) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Done
created_at: 2025-05-12T20:35:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Done
created_at: 2025-05-12T20:35:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
+	const struct mec5_spi_devices *child_devices;
+};
+
+#define MEC5_QSPI_XFR_FLAG_START BIT(0)
+#define MEC5_QSPI_XFR_FLAG_BUSY  BIT(1)
+#define MEC5_QSPI_XFR_FLAG_LDMA  BIT(2)
+
+/* Device run time data */
+struct mec5_qspi_data {
+	struct spi_context ctx;
+	const struct spi_buf *rxb;
+	const struct spi_buf *txb;
+	size_t rxcnt;
+	size_t txcnt;
+	volatile uint32_t qstatus;
+	volatile uint32_t xfr_flags;
+	size_t total_tx_size;
+	size_t total_rx_size;
+	size_t chunk_size;
+	uint32_t rxdb;
+	uint32_t byte_time_ns;
+	uint32_t freq;
+	uint32_t operation;
+	uint8_t cs;
+};
+
+static const enum mec_qspi_signal_mode mec5_qspi_sig_mode[4] = {
+	MEC_SPI_SIGNAL_MODE_0, MEC_SPI_SIGNAL_MODE_1, MEC_SPI_SIGNAL_MODE_2, MEC_SPI_SIGNAL_MODE_3};
+
+static int spi_feature_support(const struct spi_config *config)
+{
+	/* NOTE: bit(11) is Half-duplex(3-wire) */
+	if (config->operation & (SPI_TRANSFER_LSB | SPI_OP_MODE_SLAVE | SPI_MODE_LOOP | BIT(11))) {
+		LOG_ERR("Driver does not support LSB first, slave, loop back, or half-duplex");
+		return -ENOTSUP;
+	}
+
+	if (config->operation & SPI_CS_ACTIVE_HIGH) {
+		LOG_ERR("CS active high not supported");
+		return -ENOTSUP;
+	}
+
+	if (SPI_WORD_SIZE_GET(config->operation) != 8) {
+		LOG_ERR("Word size != 8 not supported");
+		return -ENOTSUP;
+	}
+
+	return 0;
+}
+
+int get_cs_timing_from_dt(const struct device *dev, uint8_t cs, uint32_t *cstm)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (cstm == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*cstm = cd->cs_timing;
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Looks up QSPI clock and control signal taps from device tree.
+ * if chip select entry is present in driver DT then return
+ * taps value with bits[7:0] = clock tap value, bits[15:8] = control tap value.
+ */
+int get_taps_from_dt(const struct device *dev, uint8_t cs, uint32_t *taps)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+
+	if (taps == NULL) {
+		return -EINVAL;
+	}
+
+	for (uint8_t n = 0; n > devcfg->num_child_devices; n++) {
+		const struct mec5_spi_devices *cd = &devcfg->child_devices[n];
+
+		if (cd->cs == cs) {
+			*taps = (uint32_t)cd->sck_tap | ((uint32_t)cd->ctrl_tap << 8);
+			return 0;
+		}
+	}
+
+	return -ENOTSUP;
+}
+
+/* Configure the controller.
+ * NOTE: QSPI controller hardware controls up to two chip selects. If a previous call the driver
+ * had the SPI_HOLD_ON_CS flag set then performing a controller reset will cause chip select
+ * to de-assert. We must check for this corner case.
+ * The driver data structure has member ctx which is type struct spi_context. The context has
+ * a pointer to struct spi_config.
+ * struct spi_config
+ *   frequency in Hz
+ *   operation - contains flags for sampling clock edge and clock idle state
+ *               data frame size: we only support 8 bits
+ *               full or half-duplex: we only spport full-duplex
+ *               active high CS (we can only support this by using invert flag in PINCTRL for CS)
+ *               frame format: we only support Motorola frame format.
+ *               MSB or LSB first: we only support MSB first
+ *               Hold CS active at end of transfer.
+ *   slave - QSPI is controller only. We use this field for chip select (0/1).
+ *   struct spi_cs_control cs - QSPI controls chip select. We don't use this field.
+ */
+static int mec5_qspi_configure(const struct device *dev, const struct spi_config *config)
+{
+	const struct mec5_qspi_config *devcfg = dev->config;
+	struct mec_qspi_regs *regs = devcfg->regs;
+	struct mec5_qspi_data *data = dev->data;
+	uint32_t cstm = 0, taps = 0;
+	uint8_t sgm = 0;
+	int ret = 0;
+
+	if (config == NULL) {
+		return -EINVAL;
+	}
+
+	/* chip select */
+	if (config->slave >= MEC_QSPI_CS_MAX) {
+		LOG_ERR("Invalid chip select [0,1]");
+		return -EINVAL;
+	}
+
+	data->cs = (uint8_t)(config->slave & 0xffu);
+	mec_hal_qspi_cs_select(regs, data->cs);
+
+	ret = get_cs_timing_from_dt(dev, data->cs, &cstm);
+	if (ret == 0) {
+		mec_hal_qspi_cs_timing(regs, cstm);
+	}
+
+	ret = get_taps_from_dt(dev, data->cs, &taps);
+	if (ret == 0) {
+		mec_hal_qspi_tap_select(regs, (taps & 0xffu), ((taps >> 8) & 0xffu));
+	}
+
+	if (config->frequency != data->freq) {
+		ret = mec_hal_qspi_set_freq(regs, config->frequency);
+		if (ret != MEC_RET_OK) {
+			return -EINVAL;
+		}
+		data->freq = config->frequency;
+		mec_hal_qspi_byte_time_ns(regs, &data->byte_time_ns);
+	}
+
+	/* No HAL API for clearing the TX and RX FIFOs */
+	regs->EXE = MEC_BIT(MEC_QSPI_EXE_CLRF_Pos);
+	regs->STATUS = UINT32_MAX;
+
+	if (config->operation == data->operation) {
+		return 0;
+	}
+
+	data->operation = config->operation;
+	ret = spi_feature_support(config);
+	if (ret != 0) {
+		return ret;
+	}
+
+	ret = mec_hal_qspi_io(regs, MEC_QSPI_IO_FULL_DUPLEX);
+	if (ret != MEC_RET_OK) {
+		return -EINVAL;
+	}
+
+	if (data->operation & SPI_MODE_CPHA) {
+		sgm |= BIT(0);
+	}
+	if (data->operation & SPI_MODE_CPOL) {
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Done.  Went through all the if statements.
created_at: 2025-05-12T20:35:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/dt-bindings/spi/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: tbursztyka
body: these 2 attributes could go at the end for better sturct alignment
created_at: 2025-05-14T18:02:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2025 Microchip Technology Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#define DT_DRV_COMPAT microchip_mec5_qspi
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(spi_mec5, CONFIG_SPI_LOG_LEVEL);
+
+#include <errno.h>
+#include <zephyr/device.h>
+#include <zephyr/drivers/gpio.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/spi.h>
+#include <zephyr/dt-bindings/spi/spi.h>
+#include <zephyr/sys/sys_io.h>
+#include <zephyr/sys/util.h>
+#include <soc.h>
+#include <zephyr/irq.h>
+
+#include "spi_context.h"
+
+/* MEC5 HAL */
+#include <device_mec5.h>
+#include <mec_ecia_api.h>
+#include <mec_espi_taf.h>
+#include <mec_qspi_api.h>
+
+struct mec5_spi_devices {
+	uint32_t cs_timing;
+	uint8_t cs;
+	uint8_t sck_tap;
+	uint8_t ctrl_tap;
+	uint8_t flags;
+};
+
+/* Device constant configuration parameters */
+struct mec5_qspi_config {
+	struct mec_qspi_regs *regs;
+	int clock_freq;
+	const struct pinctrl_dev_config *pcfg;
+	void (*irq_config_func)(void);
+	uint8_t ovrc;
+	uint8_t num_child_devices;
path: drivers/spi/spi_mchp_mec5_qspi.c
user: login: scottwcpg
body: Hi @tbursztyka thanks for the review.  Made the recommended changed and pushed the update.
created_at: 2025-05-14T19:28:12Z
author_association: CONTRIBUTOR

PR comments:
user: login: scottwcpg
created_at: 2025-05-07T18:07:51Z
body: @tbursztyka Hi. Made the changes you requested and pushed an update.
author_association: CONTRIBUTOR
user: login: scottwcpg
created_at: 2025-05-08T14:45:38Z
body: Ran twister local for all four boards.  No errors.  
author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-05-12T20:19:17Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=zephyrproject-rtos_zephyr&pullRequest=89122) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=zephyrproject-rtos_zephyr&pullRequest=89122&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=zephyrproject-rtos_zephyr&pullRequest=89122&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=zephyrproject-rtos_zephyr&pullRequest=89122&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=zephyrproject-rtos_zephyr&pullRequest=89122&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=zephyrproject-rtos_zephyr&pullRequest=89122&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=zephyrproject-rtos_zephyr&pullRequest=89122)


author_association: NONE