From e7c025db24f3acd7da55636c1daa0993eb4cb655 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Wed, 18 Dec 2024 06:35:16 +0700 Subject: [PATCH] drivers: spi: Initial support SPI driver on Renesas RX130 Initial commit for SPI driver support on RSK_RX130@512KB board with RSPI module Signed-off-by: Duy Nguyen Signed-off-by: Minh Tang --- drivers/spi/CMakeLists.txt | 1 + drivers/spi/Kconfig | 1 + drivers/spi/Kconfig.renesas_rx | 32 + drivers/spi/spi_renesas_rx.c | 869 ++++++++++++++++++++++++++ dts/bindings/spi/renesas,rx-rspi.yaml | 38 ++ dts/rx/renesas/rx130-common.dtsi | 12 + modules/Kconfig.renesas | 5 + 7 files changed, 958 insertions(+) create mode 100644 drivers/spi/Kconfig.renesas_rx create mode 100644 drivers/spi/spi_renesas_rx.c create mode 100644 dts/bindings/spi/renesas,rx-rspi.yaml diff --git a/drivers/spi/CMakeLists.txt b/drivers/spi/CMakeLists.txt index 20de5467f42..cac795b2900 100644 --- a/drivers/spi/CMakeLists.txt +++ b/drivers/spi/CMakeLists.txt @@ -50,6 +50,7 @@ zephyr_library_sources_ifdef(CONFIG_SPI_PSOC6 spi_psoc6.c) zephyr_library_sources_ifdef(CONFIG_SPI_PW spi_pw.c) zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RA spi_renesas_ra.c) zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RA8 spi_b_renesas_ra8.c) +zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RX spi_renesas_rx.c) zephyr_library_sources_ifdef(CONFIG_SPI_RENESAS_RZ_RSPI spi_renesas_rz_rspi.c) zephyr_library_sources_ifdef(CONFIG_SPI_RPI_PICO_PIO spi_rpi_pico_pio.c) zephyr_library_sources_ifdef(CONFIG_SPI_RV32M1_LPSPI spi_rv32m1_lpspi.c) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 75e99752d00..285b20b11f6 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -128,6 +128,7 @@ source "drivers/spi/Kconfig.psoc6" source "drivers/spi/Kconfig.pw" source "drivers/spi/Kconfig.renesas_ra" source "drivers/spi/Kconfig.renesas_ra8" +source "drivers/spi/Kconfig.renesas_rx" source "drivers/spi/Kconfig.renesas_rz" source "drivers/spi/Kconfig.rpi_pico" source "drivers/spi/Kconfig.rv32m1_lpspi" diff --git a/drivers/spi/Kconfig.renesas_rx b/drivers/spi/Kconfig.renesas_rx new file mode 100644 index 00000000000..7bd23ae151b --- /dev/null +++ b/drivers/spi/Kconfig.renesas_rx @@ -0,0 +1,32 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config SPI_RENESAS_RX + bool "Renesas RX Series SPI Driver" + default y + depends on DT_HAS_RENESAS_RX_RSPI_ENABLED + select USE_RX_RDP_RSPI + help + Enable Renesas RX series SPI driver. + +if SPI_RENESAS_RX + +config SPI_RENESAS_RX_INTERRUPT + bool "RX MCU RSPI Interrupt Support" + default y + help + Enable Interrupt Support for the RSPI Driver of RX family. + +config SPI_RENESAS_RX_USE_HW_SS + bool "RX MCU RSPI Hardware Slave Select support" + default y + help + Use Slave Select pin instead of software Slave Select. + +config SPI_RENESAS_RX_HIGH_SPEED_READ + bool "RX MCU RSPI high speed read mode" + default y + help + Enable high speed read mode of RX RSPI Driver + +endif # SPI_RENESAS_RX diff --git a/drivers/spi/spi_renesas_rx.c b/drivers/spi/spi_renesas_rx.c new file mode 100644 index 00000000000..024bb77992e --- /dev/null +++ b/drivers/spi/spi_renesas_rx.c @@ -0,0 +1,869 @@ +/* + * Copyright (c) 2025 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_rx_rspi + +#include +#include +#include +#include +#include "r_rspi_rx_if.h" +#include "iodefine.h" + +#include +LOG_MODULE_REGISTER(rx_rspi); + +#include "spi_context.h" + +#if defined(CONFIG_SPI_RENESAS_RX_INTERRUPT) +typedef enum { + /* Values will be used as bit flags.*/ + RSPI_DO_TX = 0x1, + RSPI_DO_RX = 0x2, + RSPI_DO_TX_RX = 0x3 +} rspi_operation_t; + +typedef struct rx_rspi_tcb_s { + rspi_operation_t transfer_mode; + rspi_str_tranmode_t data_tran_mode; + uint16_t tx_count; + uint16_t rx_count; + uint16_t xfr_length; + uint8_t bytes_per_transfer; + bool do_rx_now; + bool do_tx; +} rx_rspi_tcb_t; + +static uint8_t rspi_get_data_type(rspi_command_word_t command_word) +{ + switch (command_word.bit_length) { + case RSPI_SPCMD_BIT_LENGTH_8: + return RSPI_BYTE_DATA; + + case RSPI_SPCMD_BIT_LENGTH_9: + case RSPI_SPCMD_BIT_LENGTH_10: + case RSPI_SPCMD_BIT_LENGTH_11: + case RSPI_SPCMD_BIT_LENGTH_12: + case RSPI_SPCMD_BIT_LENGTH_13: + case RSPI_SPCMD_BIT_LENGTH_14: + case RSPI_SPCMD_BIT_LENGTH_15: + case RSPI_SPCMD_BIT_LENGTH_16: + return RSPI_WORD_DATA; + + case RSPI_SPCMD_BIT_LENGTH_20: + case RSPI_SPCMD_BIT_LENGTH_24: + case RSPI_SPCMD_BIT_LENGTH_32: + return RSPI_LONG_DATA; + + default: + return 0; + } +} /* End of function rspi_get_data_type */ + +#endif /* CONFIG_SPI_RENESAS_RX_INTERRUPT */ + +struct rx_rspi_data { + struct spi_context ctx; + int channel_id; + int ssl_assert; + volatile struct st_rspi *preg; + rspi_handle_t rspi; + rspi_chnl_settings_t channel_setting; + rspi_command_word_t command_word; + rspi_callback_data_t callback_data; + uint8_t dfs; +#if CONFIG_SPI_RENESAS_RX_INTERRUPT + rx_rspi_tcb_t tcb; + uint32_t rxdata; + uint32_t data_len; +#endif +}; + +struct rx_rspi_config { + const struct pinctrl_dev_config *pcfg; +}; + +static void spi_cb(void *p_args) +{ + struct device *dev = (struct device *)p_args; + struct rx_rspi_data *data = dev->data; + + switch (data->callback_data.event_code) { + case RSPI_EVT_TRANSFER_COMPLETE: + spi_context_cs_control(&data->ctx, false); + spi_context_complete(&data->ctx, dev, 0); + break; + case RSPI_EVT_TRANSFER_ABORTED: + case RSPI_EVT_ERR_MODE_FAULT: + case RSPI_EVT_ERR_READ_OVF: + case RSPI_EVT_ERR_PARITY: + case RSPI_EVT_ERR_UNDER_RUN: + case RSPI_EVT_ERR_UNDEF: + spi_context_cs_control(&data->ctx, false); + spi_context_complete(&data->ctx, dev, -EIO); + break; + default: + break; + } +} + +#if defined(CONFIG_SPI_RENESAS_RX_INTERRUPT) +static void transmit_data(struct rx_rspi_data *data, uint16_t tx_count) +{ + rx_rspi_tcb_t *rspi_tcb = &(data->tcb); + void *psrc = (void *)data->ctx.tx_buf; + uint8_t data_size = rspi_tcb->bytes_per_transfer; + + if (rspi_tcb->do_tx) { + if (RSPI_BYTE_DATA == data_size) { + data->preg->SPDR.LONG = ((uint8_t *)psrc)[tx_count]; + } else if (RSPI_WORD_DATA == data_size) { + data->preg->SPDR.LONG = ((uint16_t *)psrc)[tx_count]; + } else { + data->preg->SPDR.LONG = ((uint32_t *)psrc)[tx_count]; + } + } else { + data->preg->SPDR.LONG = 0; + } +} + +void rx_rspi_spti_sub(const struct device *dev) +{ + struct rx_rspi_data *data = dev->data; + rx_rspi_tcb_t *rspi_tcb = &(data->tcb); + uint16_t tx_count = rspi_tcb->tx_count; + + /* If transmit only, enable SPII interrupt in transmit */ + if ((!spi_context_rx_on(&data->ctx)) && (tx_count == rspi_tcb->xfr_length - 1)) { + data->preg->SPCR2.BIT.SPIIE = 1; + + /* If the SPI is in slave mode */ + if (spi_context_is_slave(&data->ctx)) { + /* Disable RSPI */ + data->preg->SPCR.BIT.SPE = 0; + + /** + * Transfer complete. Call the user callback function passing + * pointer to the result structure. + */ + if (data->rspi->pcallback != NULL) { + data->callback_data.handle = data->rspi; + data->callback_data.event_code = RSPI_EVT_TRANSFER_COMPLETE; + data->rspi->pcallback((void *)dev); + } + } + } + + /* Service the hardware first to keep it busy. */ + /* Feed the TX. */ + if (tx_count < rspi_tcb->xfr_length) { + transmit_data(data, tx_count); + rspi_tcb->tx_count++; + } else { + if (spi_context_is_slave(&data->ctx)) { + spi_context_update_tx(&data->ctx, data->dfs, data->data_len); + } + } +} +#endif + +static int rx_rspi_configure(const struct device *dev, const struct spi_config *config) +{ + struct rx_rspi_data *data = dev->data; + rspi_err_t err; + + if (spi_context_configured(&data->ctx, config)) { + /* Nothing to do */ + return 0; + } + + err = R_RSPI_Close(data->rspi); + + if ((config->operation & SPI_FRAME_FORMAT_TI) == SPI_FRAME_FORMAT_TI) { + return -ENOTSUP; + } + + if (config->operation & SPI_OP_MODE_SLAVE) { + data->channel_setting.master_slave_mode = RSPI_MS_MODE_SLAVE; + } else { + data->channel_setting.master_slave_mode = RSPI_MS_MODE_MASTER; + } + + if (SPI_MODE_GET(config->operation) & SPI_MODE_CPOL) { + data->command_word.cpol = RSPI_SPCMD_CPOL_IDLE_HI; + } else { + data->command_word.cpol = RSPI_SPCMD_CPOL_IDLE_LO; + } + + if (SPI_MODE_GET(config->operation) & SPI_MODE_CPHA) { + data->command_word.cpha = RSPI_SPCMD_CPHA_SAMPLE_EVEN; + } else { + if (data->channel_setting.master_slave_mode == RSPI_MS_MODE_MASTER) { + data->command_word.cpha = RSPI_SPCMD_CPHA_SAMPLE_ODD; + } else { + /* In slave mode cpha must be 1 */ + LOG_ERR("Invalid clock phase"); + return -EINVAL; + } + } + + if (config->operation & SPI_TRANSFER_LSB) { + data->command_word.bit_order = RSPI_SPCMD_ORDER_LSB_FIRST; + } else { + data->command_word.bit_order = RSPI_SPCMD_ORDER_MSB_FIRST; + } + + if (spi_cs_is_gpio(config) || !IS_ENABLED(CONFIG_SPI_RENESAS_RX_USE_HW_SS)) { + data->channel_setting.gpio_ssl = RSPI_IF_MODE_3WIRE; + } else { + data->channel_setting.gpio_ssl = RSPI_IF_MODE_4WIRE; + switch (data->ssl_assert) { + case 0: + data->command_word.ssl_assert = RSPI_SPCMD_ASSERT_SSL0; + break; + case 1: + data->command_word.ssl_assert = RSPI_SPCMD_ASSERT_SSL1; + break; + case 2: + data->command_word.ssl_assert = RSPI_SPCMD_ASSERT_SSL2; + break; + case 3: + data->command_word.ssl_assert = RSPI_SPCMD_ASSERT_SSL3; + break; + default: + LOG_ERR("Invalid SSL"); + return -EINVAL; + } + } + + data->channel_setting.bps_target = config->frequency; + data->channel_setting.tran_mode = RSPI_TRANS_MODE_SW; + + uint8_t array_rspi_bit_length[] = { + RSPI_SPCMD_BIT_LENGTH_8, RSPI_SPCMD_BIT_LENGTH_9, RSPI_SPCMD_BIT_LENGTH_10, + RSPI_SPCMD_BIT_LENGTH_11, RSPI_SPCMD_BIT_LENGTH_12, RSPI_SPCMD_BIT_LENGTH_13, + RSPI_SPCMD_BIT_LENGTH_14, RSPI_SPCMD_BIT_LENGTH_15, RSPI_SPCMD_BIT_LENGTH_16}; + + uint16_t bit_frame_size = SPI_WORD_SIZE_GET(config->operation); + + if (bit_frame_size >= 8 && bit_frame_size <= 16) { + data->command_word.bit_length = array_rspi_bit_length[bit_frame_size - 8]; + } else { + switch (bit_frame_size) { + case 20: + data->command_word.bit_length = RSPI_SPCMD_BIT_LENGTH_20; + break; + case 24: + data->command_word.bit_length = RSPI_SPCMD_BIT_LENGTH_24; + break; + case 32: + data->command_word.bit_length = RSPI_SPCMD_BIT_LENGTH_32; + break; + default: + return -ENOTSUP; + } + } + + err = R_RSPI_Open(data->channel_id, &data->channel_setting, data->command_word, spi_cb, + &data->rspi); + if (err != RSPI_SUCCESS) { + LOG_ERR("R_RSPI_Open error: %d", err); + return -EINVAL; +#if defined(CONFIG_SPI_RENESAS_RX_INTERRUPT) + } else { + data->tcb.data_tran_mode = data->channel_setting.tran_mode; +#endif + } + /* Manually set these bits, because the Open function not */ + data->preg->SPCMD0.BIT.CPHA = data->command_word.cpha; + data->preg->SPCMD0.BIT.LSBF = data->command_word.bit_order; + data->preg->SPCMD0.BIT.SSLA = data->command_word.ssl_assert; + + /* Set the ctx config = config for next time enter the function */ + data->ctx.config = config; + + return 0; +} + +static bool rx_spi_transfer_ongoing(struct rx_rspi_data *data) +{ +#if defined(CONFIG_SPI_RENESAS_RX_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_RENESAS_RX_INTERRUPT +static int rx_rspi_transceive_slave(struct rx_rspi_data *data) +{ + if (data->preg->SPSR.BIT.SPTEF && spi_context_tx_on(&data->ctx)) { + uint32_t tx; + + if (data->ctx.tx_buf != NULL) { + if (data->dfs > 2) { + tx = UNALIGNED_GET((uint32_t *)(data->ctx.tx_buf)); + } else if (data->dfs > 1) { + tx = UNALIGNED_GET((uint16_t *)(data->ctx.tx_buf)); + } else { + tx = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf)); + } + } else { + tx = 0; + } + /* Write a specific number of frame clear the SPTEF bit */ + data->preg->SPDR.LONG = tx; + spi_context_update_tx(&data->ctx, data->dfs, 1); + } else { + data->preg->SPCR.BIT.SPTIE = 0; + } + + uint32_t rx; + + if (data->preg->SPSR.BIT.SPRF && spi_context_rx_buf_on(&data->ctx)) { + /* Read data from the Data Reg make the receive full flag being cleared */ + rx = data->preg->SPDR.LONG; + 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 rx_rspi_transceive_master(struct rx_rspi_data *data) +{ + uint32_t tx; + uint32_t rx; + + if (spi_context_tx_buf_on(&data->ctx)) { + if (data->dfs > 2) { + tx = UNALIGNED_GET((uint32_t *)(data->ctx.tx_buf)); + } else if (data->dfs > 1) { + tx = UNALIGNED_GET((uint16_t *)(data->ctx.tx_buf)); + } else { + tx = UNALIGNED_GET((uint8_t *)(data->ctx.tx_buf)); + } + } else { + tx = 0U; + } + + while (!data->preg->SPSR.BIT.SPTEF) { + } + + data->preg->SPDR.LONG = tx; + + spi_context_update_tx(&data->ctx, data->dfs, 1); + + if (spi_context_rx_on(&data->ctx)) { + while (!data->preg->SPSR.BIT.SPRF) { + } + rx = data->preg->SPDR.LONG; + + 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); + } else { + /* If there no rx and the tx still send, read and drop the data */ + if (data->preg->SPSR.BIT.SPRF) { + /* In case there no rx drop the incoming data*/ + rx = data->preg->SPDR.LONG; + } + } + + return 0; +} + +static int rx_rspi_transceive_data(struct rx_rspi_data *data) +{ + uint16_t operation = data->ctx.config->operation; + + if (SPI_OP_MODE_GET(operation) == SPI_OP_MODE_MASTER) { + rx_rspi_transceive_master(data); + } else { + rx_rspi_transceive_slave(data); + } + + return 0; +} +#endif + +static int transceive(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs, + bool asynchronous, spi_callback_t cb, void *userdata) +{ + struct rx_rspi_data *data = dev->data; + int ret = 0; + + if (!tx_bufs && !rx_bufs) { + return 0; + } + + if (!IS_ENABLED(CONFIG_SPI_RENESAS_RX_INTERRUPT) && asynchronous) { + return -ENOTSUP; + } + + spi_context_lock(&data->ctx, asynchronous, cb, userdata, spi_cfg); + + ret = rx_rspi_configure(dev, spi_cfg); + if (ret) { + goto end; + } + + data->dfs = ((SPI_WORD_SIZE_GET(spi_cfg->operation) - 1) / 8) + 1; + + spi_context_buffers_setup(&data->ctx, tx_bufs, rx_bufs, data->dfs); + spi_context_cs_control(&data->ctx, true); + + if ((!spi_context_tx_buf_on(&data->ctx)) && (!spi_context_rx_buf_on(&data->ctx))) { + /* If current buffer has no data, do nothing */ + goto end; + } + +#ifdef CONFIG_SPI_RENESAS_RX_INTERRUPT + + if (data->ctx.rx_len == 0) { + data->data_len = spi_context_is_slave(&data->ctx) + ? (spi_context_total_tx_len(&data->ctx) / data->dfs) + : 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->dfs) + : 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)) / + data->dfs) + : MIN(data->ctx.tx_len, data->ctx.rx_len); + } + + data->tcb.xfr_length = data->data_len; + data->tcb.tx_count = 0; + data->tcb.rx_count = 0; + data->tcb.do_rx_now = false; + data->tcb.do_tx = true; + data->tcb.bytes_per_transfer = rspi_get_data_type(data->command_word); + if (data->tcb.bytes_per_transfer == 0) { + LOG_ERR("Invalid bit length"); + ret = -EINVAL; + goto end; + } + + if (data->ctx.rx_buf == NULL) { + data->tcb.transfer_mode = RSPI_DO_TX; + rspi_err_t err = R_RSPI_Write(data->rspi, data->command_word, + (void *)data->ctx.tx_buf, data->data_len); + if (err != 0) { + ret = -EINVAL; + goto end; + } + } else if (data->ctx.tx_buf == NULL) { + data->tcb.transfer_mode = RSPI_DO_RX; + data->tcb.do_tx = false; + rspi_err_t err = R_RSPI_Read(data->rspi, data->command_word, + (void *)data->ctx.rx_buf, data->data_len); + if (err != 0) { + ret = -EINVAL; + goto end; + } + } else { + data->tcb.transfer_mode = RSPI_DO_TX_RX; + rspi_err_t err = + R_RSPI_WriteRead(data->rspi, data->command_word, (void *)data->ctx.tx_buf, + (void *)data->ctx.rx_buf, data->data_len); + if (err != 0) { + ret = -EINVAL; + goto end; + } + } + ret = spi_context_wait_for_completion(&data->ctx); + +#else + data->preg->SPCR.BIT.TXMD = 0x0; /* tx - rx */ + if (!spi_context_rx_on(&data->ctx)) { + data->preg->SPCR.BIT.TXMD = 0x1; /* tx only */ + } + + /* Enable the SPI Transfer */ + data->preg->SPCMD0.BIT.SPB = data->command_word.bit_length; + data->preg->SPCR.BIT.SPE = 1; + + do { + rx_rspi_transceive_data(data); + } while (rx_spi_transfer_ongoing(data)); + + if (SPI_OP_MODE_GET(data->ctx.config->operation) == SPI_OP_MODE_MASTER) { + /* Wait for transmision complete */ + while (data->preg->SPSR.BIT.IDLNF) { + if (data->preg->SPSR.BIT.SPRF) { + /* Drop the incoming data because there are no rx */ + uint32_t trash_can; + + trash_can = data->preg->SPDR.LONG; + } + } + } + + /* Disable the SPI Transfer. */ + data->preg->SPCR.BIT.SPE = 0; + +#ifdef CONFIG_SPI_SLAVE + if (spi_context_is_slave(&data->ctx) && !ret) { + ret = data->ctx.recv_frames; + } +#endif /* CONFIG_SPI_SLAVE */ +#endif /* CONFIG_SPI_RENESAS_RX_INTERRUPT */ + +end: + spi_context_release(&data->ctx, ret); + + return ret; +} + +static int rx_rspi_transceive(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, const struct spi_buf_set *rx_bufs) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, false, NULL, NULL); +} + +#ifdef CONFIG_SPI_ASYNC +static int rx_rspi_transceive_async(const struct device *dev, const struct spi_config *spi_cfg, + const struct spi_buf_set *tx_bufs, + const struct spi_buf_set *rx_bufs, spi_callback_t cb, + void *userdata) +{ + return transceive(dev, spi_cfg, tx_bufs, rx_bufs, true, cb, userdata); +} +#endif + +static int rx_rspi_release(const struct device *dev, const struct spi_config *spi_cfg) +{ + struct rx_rspi_data *data = dev->data; + + spi_context_unlock_unconditionally(&data->ctx); + + return 0; +} + +static DEVICE_API(spi, rx_spi_api) = { + .transceive = rx_rspi_transceive, +#ifdef CONFIG_SPI_ASYNC + .transceive_async = rx_rspi_transceive_async, +#endif + .release = rx_rspi_release, +}; + +static int rspi_rx_init(const struct device *dev) +{ + const struct rx_rspi_config *config = dev->config; + struct rx_rspi_data *data = dev->data; + int ret; + + 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_RENESAS_RX_INTERRUPT) + +static void rx_rspi_retransmit(struct rx_rspi_data *data) +{ + if (data->ctx.rx_len == 0) { + data->data_len = data->ctx.tx_len; + data->tcb.transfer_mode = RSPI_DO_TX; + data->tcb.do_tx = true; + } else if (data->ctx.tx_len == 0) { + data->data_len = data->ctx.rx_len; + data->tcb.transfer_mode = RSPI_DO_RX; + data->tcb.do_tx = false; + } else { + data->data_len = MIN(data->ctx.tx_len, data->ctx.rx_len); + data->tcb.transfer_mode = RSPI_DO_TX_RX; + data->tcb.do_tx = true; + } + + data->tcb.do_rx_now = false; + data->tcb.tx_count = 0; + data->tcb.rx_count = 0; + data->tcb.xfr_length = data->data_len; + + /* Execute the transmit of first byte here to start transmit on the new buffer */ + rx_rspi_tcb_t *rspi_tcb = &(data->tcb); + uint16_t tx_count = rspi_tcb->tx_count; + + if (tx_count < rspi_tcb->xfr_length) { + transmit_data(data, tx_count); + rspi_tcb->tx_count++; + } +} + +static void rx_rspi_spri_isr(const struct device *dev) +{ + struct rx_rspi_data *data = dev->data; + rx_rspi_tcb_t *rspi_tcb = &(data->tcb); + uint32_t *rxdata = &(data->rxdata); + + if (RSPI_TRANS_MODE_SW == rspi_tcb->data_tran_mode) { + *rxdata = data->preg->SPDR.LONG; + rspi_tcb->rx_count++; +#if RSPI_CFG_HIGH_SPEED_READ == 0 + rx_rspi_spti_sub(dev); +#endif + void *pdest = (void *)data->ctx.rx_buf; + uint16_t rx_count = rspi_tcb->rx_count; + uint8_t data_size = rspi_tcb->bytes_per_transfer; + + if (rspi_tcb->do_rx_now) { + if (RSPI_BYTE_DATA == data_size) { + ((uint8_t *)pdest)[rx_count - 1] = *((uint8_t *)rxdata); + } else if (RSPI_WORD_DATA == data_size) { + ((uint16_t *)pdest)[rx_count - 1] = *((uint16_t *)rxdata); + } else { + ((uint32_t *)pdest)[rx_count - 1] = *rxdata; + } + } + if (rx_count == rspi_tcb->xfr_length) { + data->preg->SPCR2.BIT.SPIIE = 1; + + /* If the SPI is in slave mode */ + if (spi_context_is_slave(&data->ctx)) { + spi_context_update_rx(&data->ctx, data->dfs, data->data_len); + /* Disable RSPI */ + data->preg->SPCR.BIT.SPE = 0; + + /** + * Transfer complete. Call the user callback function passing + * pointer to the result structure. + */ + if (data->rspi->pcallback != NULL) { + data->callback_data.handle = data->rspi; + data->callback_data.event_code = RSPI_EVT_TRANSFER_COMPLETE; + data->rspi->pcallback((void *)dev); + } + } + } + } else { + R_RSPI_IntSpriIerClear(data->rspi); + R_RSPI_DisableRSPI(data->rspi); + + /** + * Transfer complete. Call the user callback function passing pointer to the result + * structure. + */ + if (data->rspi->pcallback != NULL) { + data->callback_data.handle = data->rspi; + data->callback_data.event_code = RSPI_EVT_TRANSFER_COMPLETE; + + data->rspi->pcallback((void *)dev); + } + } +} + +static void rx_rspi_spti_isr(const struct device *dev) +{ + struct rx_rspi_data *data = dev->data; + uint32_t *rxdata = &(data->rxdata); + rx_rspi_tcb_t *rspi_tcb = &(data->tcb); + + if (RSPI_TRANS_MODE_SW == rspi_tcb->data_tran_mode) { + if (0 == rspi_tcb->tx_count) { + *rxdata = data->preg->SPDR.LONG; + } + +/** + * If master mode then disable further SPTI interrupts on first transmit. + * If slave mode then we do two transmits to fill the double buffer, + * then disable SPTI interrupts. + * The receive interrupt will handle any remaining data. + */ +#if RSPI_CFG_HIGH_SPEED_READ == 0 + if ((data->preg->SPCR.BIT.MSTR) || (rspi_tcb->txcount > 0)) { + data->preg->SPCR.BIT.SPTIE = 0; + } +#endif + rx_rspi_spti_sub(dev); + + if (rspi_tcb->transfer_mode & RSPI_DO_RX) { + /* Count was incremented in the call to rx_rspi_spti_sub. */ + if ((data->preg->SPCR.BIT.MSTR) || (rspi_tcb->tx_count > 1)) { + /* Enables saving of receive data on next receive interrupt. */ + rspi_tcb->do_rx_now = true; + } + } + } else { + R_RSPI_DisableSpti(data->rspi); + R_RSPI_IntSptiIerClear(data->rspi); + } +} + +static void rx_rspi_spii_isr(const struct device *dev) +{ + struct rx_rspi_data *data = dev->data; + + if (data->tcb.rx_count >= data->tcb.xfr_length) { + spi_context_update_rx(&data->ctx, data->dfs, data->data_len); + } + if (data->tcb.tx_count >= data->tcb.xfr_length) { + spi_context_update_tx(&data->ctx, data->dfs, data->data_len); + } + if (rx_spi_transfer_ongoing(data)) { + data->preg->SPCR2.BIT.SPIIE = 0; + rx_rspi_retransmit(data); + } else { + uint8_t status_flags = data->preg->SPSR.BYTE; + rspi_evt_t event = RSPI_EVT_ERR_UNDEF; + rspi_callback_data_t *rspi_cb_data = &(data->callback_data); + + rspi_cb_data->event_code = event; + if ((status_flags & RSPI_SPSR_IDLNF) == 0x00) { + /* Disable idle interrupt requests of the RSPI. */ + data->preg->SPCR2.BIT.SPIIE = 0; + + /* Disable RSPI */ + data->preg->SPCR.BIT.SPE = 0; + + /** + * Transfer complete. Call the user callback function passing pointer to the + * result structure. + */ + if (data->rspi->pcallback != NULL) { + rspi_cb_data->handle = data->rspi; + rspi_cb_data->event_code = RSPI_EVT_TRANSFER_COMPLETE; + data->rspi->pcallback((void *)dev); + } + } + } +} + +static void rx_rspi_spei_isr(const struct device *dev) +{ + struct rx_rspi_data *data = dev->data; + uint8_t status_flags = data->preg->SPSR.BYTE; + rspi_evt_t event = RSPI_EVT_ERR_UNDEF; + rspi_callback_data_t *rspi_cb_data = &(data->callback_data); + + /* Identify and clear error condition. */ + if (status_flags & RSPI_SPSR_OVRF) { + event = RSPI_EVT_ERR_READ_OVF; + /* Clear error source: OVRF flag. */ + data->preg->SPSR.BIT.OVRF = 0; + goto error_callback; + } + + if (status_flags & RSPI_SPSR_MODF) { + if (status_flags & RSPI_SPSR_UDRF) { + event = RSPI_EVT_ERR_UNDER_RUN; + /* Clear error source: MODF flag and UDRF. */ + data->preg->SPSR.BYTE &= RSPI_SPSR_MODF_UDRF_MASK; + } else { + event = RSPI_EVT_ERR_MODE_FAULT; + /* Clear error source: MODF flag. */ + data->preg->SPSR.BIT.MODF = 0; + } + goto error_callback; + } + + if (status_flags & RSPI_SPSR_PERF) { + event = RSPI_EVT_ERR_PARITY; + /* Clear error source: PERF flag. */ + data->preg->SPSR.BIT.PERF = 0; + goto error_callback; + } +error_callback: + rspi_cb_data->event_code = event; + + /* Disable the RSPI operation. */ + data->preg->SPCR.BYTE &= (uint8_t)(~((RSPI_SPCR_SPTIE | RSPI_SPCR_SPRIE) | RSPI_SPCR_SPE)); + + /* Disable idle interrupt requests of the RSPI. */ + data->preg->SPCR2.BIT.SPIIE = 0; + + /* Call the user callback function passing pointer to the result structure. */ + if (data->rspi->pcallback != NULL) { + rspi_cb_data->handle = data->rspi; + data->rspi->pcallback((void *)dev); + } +} + +#endif + +#if defined(CONFIG_SPI_RENESAS_RX_INTERRUPT) + +#define RX_RSPI_IRQ_CONFIG_INIT(n) \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, spri, irq), DT_INST_IRQ_BY_NAME(n, spri, priority), \ + rx_rspi_spri_isr, DEVICE_DT_INST_GET(n), 0); \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, spti, irq), DT_INST_IRQ_BY_NAME(n, spti, priority), \ + rx_rspi_spti_isr, DEVICE_DT_INST_GET(n), 0); \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, spii, irq), DT_INST_IRQ_BY_NAME(n, spii, priority), \ + rx_rspi_spii_isr, DEVICE_DT_INST_GET(n), 0); \ + IRQ_CONNECT(DT_INST_IRQ_BY_NAME(n, spei, irq), DT_INST_IRQ_BY_NAME(n, spei, priority), \ + rx_rspi_spei_isr, DEVICE_DT_INST_GET(n), 0); \ + \ + irq_enable(DT_INST_IRQ_BY_NAME(n, spri, irq)); \ + irq_enable(DT_INST_IRQ_BY_NAME(n, spti, irq)); \ + irq_enable(DT_INST_IRQ_BY_NAME(n, spei, irq)); + +#else + +#define RX_RSPI_IRQ_CONFIG_INIT(n) + +#endif + +#define RX_RSPI_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + static const struct rx_rspi_config rx_rspi_config_##n = { \ + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ + }; \ + static struct rx_rspi_data rx_rspi_data_##n = { \ + SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(n), ctx) \ + SPI_CONTEXT_INIT_LOCK(rx_rspi_data_##n, ctx), \ + SPI_CONTEXT_INIT_SYNC(rx_rspi_data_##n, ctx), \ + .preg = (struct st_rspi *)DT_INST_REG_ADDR(n), \ + .channel_id = DT_INST_PROP(n, channel), \ + .ssl_assert = DT_INST_PROP(n, ssl_assert), \ + }; \ + static int rspi_rx_init##n(const struct device *dev) \ + { \ + int err = rspi_rx_init(dev); \ + if (err != 0) { \ + return err; \ + } \ + RX_RSPI_IRQ_CONFIG_INIT(n); \ + return 0; \ + } \ + SPI_DEVICE_DT_INST_DEFINE(n, rspi_rx_init##n, PM_DEVICE_DT_INST_GET(n), &rx_rspi_data_##n, \ + &rx_rspi_config_##n, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + &rx_spi_api); + +DT_INST_FOREACH_STATUS_OKAY(RX_RSPI_INIT) diff --git a/dts/bindings/spi/renesas,rx-rspi.yaml b/dts/bindings/spi/renesas,rx-rspi.yaml new file mode 100644 index 00000000000..898d73f8fec --- /dev/null +++ b/dts/bindings/spi/renesas,rx-rspi.yaml @@ -0,0 +1,38 @@ +# Copyright (c) 2025 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RX RSPI + +compatible: "renesas,rx-rspi" + +include: [spi-controller.yaml, pinctrl-device.yaml] + +properties: + reg: + required: true + + channel: + type: int + required: true + description: | + The index of SPI channel + + ssl-assert: + type: int + default: 0 + enum: + - 0 + - 1 + - 2 + - 3 + description: | + Slave select to be asserted during transfer operation + + interrupts: + required: true + + pinctrl-0: + required: true + + pinctrl-names: + required: true diff --git a/dts/rx/renesas/rx130-common.dtsi b/dts/rx/renesas/rx130-common.dtsi index 9acc3541472..50a6e3523d8 100644 --- a/dts/rx/renesas/rx130-common.dtsi +++ b/dts/rx/renesas/rx130-common.dtsi @@ -501,6 +501,18 @@ }; }; + rspi0: spi@88380 { + compatible = "renesas,rx-rspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x00088380 0x100>; + channel = <0>; + clocks = <&pclkb MSTPB 17>; + interrupts = <44 1>, <45 1>, <46 1>, <47 1>; + interrupt-names = "spei", "spri", "spti", "spii"; + status = "disabled"; + }; + ofsm: ofsm@ffffff80 { compatible = "zephyr,memory-region"; reg = <0xFFFFFF80 0x0F>; diff --git a/modules/Kconfig.renesas b/modules/Kconfig.renesas index 86498ce24fb..1f62e38720f 100644 --- a/modules/Kconfig.renesas +++ b/modules/Kconfig.renesas @@ -279,4 +279,9 @@ config USE_RX_RDP_SCI_UART help Enable RX RDP SCI UART driver +config USE_RX_RDP_RSPI + bool + help + Enable RX RDP RSPI driver + endif # HAS_RENESAS_RX_RDP