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 <duy.nguyen.xa@renesas.com> Signed-off-by: Minh Tang <minh.tang.ue@bp.renesas.com>
This commit is contained in:
parent
22b373cefd
commit
e7c025db24
7 changed files with 958 additions and 0 deletions
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
32
drivers/spi/Kconfig.renesas_rx
Normal file
32
drivers/spi/Kconfig.renesas_rx
Normal file
|
@ -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
|
869
drivers/spi/spi_renesas_rx.c
Normal file
869
drivers/spi/spi_renesas_rx.c
Normal file
|
@ -0,0 +1,869 @@
|
|||
/*
|
||||
* Copyright (c) 2025 Renesas Electronics Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT renesas_rx_rspi
|
||||
|
||||
#include <zephyr/drivers/spi.h>
|
||||
#include <zephyr/drivers/pinctrl.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <soc.h>
|
||||
#include "r_rspi_rx_if.h"
|
||||
#include "iodefine.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
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)
|
38
dts/bindings/spi/renesas,rx-rspi.yaml
Normal file
38
dts/bindings/spi/renesas,rx-rspi.yaml
Normal file
|
@ -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
|
|
@ -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>;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue