diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 36c21ba530a..f6adb64ecd3 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -52,6 +52,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_AMBIQ_BLEIF spi_ambiq_bleif.c) zephyr_library_sources_ifdef(CONFIG_SPI_RPI_PICO_PIO spi_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_SPI_MCHP_MSS spi_mchp_mss.c) zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RA8 spi_b_renesas_ra8.c) +zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RA spi_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_SPI_RTIO spi_rtio.c) zephyr_library_sources_ifdef(CONFIG_SPI_ASYNC spi_signal.c) zephyr_library_sources_ifdef(CONFIG_SPI_ITE_IT8XXX2 spi_it8xxx2.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 18345365b25..e718e93f653 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -165,4 +165,6 @@ source "drivers/spi/Kconfig.renesas_ra8" source "drivers/spi/Kconfig.it8xxx2" +source "drivers/spi/Kconfig.renesas_ra" + endif # SPI diff --git a/drivers/spi/Kconfig.renesas_ra b/drivers/spi/Kconfig.renesas_ra new file mode 100644 index 00000000000..f0e012d98ec --- /dev/null +++ b/drivers/spi/Kconfig.renesas_ra @@ -0,0 +1,35 @@ +# Renesas RA Family + +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SPI_RENESAS_RA + bool "Renesas RA SPI" + default y + depends on DT_HAS_RENESAS_RA_SPI_ENABLED + select USE_RA_FSP_SPI + select PINCTRL + help + Enable Renesas RA SPI Driver. + +if SPI_RENESAS_RA + +config SPI_INTERRUPT + bool "RA MCU SPI Interrupt Support" + help + Enable Interrupt support for the SPI Driver of RA family. + +config SPI_RA_DTC + bool "RA MCU SPI DTC Support" + select USE_RA_FSP_DTC + help + Enable the SPI DTC mode for SPI instances + +config SPI_USE_HW_SS + bool "RA MCU SPI Hardware Slave Select support" + default y + depends on !SOC_SERIES_RA2A1 + help + Use Slave Select pin instead of software Slave Select. + +endif # SPI_RENESAS_RA diff --git a/drivers/spi/spi_renesas_ra.c b/drivers/spi/spi_renesas_ra.c new file mode 100644 index 00000000000..75d830c737c --- /dev/null +++ b/drivers/spi/spi_renesas_ra.c @@ -0,0 +1,826 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_spi + +#include +#include +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(ra_spi); + +#include "spi_context.h" + +#if defined(CONFIG_SPI_INTERRUPT) +void spi_rxi_isr(void); +void spi_txi_isr(void); +void spi_tei_isr(void); +void spi_eri_isr(void); +#endif + +struct ra_spi_config { + const struct pinctrl_dev_config *pcfg; +}; + +struct ra_spi_data { + struct spi_context ctx; + uint8_t dfs; + struct st_spi_instance_ctrl spi; + struct st_spi_cfg fsp_config; + struct st_spi_extended_cfg fsp_config_extend; +#if CONFIG_SPI_INTERRUPT + uint32_t data_len; +#endif +#if defined(CONFIG_SPI_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_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; + } + + if (data->spi.open != 0) { + R_SPI_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 { + if (data->fsp_config.operating_mode == SPI_MODE_MASTER) { + data->fsp_config.clk_phase = SPI_CLK_PHASE_EDGE_ODD; + } else { + LOG_ERR("Invalid clock phase"); + return -EINVAL; + } + } + + 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->operation & SPI_CS_ACTIVE_HIGH) { + data->fsp_config_extend.ssl_polarity = SPI_SSLP_HIGH; + } else { + data->fsp_config_extend.ssl_polarity = SPI_SSLP_LOW; + } + + if (!(config->operation & SPI_OP_MODE_SLAVE)) { + LOG_INF("frequency: %d", config->frequency); + fsp_err = R_SPI_CalculateBitrate(config->frequency, + &data->fsp_config_extend.spck_div); + if (fsp_err != FSP_SUCCESS) { + LOG_ERR("spi frequency calculate error %d", fsp_err); + return -EIO; + } + } + + data->fsp_config_extend.spi_comm = SPI_COMMUNICATION_FULL_DUPLEX; + if (spi_cs_is_gpio(config) || !IS_ENABLED(CONFIG_SPI_USE_HW_SS)) { + data->fsp_config_extend.spi_clksyn = SPI_SSL_MODE_CLK_SYN; + } else { + data->fsp_config_extend.spi_clksyn = SPI_SSL_MODE_SPI; + data->fsp_config_extend.ssl_select = SPI_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_Open(&data->spi, &data->fsp_config); + if (fsp_err != FSP_SUCCESS) { + LOG_ERR("R_SPI_Open error: %d", fsp_err); + return -EIO; + } + data->ctx.config = config; + + return 0; +} + +static bool ra_spi_transfer_ongoing(struct ra_spi_data *data) +{ +#if defined(CONFIG_SPI_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_INTERRUPT +static int ra_spi_transceive_slave(struct ra_spi_data *data) +{ + R_SPI0_Type *p_spi_reg = data->spi.p_regs; + spi_bit_width_t spi_width = + (spi_bit_width_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1); + + if (p_spi_reg->SPSR_b.SPTEF && spi_context_tx_buf_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; + } + + /* Update data register */ + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + p_spi_reg->SPDR = (uint32_t)tx; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + p_spi_reg->SPDR_BY = (uint8_t)tx; + } else { + p_spi_reg->SPDR_HA = (uint16_t)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; + + /* Update RX data */ + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + rx = (uint32_t)p_spi_reg->SPDR; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + rx = (uint8_t)p_spi_reg->SPDR_BY; + } else { + rx = (uint16_t)p_spi_reg->SPDR_HA; + } + + 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_transceive_master(struct ra_spi_data *data) +{ + R_SPI0_Type *p_spi_reg = data->spi.p_regs; + spi_bit_width_t spi_width = + (spi_bit_width_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1); + 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) { + } + + /* Update data register */ + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + p_spi_reg->SPDR = (uint32_t)tx; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + p_spi_reg->SPDR_BY = (uint8_t)tx; + } else { + p_spi_reg->SPDR_HA = (uint16_t)tx; + } + + spi_context_update_tx(&data->ctx, data->dfs, 1); + + if (p_spi_reg->SPCR_b.TXMD == 0x0) { + while (!p_spi_reg->SPSR_b.SPRF) { + } + + /* Update RX data */ + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + rx = (uint32_t)p_spi_reg->SPDR; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + rx = (uint8_t)p_spi_reg->SPDR_BY; + } else { + rx = (uint16_t)p_spi_reg->SPDR_HA; + } + + if (spi_context_rx_buf_on(&data->ctx)) { + 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_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_transceive_master(data); + } else { + ra_spi_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_SPI0_Type *p_spi_reg; + int ret = 0; + + if (!tx_bufs && !rx_bufs) { + return 0; + } + +#ifndef CONFIG_SPI_INTERRUPT + if (asynchronous) { + return -ENOTSUP; + } +#endif + + spi_context_lock(&data->ctx, asynchronous, cb, userdata, config); + + ret = ra_spi_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; + spi_bit_width_t spi_width = + (spi_bit_width_t)(SPI_WORD_SIZE_GET(data->ctx.config->operation) - 1); + /* 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_INTERRUPT + 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_Write(&data->spi, data->ctx.tx_buf, data->data_len, spi_width); + } else if (data->ctx.tx_buf == NULL) { + R_SPI_Read(&data->spi, data->ctx.rx_buf, data->data_len, spi_width); + } else { + R_SPI_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); + if (!spi_context_tx_on(&data->ctx)) { + p_spi_reg->SPCR_b.TXMD = 0x0; + } + if (!spi_context_rx_on(&data->ctx)) { + p_spi_reg->SPCR_b.TXMD = 0x1; /* tx only */ + } + + uint32_t spdcr = p_spi_reg->SPDCR; + + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + /* Configure Word access to data register. */ + spdcr &= ~R_SPI0_SPDCR_SPBYT_Msk; + spdcr |= R_SPI0_SPDCR_SPLW_Msk; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + /* Set SPBYT so 8bit transfer works with the DTC/DMAC. */ + spdcr |= R_SPI0_SPDCR_SPBYT_Msk; + } else { + /* Configure Half-Word access to data register. */ + spdcr &= ~(R_SPI0_SPDCR_SPBYT_Msk | R_SPI0_SPDCR_SPLW_Msk); + } + + /* Configure data length based on the selected bit width . */ + uint32_t bit_width = spi_width; + + if (bit_width > SPI_BIT_WIDTH_16_BITS) { + bit_width = ((bit_width + 1) >> 2) - 5; + } + p_spi_reg->SPDCR = (uint8_t)spdcr; + p_spi_reg->SPCMD[0] |= bit_width << 8; + + /* Enable the SPI Transfer. */ + p_spi_reg->SPCR |= R_SPI0_SPCR_SPE_Msk; + + do { + ra_spi_transceive_data(data); + } while (ra_spi_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; + + spi_context_cs_control(&data->ctx, false); + spi_context_complete(&data->ctx, dev, 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_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_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_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_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = ra_spi_transceive_async, +#endif /* CONFIG_SPI_ASYNC */ + .release = ra_spi_release}; + +static int spi_ra_init(const struct device *dev) +{ + const struct ra_spi_config *config = dev->config; + struct ra_spi_data *data = dev->data; + int ret; + + /* 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_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_RA_DTC + /* Determine DTC transfer size */ + transfer_size_t size; + + if (SPI_BIT_WIDTH_16_BITS < spi_width) { + size = TRANSFER_SIZE_4_BYTE; + } else if (SPI_BIT_WIDTH_8_BITS >= spi_width) { + size = TRANSFER_SIZE_1_BYTE; + } else { + size = TRANSFER_SIZE_2_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; + } + R_SPI0_Type *p_spi_reg = data->spi.p_regs; + + p_transfer_tx->p_api->reconfigure(p_transfer_tx->p_ctrl, p_info); + + /* Enable the SPI Transfer */ + p_spi_reg->SPCR |= R_SPI0_SPCR_SPE_Msk; + } + +#endif +} + +static void ra_spi_rxi_isr(const struct device *dev) +{ +#ifndef CONFIG_SPI_SLAVE + ARG_UNUSED(dev); + spi_rxi_isr(); +#else + struct ra_spi_data *data = dev->data; + + spi_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; + } + 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_txi_isr(); +} + +static void ra_spi_tei_isr(const struct device *dev) +{ + struct ra_spi_data *data = dev->data; + R_SPI0_Type *p_spi_reg = data->spi.p_regs; + + 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_transfer_ongoing(data)) { + R_BSP_IrqDisable(data->spi.p_cfg->txi_irq); + /* Disable the SPI Transfer. */ + p_spi_reg->SPCR_b.SPE = 0U; + /* Clear the status register. */ + p_spi_reg->SPSR; + p_spi_reg->SPSR = 0; + R_BSP_IrqEnable(data->spi.p_cfg->txi_irq); + +#ifndef CONFIG_SPI_RA_DTC + /* Enable the SPI Transfer */ + p_spi_reg->SPCR |= R_SPI0_SPCR_SPE_Msk; +#endif + + R_ICU->IELSR_b[data->fsp_config.tei_irq].IR = 0U; + ra_spi_retransmit(data); + } else { + spi_tei_isr(); + } +} + +static void ra_spi_eri_isr(const struct device *dev) +{ + ARG_UNUSED(dev); + spi_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_INTERRUPT) + +#define RA_SPI_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_IRQ_CONFIG_INIT(index) + +#endif + +#ifndef CONFIG_SPI_RA_DTC +#define RA_SPI_DTC_STRUCT_INIT(index) +#define RA_SPI_DTC_INIT(index) +#else +#define RA_SPI_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_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), \ + }; \ + \ + 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_DTC_STRUCT_INIT(index)}; \ + \ + static int spi_ra_init##index(const struct device *dev) \ + { \ + RA_SPI_DTC_INIT(index); \ + int err = spi_ra_init(dev); \ + if (err != 0) { \ + return err; \ + } \ + RA_SPI_IRQ_CONFIG_INIT(index); \ + return 0; \ + } \ + \ + DEVICE_DT_INST_DEFINE(index, spi_ra_init##index, PM_DEVICE_DT_INST_GET(index), \ + &ra_spi_data_##index, &ra_spi_config_##index, PRE_KERNEL_1, \ + CONFIG_SPI_INIT_PRIORITY, &ra_spi_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(RA_SPI_INIT) diff --git a/dts/bindings/spi/renesas,ra-spi.yaml b/dts/bindings/spi/renesas,ra-spi.yaml new file mode 100644 index 00000000000..b5ac343e972 --- /dev/null +++ b/dts/bindings/spi/renesas,ra-spi.yaml @@ -0,0 +1,34 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA SPI controller + +compatible: "renesas,ra-spi" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + channel: + type: int + required: true + + reg: + required: true + + tx-dtc: + type: boolean + + rx-dtc: + type: boolean + + pinctrl-0: + required: true + + pinctrl-names: + required: true + + interrupts: + required: true + + interrupt-names: + required: true diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index 9e8de7afed8..9a1f7969385 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -15,6 +15,11 @@ config USE_RA_FSP_SCI_B_UART help Enable RA FSP SCI-B UART driver +config USE_RA_FSP_SPI + bool + help + Enable RA FSP SPI driver + config USE_RA_FSP_DTC bool help