/*
 * Copyright (c) 2024 Renesas Electronics Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT renesas_ra8_spi_b

#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/clock_control/renesas_ra_cgc.h>
#include <zephyr/irq.h>
#include <soc.h>
#include <instances/r_dtc.h>
#include <instances/r_spi_b.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ra8_spi_b);

#include "spi_context.h"

#if defined(CONFIG_SPI_B_INTERRUPT)
void spi_b_rxi_isr(void);
void spi_b_txi_isr(void);
void spi_b_tei_isr(void);
void spi_b_eri_isr(void);
#endif

struct ra_spi_config {
	const struct pinctrl_dev_config *pcfg;
	const struct device *clock_dev;
	const struct clock_control_ra_subsys_cfg clock_subsys;
};

struct ra_spi_data {
	struct spi_context ctx;
	uint8_t dfs;
	struct st_spi_b_instance_ctrl spi;
	struct st_spi_cfg fsp_config;
	struct st_spi_b_extended_cfg fsp_config_extend;
#if CONFIG_SPI_B_INTERRUPT
	uint32_t data_len;
#endif
#if defined(CONFIG_SPI_B_RA_DTC)
	/* RX */
	struct st_transfer_instance rx_transfer;
	struct st_dtc_instance_ctrl rx_transfer_ctrl;
	struct st_transfer_info rx_transfer_info;
	struct st_transfer_cfg rx_transfer_cfg;
	struct st_dtc_extended_cfg rx_transfer_cfg_extend;

	/* TX */
	struct st_transfer_instance tx_transfer;
	struct st_dtc_instance_ctrl tx_transfer_ctrl;
	struct st_transfer_info tx_transfer_info;
	struct st_transfer_cfg tx_transfer_cfg;
	struct st_dtc_extended_cfg tx_transfer_cfg_extend;
#endif
};

static void spi_cb(spi_callback_args_t *p_args)
{
	struct device *dev = (struct device *)p_args->p_context;
	struct ra_spi_data *data = dev->data;

	switch (p_args->event) {
	case SPI_EVENT_TRANSFER_COMPLETE:
		spi_context_cs_control(&data->ctx, false);
		spi_context_complete(&data->ctx, dev, 0);
		break;
	case SPI_EVENT_ERR_MODE_FAULT:    /* Mode fault error */
	case SPI_EVENT_ERR_READ_OVERFLOW: /* Read overflow error */
	case SPI_EVENT_ERR_PARITY:        /* Parity error */
	case SPI_EVENT_ERR_OVERRUN:       /* Overrun error */
	case SPI_EVENT_ERR_FRAMING:       /* Framing error */
	case SPI_EVENT_ERR_MODE_UNDERRUN: /* Underrun error */
		spi_context_cs_control(&data->ctx, false);
		spi_context_complete(&data->ctx, dev, -EIO);
		break;
	default:
		break;
	}
}

static int ra_spi_b_configure(const struct device *dev, const struct spi_config *config)
{
	struct ra_spi_data *data = dev->data;
	fsp_err_t fsp_err;

	if (spi_context_configured(&data->ctx, config)) {
		/* Nothing to do */
		return 0;
	}

	fsp_err = R_SPI_B_Close(&data->spi);

	if ((config->operation & SPI_FRAME_FORMAT_TI) == SPI_FRAME_FORMAT_TI) {
		return -ENOTSUP;
	}

	if (config->operation & SPI_OP_MODE_SLAVE) {
		data->fsp_config.operating_mode = SPI_MODE_SLAVE;
	} else {
		data->fsp_config.operating_mode = SPI_MODE_MASTER;
	}

	if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) {
		data->fsp_config.clk_polarity = SPI_CLK_POLARITY_HIGH;
	} else {
		data->fsp_config.clk_polarity = SPI_CLK_POLARITY_LOW;
	}

	if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) {
		data->fsp_config.clk_phase = SPI_CLK_PHASE_EDGE_EVEN;
	} else {
		data->fsp_config.clk_phase = SPI_CLK_PHASE_EDGE_ODD;
	}

	if (config->operation & SPI_TRANSFER_LSB) {
		data->fsp_config.bit_order = SPI_BIT_ORDER_LSB_FIRST;
	} else {
		data->fsp_config.bit_order = SPI_BIT_ORDER_MSB_FIRST;
	}

	if (config->frequency > 0) {
		fsp_err = R_SPI_B_CalculateBitrate(config->frequency,
						   data->fsp_config_extend.clock_source,
						   &data->fsp_config_extend.spck_div);
		__ASSERT(fsp_err == 0, "spi_b: spi frequency calculate error: %d", fsp_err);
	}

	data->fsp_config_extend.spi_comm = SPI_B_COMMUNICATION_FULL_DUPLEX;
	if (spi_cs_is_gpio(config) || !IS_ENABLED(CONFIG_SPI_B_USE_HW_SS)) {
		data->fsp_config_extend.spi_clksyn = SPI_B_SSL_MODE_CLK_SYN;
	} else {
		data->fsp_config_extend.spi_clksyn = SPI_B_SSL_MODE_SPI;
		data->fsp_config_extend.ssl_select = SPI_B_SSL_SELECT_SSL0;
	}

	data->fsp_config.p_extend = &data->fsp_config_extend;

	data->fsp_config.p_callback = spi_cb;
	data->fsp_config.p_context = dev;
	fsp_err = R_SPI_B_Open(&data->spi, &data->fsp_config);
	if (fsp_err != FSP_SUCCESS) {
		LOG_ERR("R_SPI_B_Open error: %d", fsp_err);
		return -EINVAL;
	}
	data->ctx.config = config;

	return 0;
}

static bool ra_spi_b_transfer_ongoing(struct ra_spi_data *data)
{
#if defined(CONFIG_SPI_B_INTERRUPT)
	return (spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx));
#else
	if (spi_context_total_tx_len(&data->ctx) < spi_context_total_rx_len(&data->ctx)) {
		return (spi_context_tx_on(&data->ctx) || spi_context_rx_on(&data->ctx));
	} else {
		return (spi_context_tx_on(&data->ctx) && spi_context_rx_on(&data->ctx));
	}
#endif
}

#ifndef CONFIG_SPI_B_INTERRUPT
static int ra_spi_b_transceive_slave(struct ra_spi_data *data)
{
	R_SPI_B0_Type *p_spi_reg = data->spi.p_regs;

	if (p_spi_reg->SPSR_b.SPTEF && spi_context_tx_on(&data->ctx)) {
		uint32_t tx;

		if (data->ctx.tx_buf != NULL) {
			if (data->dfs > 2) {
				tx = *(uint32_t *)(data->ctx.tx_buf);
			} else if (data->dfs > 1) {
				tx = *(uint16_t *)(data->ctx.tx_buf);
			} else {
				tx = *(uint8_t *)(data->ctx.tx_buf);
			}
		} else {
			tx = 0;
		}
		/* Clear Transmit Empty flag */
		p_spi_reg->SPSRC = R_SPI_B0_SPSRC_SPTEFC_Msk;

		p_spi_reg->SPDR = tx;

		spi_context_update_tx(&data->ctx, data->dfs, 1);
	} else {
		p_spi_reg->SPCR_b.SPTIE = 0;
	}

	if (p_spi_reg->SPSR_b.SPRF && spi_context_rx_buf_on(&data->ctx)) {
		uint32_t rx;

		rx = p_spi_reg->SPDR;
		/* Clear Receive Full flag */
		p_spi_reg->SPSRC = R_SPI_B0_SPSRC_SPRFC_Msk;
		if (data->dfs > 2) {
			UNALIGNED_PUT(rx, (uint32_t *)data->ctx.rx_buf);
		} else if (data->dfs > 1) {
			UNALIGNED_PUT(rx, (uint16_t *)data->ctx.rx_buf);
		} else {
			UNALIGNED_PUT(rx, (uint8_t *)data->ctx.rx_buf);
		}
		spi_context_update_rx(&data->ctx, data->dfs, 1);
	}

	return 0;
}

static int ra_spi_b_transceive_master(struct ra_spi_data *data)
{
	R_SPI_B0_Type *p_spi_reg = data->spi.p_regs;
	uint32_t tx;
	uint32_t rx;

	/* Tx transfer*/
	if (spi_context_tx_buf_on(&data->ctx)) {
		if (data->dfs > 2) {
			tx = *(uint32_t *)(data->ctx.tx_buf);
		} else if (data->dfs > 1) {
			tx = *(uint16_t *)(data->ctx.tx_buf);
		} else {
			tx = *(uint8_t *)(data->ctx.tx_buf);
		}
	} else {
		tx = 0U;
	}

	while (!p_spi_reg->SPSR_b.SPTEF) {
	}

	p_spi_reg->SPDR = tx;

	/* Clear Transmit Empty flag */
	p_spi_reg->SPSRC = R_SPI_B0_SPSRC_SPTEFC_Msk;
	spi_context_update_tx(&data->ctx, data->dfs, 1);

	/* Rx receive */
	if (spi_context_rx_on(&data->ctx)) {
		while (!p_spi_reg->SPSR_b.SPRF) {
		}
		rx = p_spi_reg->SPDR;
		/* Clear Receive Full flag */
		p_spi_reg->SPSRC = R_SPI_B0_SPSRC_SPRFC_Msk;
		if (data->dfs > 2) {
			UNALIGNED_PUT(rx, (uint32_t *)data->ctx.rx_buf);
		} else if (data->dfs > 1) {
			UNALIGNED_PUT(rx, (uint16_t *)data->ctx.rx_buf);
		} else {
			UNALIGNED_PUT(rx, (uint8_t *)data->ctx.rx_buf);
		}
		spi_context_update_rx(&data->ctx, data->dfs, 1);
	}

	return 0;
}

static int ra_spi_b_transceive_data(struct ra_spi_data *data)
{
	uint16_t operation = data->ctx.config->operation;

	if (SPI_OP_MODE_GET(operation) == SPI_OP_MODE_MASTER) {
		ra_spi_b_transceive_master(data);
	} else {
		ra_spi_b_transceive_slave(data);
	}

	return 0;
}
#endif

static int transceive(const struct device *dev, const struct spi_config *config,
		      const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs,
		      bool asynchronous, spi_callback_t cb, void *userdata)
{
	struct ra_spi_data *data = dev->data;
	R_SPI_B0_Type *p_spi_reg;
	int ret = 0;

	if (!tx_bufs && !rx_bufs) {
		return 0;
	}

#ifndef CONFIG_SPI_B_INTERRUPT
	if (asynchronous) {
		return -ENOTSUP;
	}
#endif

	spi_context_lock(&data->ctx, asynchronous, cb, userdata, config);

	ret = ra_spi_b_configure(dev, config);
	if (ret) {
		goto end;
	}
	data->dfs = ((SPI_WORD_SIZE_GET(config->operation) - 1) / 8) + 1;
	p_spi_reg = data->spi.p_regs;
	/* Set buffers info */
	spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, data->dfs);

	spi_context_cs_control(&data->ctx, true);

#ifdef CONFIG_SPI_B_INTERRUPT
	spi_bit_width_t spi_width =
		(spi_bit_width_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1);

	if (data->ctx.rx_len == 0) {
		data->data_len = spi_context_is_slave(&data->ctx)
					 ? spi_context_total_tx_len(&data->ctx)
					 : data->ctx.tx_len;
	} else if (data->ctx.tx_len == 0) {
		data->data_len = spi_context_is_slave(&data->ctx)
					 ? spi_context_total_rx_len(&data->ctx)
					 : data->ctx.rx_len;
	} else {
		data->data_len = spi_context_is_slave(&data->ctx)
					 ? MAX(spi_context_total_tx_len(&data->ctx),
					       spi_context_total_rx_len(&data->ctx))
					 : MIN(data->ctx.tx_len, data->ctx.rx_len);
	}

	if (data->ctx.rx_buf == NULL) {
		R_SPI_B_Write(&data->spi, data->ctx.tx_buf, data->data_len, spi_width);
	} else if (data->ctx.tx_buf == NULL) {
		R_SPI_B_Read(&data->spi, data->ctx.rx_buf, data->data_len, spi_width);
	} else {
		R_SPI_B_WriteRead(&data->spi, data->ctx.tx_buf, data->ctx.rx_buf, data->data_len,
				  spi_width);
	}
	ret = spi_context_wait_for_completion(&data->ctx);

#else
	p_spi_reg->SPCR_b.TXMD = 0x0; /* tx - rx*/
	if (!spi_context_tx_on(&data->ctx)) {
		p_spi_reg->SPCR_b.TXMD = 0x2; /* rx only */
	}
	if (!spi_context_rx_on(&data->ctx)) {
		p_spi_reg->SPCR_b.TXMD = 0x1; /* tx only */
	}

	/* Clear FIFOs */
	p_spi_reg->SPFCR = 1;

	/* Enable the SPI Transfer. */
	p_spi_reg->SPCR_b.SPE = 1;
	p_spi_reg->SPCMD0 |= (uint32_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1)
			     << R_SPI_B0_SPCMD0_SPB_Pos;
	do {
		ra_spi_b_transceive_data(data);
	} while (ra_spi_b_transfer_ongoing(data));

	/* Wait for transmision complete */
	while (p_spi_reg->SPSR_b.IDLNF) {
	}

	/* Disable the SPI Transfer. */
	p_spi_reg->SPCR_b.SPE = 0;
#endif
#ifdef CONFIG_SPI_SLAVE
	if (spi_context_is_slave(&data->ctx) && !ret) {
		ret = data->ctx.recv_frames;
	}
#endif /* CONFIG_SPI_SLAVE */

end:
	spi_context_release(&data->ctx, ret);

	return ret;
}

static int ra_spi_b_transceive(const struct device *dev, const struct spi_config *config,
			       const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs)
{
	return transceive(dev, config, tx_bufs, rx_bufs, false, NULL, NULL);
}

#ifdef CONFIG_SPI_ASYNC
static int ra_spi_b_transceive_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)
{
	return transceive(dev, config, tx_bufs, rx_bufs, true, cb, userdata);
}
#endif /* CONFIG_SPI_ASYNC */

static int ra_spi_b_release(const struct device *dev, const struct spi_config *config)
{
	struct ra_spi_data *data = dev->data;

	spi_context_unlock_unconditionally(&data->ctx);

	return 0;
}

static const struct spi_driver_api ra_spi_driver_api = {.transceive = ra_spi_b_transceive,
#ifdef CONFIG_SPI_ASYNC
							.transceive_async =
								ra_spi_b_transceive_async,
#endif /* CONFIG_SPI_ASYNC */
							.release = ra_spi_b_release};

static spi_b_clock_source_t ra_spi_b_clock_name(const struct device *clock_dev)
{
	const char *clock_dev_name = clock_dev->name;

	if (strcmp(clock_dev_name, "spiclk") == 0 || strcmp(clock_dev_name, "scispiclk") == 0) {
		return SPI_B_CLOCK_SOURCE_SCISPICLK;
	}

	return SPI_B_CLOCK_SOURCE_PCLK;
}

static int spi_b_ra_init(const struct device *dev)
{
	const struct ra_spi_config *config = dev->config;
	struct ra_spi_data *data = dev->data;
	int ret;

	if (!device_is_ready(config->clock_dev)) {
		return -ENODEV;
	}

	data->fsp_config_extend.clock_source = ra_spi_b_clock_name(config->clock_dev);

	/* Configure dt provided device signals when available */
	ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
	if (ret < 0) {
		return ret;
	}

	ret = spi_context_cs_configure_all(&data->ctx);
	if (ret < 0) {
		return ret;
	}

	spi_context_unlock_unconditionally(&data->ctx);

	return 0;
}

#if defined(CONFIG_SPI_B_INTERRUPT)

static void ra_spi_retransmit(struct ra_spi_data *data)
{
	spi_bit_width_t spi_width =
		(spi_bit_width_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1);

	if (data->ctx.rx_len == 0) {
		data->data_len = data->ctx.tx_len;
		data->spi.p_tx_data = data->ctx.tx_buf;
		data->spi.p_rx_data = NULL;
	} else if (data->ctx.tx_len == 0) {
		data->data_len = data->ctx.rx_len;
		data->spi.p_tx_data = NULL;
		data->spi.p_rx_data = data->ctx.rx_buf;
	} else {
		data->data_len = MIN(data->ctx.tx_len, data->ctx.rx_len);
		data->spi.p_tx_data = data->ctx.tx_buf;
		data->spi.p_rx_data = data->ctx.rx_buf;
	}

	data->spi.bit_width = spi_width;
	data->spi.rx_count = 0;
	data->spi.tx_count = 0;
	data->spi.count = data->data_len;
#ifdef CONFIG_SPI_B_RA_DTC
	/* Determine DTC transfer size */
	transfer_size_t size;

	if (spi_width > SPI_BIT_WIDTH_16_BITS) { /* Bit Widths of 17-32 bits */
		size = TRANSFER_SIZE_4_BYTE;
	} else if (spi_width > SPI_BIT_WIDTH_8_BITS) { /* Bit Widths of 9-16 bits*/
		size = TRANSFER_SIZE_2_BYTE;
	} else { /* Bit Widths of 4-8 bits */
		size = TRANSFER_SIZE_1_BYTE;
	}

	if (data->spi.p_cfg->p_transfer_rx) {
		/* When the rxi interrupt is called, all transfers will be finished. */
		data->spi.rx_count = data->data_len;

		transfer_instance_t *p_transfer_rx =
			(transfer_instance_t *)data->spi.p_cfg->p_transfer_rx;
		transfer_info_t *p_info = p_transfer_rx->p_cfg->p_info;

		/* Configure the receive DMA instance. */
		p_info->transfer_settings_word_b.size = size;
		p_info->length = (uint16_t)data->data_len;
		p_info->transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED;
		p_info->p_dest = data->ctx.rx_buf;

		if (NULL == data->ctx.rx_buf) {
			static uint32_t dummy_rx;

			p_info->transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED;
			p_info->p_dest = &dummy_rx;
		}

		p_transfer_rx->p_api->reconfigure(p_transfer_rx->p_ctrl, p_info);
	}

	if (data->spi.p_cfg->p_transfer_tx) {
		/* When the txi interrupt is called, all transfers will be finished. */
		data->spi.tx_count = data->data_len;

		transfer_instance_t *p_transfer_tx =
			(transfer_instance_t *)data->spi.p_cfg->p_transfer_tx;
		transfer_info_t *p_info = p_transfer_tx->p_cfg->p_info;

		/* Configure the transmit DMA instance. */
		p_info->transfer_settings_word_b.size = size;
		p_info->length = (uint16_t)data->data_len;
		p_info->transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED;
		p_info->p_src = data->ctx.tx_buf;

		if (NULL == data->ctx.tx_buf) {
			static uint32_t dummy_tx;

			p_info->transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_FIXED;
			p_info->p_src = &dummy_tx;
		}

		p_transfer_tx->p_api->reconfigure(p_transfer_tx->p_ctrl, p_info);
	}
#endif
	data->spi.p_regs->SPSRC = R_SPI_B0_SPSRC_SPTEFC_Msk;
}

static void ra_spi_rxi_isr(const struct device *dev)
{
#ifndef CONFIG_SPI_SLAVE
	ARG_UNUSED(dev);
	spi_b_rxi_isr();
#else
	struct ra_spi_data *data = dev->data;

	spi_b_rxi_isr();
	if (spi_context_is_slave(&data->ctx) && data->spi.rx_count == data->spi.count) {

		if (data->ctx.rx_buf != NULL && data->ctx.tx_buf != NULL) {
			data->ctx.recv_frames = MIN(spi_context_total_tx_len(&data->ctx),
						    spi_context_total_rx_len(&data->ctx));
		} else if (data->ctx.tx_buf == NULL) {
			data->ctx.recv_frames = data->data_len;
		} else {
			/* Do nothing */
		}

		R_BSP_IrqDisable(data->fsp_config.tei_irq);

		/* Writing 0 to SPE generatates a TXI IRQ. Disable the TXI IRQ.
		 * (See Section 38.2.1 SPI Control Register in the RA6T2 manual R01UH0886EJ0100).
		 */
		R_BSP_IrqDisable(data->fsp_config.txi_irq);

		/* Disable the SPI Transfer. */
		data->spi.p_regs->SPCR_b.SPE = 0;

		/* Re-enable the TXI IRQ and clear the pending IRQ. */
		R_BSP_IrqEnable(data->fsp_config.txi_irq);

		spi_context_cs_control(&data->ctx, false);
		spi_context_complete(&data->ctx, dev, 0);
	}
#endif
}

static void ra_spi_txi_isr(const struct device *dev)
{
	ARG_UNUSED(dev);
	spi_b_txi_isr();
}

static void ra_spi_tei_isr(const struct device *dev)
{
	struct ra_spi_data *data = dev->data;

	if (data->spi.rx_count == data->spi.count) {
		spi_context_update_rx(&data->ctx, 1, data->data_len);
	}
	if (data->spi.tx_count == data->spi.count) {
		spi_context_update_tx(&data->ctx, 1, data->data_len);
	}
	if (ra_spi_b_transfer_ongoing(data)) {
		R_ICU->IELSR_b[data->fsp_config.tei_irq].IR = 0U;
		ra_spi_retransmit(data);
	} else {
		spi_b_tei_isr();
	}
}

static void ra_spi_eri_isr(const struct device *dev)
{
	ARG_UNUSED(dev);
	spi_b_eri_isr();
}
#endif

#define _ELC_EVENT_SPI_RXI(channel) ELC_EVENT_SPI##channel##_RXI
#define _ELC_EVENT_SPI_TXI(channel) ELC_EVENT_SPI##channel##_TXI
#define _ELC_EVENT_SPI_TEI(channel) ELC_EVENT_SPI##channel##_TEI
#define _ELC_EVENT_SPI_ERI(channel) ELC_EVENT_SPI##channel##_ERI

#define ELC_EVENT_SPI_RXI(channel) _ELC_EVENT_SPI_RXI(channel)
#define ELC_EVENT_SPI_TXI(channel) _ELC_EVENT_SPI_TXI(channel)
#define ELC_EVENT_SPI_TEI(channel) _ELC_EVENT_SPI_TEI(channel)
#define ELC_EVENT_SPI_ERI(channel) _ELC_EVENT_SPI_ERI(channel)

#if defined(CONFIG_SPI_B_INTERRUPT)

#define RA_SPI_B_IRQ_CONFIG_INIT(index)                                                            \
	do {                                                                                       \
		ARG_UNUSED(dev);                                                                   \
                                                                                                   \
		R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, rxi, irq)] =                               \
			ELC_EVENT_SPI_RXI(DT_INST_PROP(index, channel));                           \
		R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, txi, irq)] =                               \
			ELC_EVENT_SPI_TXI(DT_INST_PROP(index, channel));                           \
		R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, tei, irq)] =                               \
			ELC_EVENT_SPI_TEI(DT_INST_PROP(index, channel));                           \
		R_ICU->IELSR[DT_INST_IRQ_BY_NAME(index, eri, irq)] =                               \
			ELC_EVENT_SPI_ERI(DT_INST_PROP(index, channel));                           \
                                                                                                   \
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, rxi, irq),                                  \
			    DT_INST_IRQ_BY_NAME(index, rxi, priority), ra_spi_rxi_isr,             \
			    DEVICE_DT_INST_GET(index), 0);                                         \
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, txi, irq),                                  \
			    DT_INST_IRQ_BY_NAME(index, txi, priority), ra_spi_txi_isr,             \
			    DEVICE_DT_INST_GET(index), 0);                                         \
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, tei, irq),                                  \
			    DT_INST_IRQ_BY_NAME(index, tei, priority), ra_spi_tei_isr,             \
			    DEVICE_DT_INST_GET(index), 0);                                         \
		IRQ_CONNECT(DT_INST_IRQ_BY_NAME(index, eri, irq),                                  \
			    DT_INST_IRQ_BY_NAME(index, eri, priority), ra_spi_eri_isr,             \
			    DEVICE_DT_INST_GET(index), 0);                                         \
                                                                                                   \
		irq_enable(DT_INST_IRQ_BY_NAME(index, rxi, irq));                                  \
		irq_enable(DT_INST_IRQ_BY_NAME(index, txi, irq));                                  \
		irq_enable(DT_INST_IRQ_BY_NAME(index, eri, irq));                                  \
	} while (0)

#else

#define RA_SPI_B_IRQ_CONFIG_INIT(index)

#endif

#ifndef CONFIG_SPI_B_RA_DTC
#define RA_SPI_B_DTC_STRUCT_INIT(index)
#define RA_SPI_B_DTC_INIT(index)
#else
#define RA_SPI_B_DTC_INIT(index)                                                                   \
	do {                                                                                       \
		if (DT_INST_PROP_OR(index, rx_dtc, false)) {                                       \
			ra_spi_data_##index.fsp_config.p_transfer_rx =                             \
				&ra_spi_data_##index.rx_transfer;                                  \
		}                                                                                  \
		if (DT_INST_PROP_OR(index, tx_dtc, false)) {                                       \
			ra_spi_data_##index.fsp_config.p_transfer_tx =                             \
				&ra_spi_data_##index.tx_transfer;                                  \
		}                                                                                  \
	} while (0)
#define RA_SPI_B_DTC_STRUCT_INIT(index)                                                            \
	.rx_transfer_info =                                                                        \
		{                                                                                  \
			.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED, \
			.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_DESTINATION,  \
			.transfer_settings_word_b.irq = TRANSFER_IRQ_END,                          \
			.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED,       \
			.transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_FIXED,        \
			.transfer_settings_word_b.size = TRANSFER_SIZE_1_BYTE,                     \
			.transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL,                     \
			.p_dest = (void *)NULL,                                                    \
			.p_src = (void const *)NULL,                                               \
			.num_blocks = 0,                                                           \
			.length = 0,                                                               \
	},                                                                                         \
	.rx_transfer_cfg_extend = {.activation_source = DT_INST_IRQ_BY_NAME(index, rxi, irq)},     \
	.rx_transfer_cfg =                                                                         \
		{                                                                                  \
			.p_info = &ra_spi_data_##index.rx_transfer_info,                           \
			.p_extend = &ra_spi_data_##index.rx_transfer_cfg_extend,                   \
	},                                                                                         \
	.rx_transfer =                                                                             \
		{                                                                                  \
			.p_ctrl = &ra_spi_data_##index.rx_transfer_ctrl,                           \
			.p_cfg = &ra_spi_data_##index.rx_transfer_cfg,                             \
			.p_api = &g_transfer_on_dtc,                                               \
	},                                                                                         \
	.tx_transfer_info =                                                                        \
		{                                                                                  \
			.transfer_settings_word_b.dest_addr_mode = TRANSFER_ADDR_MODE_FIXED,       \
			.transfer_settings_word_b.repeat_area = TRANSFER_REPEAT_AREA_SOURCE,       \
			.transfer_settings_word_b.irq = TRANSFER_IRQ_END,                          \
			.transfer_settings_word_b.chain_mode = TRANSFER_CHAIN_MODE_DISABLED,       \
			.transfer_settings_word_b.src_addr_mode = TRANSFER_ADDR_MODE_INCREMENTED,  \
			.transfer_settings_word_b.size = TRANSFER_SIZE_1_BYTE,                     \
			.transfer_settings_word_b.mode = TRANSFER_MODE_NORMAL,                     \
			.p_dest = (void *)NULL,                                                    \
			.p_src = (void const *)NULL,                                               \
			.num_blocks = 0,                                                           \
			.length = 0,                                                               \
	},                                                                                         \
	.tx_transfer_cfg_extend = {.activation_source = DT_INST_IRQ_BY_NAME(index, txi, irq)},     \
	.tx_transfer_cfg =                                                                         \
		{                                                                                  \
			.p_info = &ra_spi_data_##index.tx_transfer_info,                           \
			.p_extend = &ra_spi_data_##index.tx_transfer_cfg_extend,                   \
	},                                                                                         \
	.tx_transfer = {                                                                           \
		.p_ctrl = &ra_spi_data_##index.tx_transfer_ctrl,                                   \
		.p_cfg = &ra_spi_data_##index.tx_transfer_cfg,                                     \
		.p_api = &g_transfer_on_dtc,                                                       \
	},
#endif

#define RA_SPI_INIT(index)                                                                         \
                                                                                                   \
	PINCTRL_DT_INST_DEFINE(index);                                                             \
                                                                                                   \
	static const struct ra_spi_config ra_spi_config_##index = {                                \
		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(index),                                     \
		.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(index)),                            \
		.clock_subsys =                                                                    \
			{                                                                          \
				.mstp = (uint32_t)DT_INST_CLOCKS_CELL_BY_NAME(index, spiclk,       \
									      mstp),               \
				.stop_bit = DT_INST_CLOCKS_CELL_BY_NAME(index, spiclk, stop_bit),  \
			},                                                                         \
	};                                                                                         \
                                                                                                   \
	static struct ra_spi_data ra_spi_data_##index = {                                          \
		SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(index), ctx)                           \
			SPI_CONTEXT_INIT_LOCK(ra_spi_data_##index, ctx),                           \
		SPI_CONTEXT_INIT_SYNC(ra_spi_data_##index, ctx),                                   \
		.fsp_config =                                                                      \
			{                                                                          \
				.channel = DT_INST_PROP(index, channel),                           \
				.rxi_ipl = DT_INST_IRQ_BY_NAME(index, rxi, priority),              \
				.rxi_irq = DT_INST_IRQ_BY_NAME(index, rxi, irq),                   \
				.txi_ipl = DT_INST_IRQ_BY_NAME(index, txi, priority),              \
				.txi_irq = DT_INST_IRQ_BY_NAME(index, txi, irq),                   \
				.tei_ipl = DT_INST_IRQ_BY_NAME(index, tei, priority),              \
				.tei_irq = DT_INST_IRQ_BY_NAME(index, tei, irq),                   \
				.eri_ipl = DT_INST_IRQ_BY_NAME(index, eri, priority),              \
				.eri_irq = DT_INST_IRQ_BY_NAME(index, eri, irq),                   \
			},                                                                         \
		RA_SPI_B_DTC_STRUCT_INIT(index)};                                                  \
                                                                                                   \
	static int spi_b_ra_init##index(const struct device *dev)                                  \
	{                                                                                          \
		RA_SPI_B_DTC_INIT(index);                                                          \
		int err = spi_b_ra_init(dev);                                                      \
		if (err != 0) {                                                                    \
			return err;                                                                \
		}                                                                                  \
		RA_SPI_B_IRQ_CONFIG_INIT(index);                                                   \
		return 0;                                                                          \
	}                                                                                          \
                                                                                                   \
	DEVICE_DT_INST_DEFINE(index, spi_b_ra_init##index, PM_DEVICE_DT_INST_GET(index),           \
			      &ra_spi_data_##index, &ra_spi_config_##index, POST_KERNEL,           \
			      CONFIG_SPI_INIT_PRIORITY, &ra_spi_driver_api);

DT_INST_FOREACH_STATUS_OKAY(RA_SPI_INIT)
