diff --git a/arch/xtensa/src/esp32s3/Kconfig b/arch/xtensa/src/esp32s3/Kconfig index 1997c114f46..2a783c3ea75 100644 --- a/arch/xtensa/src/esp32s3/Kconfig +++ b/arch/xtensa/src/esp32s3/Kconfig @@ -301,12 +301,14 @@ config ESP32S3_SPI2 default n select ESP32S3_SPI select SPI + select ESP32S3_GPIO_IRQ if SPI_SLAVE config ESP32S3_SPI3 bool "SPI 3" default n select ESP32S3_SPI select SPI + select ESP32S3_GPIO_IRQ if SPI_SLAVE config ESP32S3_DMA bool "General DMA (GDMA)" @@ -596,6 +598,11 @@ config ESP32S3_SPI_UDCS ---help--- Use user-defined CS. +config ESP32S3_SPI_SLAVE_BUFSIZE + int "SPI slave buffer size" + default 2048 + depends on SPI_SLAVE + if ESP32S3_SPI2 config ESP32S3_SPI2_DMA @@ -645,6 +652,13 @@ endif # ESP32S3_SPI2 if ESP32S3_SPI3 +config ESP32S3_SPI3_DMA + bool "SPI3 use GDMA" + default n + depends on ESP32S3_DMA + ---help--- + Enable support for transfers using the GDMA engine. + config ESP32S3_SPI3_CSPIN int "SPI3 CS Pin" default 10 diff --git a/arch/xtensa/src/esp32s3/Make.defs b/arch/xtensa/src/esp32s3/Make.defs index 5f7b8389937..7568dd3f2ae 100644 --- a/arch/xtensa/src/esp32s3/Make.defs +++ b/arch/xtensa/src/esp32s3/Make.defs @@ -107,6 +107,9 @@ endif ifeq ($(CONFIG_ESP32S3_SPI),y) CHIP_CSRCS += esp32s3_spi.c +ifeq ($(CONFIG_SPI_SLAVE),y) +CHIP_CSRCS += esp32s3_spi_slave.c +endif endif ifeq ($(CONFIG_ESP32S3_SPIFLASH),y) diff --git a/arch/xtensa/src/esp32s3/esp32s3_dma.c b/arch/xtensa/src/esp32s3/esp32s3_dma.c index ed90ab71106..1db65f1cb7e 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_dma.c +++ b/arch/xtensa/src/esp32s3/esp32s3_dma.c @@ -66,6 +66,7 @@ static bool g_dma_chan_used[ESP32S3_DMA_CHAN_MAX]; static mutex_t g_dma_lock = NXMUTEX_INITIALIZER; +static int g_dma_ref; /**************************************************************************** * Public Functions @@ -368,9 +369,18 @@ void esp32s3_dma_wait_idle(int chan, bool tx) void esp32s3_dma_init(void) { - modifyreg32(SYSTEM_PERIP_CLK_EN1_REG, 0, SYSTEM_DMA_CLK_EN_M); - modifyreg32(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST_M, 0); + nxmutex_lock(&g_dma_lock); - modifyreg32(DMA_MISC_CONF_REG, 0, DMA_CLK_EN_M); + if (!g_dma_ref) + { + modifyreg32(SYSTEM_PERIP_CLK_EN1_REG, 0, SYSTEM_DMA_CLK_EN_M); + modifyreg32(SYSTEM_PERIP_RST_EN1_REG, SYSTEM_DMA_RST_M, 0); + + modifyreg32(DMA_MISC_CONF_REG, 0, DMA_CLK_EN_M); + } + + g_dma_ref++; + + nxmutex_unlock(&g_dma_lock); } diff --git a/arch/xtensa/src/esp32s3/esp32s3_spi.h b/arch/xtensa/src/esp32s3/esp32s3_spi.h index 4797d2d611d..3afed087b72 100644 --- a/arch/xtensa/src/esp32s3/esp32s3_spi.h +++ b/arch/xtensa/src/esp32s3/esp32s3_spi.h @@ -46,6 +46,10 @@ extern "C" #include +#ifdef CONFIG_SPI_SLAVE +# include +#endif + #ifdef CONFIG_ESP32S3_SPI2 # define ESP32S3_SPI2 2 #endif @@ -139,6 +143,39 @@ int esp32s3_spi3_cmddata(struct spi_dev_s *dev, int esp32s3_spibus_uninitialize(struct spi_dev_s *dev); +/**************************************************************************** + * Name: esp32s3_spislave_ctrlr_initialize + * + * Description: + * Initialize the selected SPI Slave bus. + * + * Input Parameters: + * port - Port number (for hardware that has multiple SPI Slave interfaces) + * + * Returned Value: + * Valid SPI Slave controller structure reference on success; + * NULL on failure. + * + ****************************************************************************/ + +struct spi_slave_ctrlr_s *esp32s3_spislave_ctrlr_initialize(int port); + +/**************************************************************************** + * Name: esp32s3_spislave_ctrlr_uninitialize + * + * Description: + * Uninitialize an SPI Slave bus. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +int esp32s3_spislave_ctrlr_uninitialize(struct spi_slave_ctrlr_s *ctrlr); + #endif /* CONFIG_ESP32S3_SPI */ #ifdef __cplusplus diff --git a/arch/xtensa/src/esp32s3/esp32s3_spi_slave.c b/arch/xtensa/src/esp32s3/esp32s3_spi_slave.c new file mode 100644 index 00000000000..9b8edf26781 --- /dev/null +++ b/arch/xtensa/src/esp32s3/esp32s3_spi_slave.c @@ -0,0 +1,1711 @@ +/**************************************************************************** + * arch/xtensa/src/esp32s3/esp32s3_spi_slave.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#if defined(CONFIG_ESP32S3_SPI) && defined(CONFIG_SPI_SLAVE) + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "esp32s3_spi.h" +#include "esp32s3_irq.h" +#include "esp32s3_gpio.h" + +#if defined(CONFIG_ESP32S3_SPI2_DMA) || defined(CONFIG_ESP32S3_SPI3_DMA) +#include "esp32s3_dma.h" +#endif + +#include "xtensa.h" +#include "hardware/esp32s3_gpio_sigmap.h" +#include "hardware/esp32s3_pinmap.h" +#include "hardware/esp32s3_spi.h" +#include "hardware/esp32s3_soc.h" +#include "hardware/esp32s3_system.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +#define SPI_SLAVE_BUFSIZE (CONFIG_ESP32S3_SPI_SLAVE_BUFSIZE) + +#if defined(CONFIG_ESP32S3_SPI2_DMA) || defined(CONFIG_ESP32S3_SPI3_DMA) +# define ESP32S3_SPI_DMA +#endif + +#ifdef ESP32S3_SPI_DMA +/* SPI DMA RX/TX number of descriptors */ + +#if (SPI_SLAVE_BUFSIZE % ESP32S3_DMA_BUFLEN_MAX) > 0 +# define SPI_DMA_DESC_NUM (SPI_SLAVE_BUFSIZE / ESP32S3_DMA_BUFLEN_MAX + 1) +#else +# define SPI_DMA_DESC_NUM (SPI_SLAVE_BUFSIZE / ESP32S3_DMA_BUFLEN_MAX) +#endif + +#endif /* ESP32S3_SPI_DMA */ + +/* Verify whether SPI has been assigned IOMUX pins. + * Otherwise, SPI signals will be routed via GPIO Matrix. + */ + +#ifdef CONFIG_ESP32S3_SPI2 +# define SPI_IS_CS_IOMUX (CONFIG_ESP32S3_SPI2_CSPIN == SPI2_IOMUX_CSPIN) +# define SPI_IS_CLK_IOMUX (CONFIG_ESP32S3_SPI2_CLKPIN == SPI2_IOMUX_CLKPIN) +# define SPI_IS_MOSI_IOMUX (CONFIG_ESP32S3_SPI2_MOSIPIN == SPI2_IOMUX_MOSIPIN) +# define SPI_IS_MISO_IOMUX (CONFIG_ESP32S3_SPI2_MISOPIN == SPI2_IOMUX_MISOPIN) + +# define SPI_VIA_IOMUX ((SPI_IS_CS_IOMUX) && \ + (SPI_IS_CLK_IOMUX) && \ + (SPI_IS_MOSI_IOMUX) && \ + (SPI_IS_MISO_IOMUX)) +#else +# define SPI_VIA_IOMUX 0 +#endif + +/* SPI Slave interrupt mask */ + +#define SPI_INT_MASK (SPI_TRANS_DONE_INT_ENA_M | \ + SPI_SLV_WR_DMA_DONE_INT_ENA_M | \ + SPI_SLV_RD_DMA_DONE_INT_ENA_M | \ + SPI_SLV_WR_BUF_DONE_INT_ENA_M | \ + SPI_SLV_RD_BUF_DONE_INT_ENA_M) + +/* SPI Slave default width */ + +#define SPI_SLAVE_DEFAULT_WIDTH (8) + +/* SPI Slave default mode */ + +#define SPI_SLAVE_DEFAULT_MODE (SPISLAVE_MODE0) + +/* SPI Slave maximum buffer size in bytes */ + +#define SPI_SLAVE_HW_BUF_SIZE (64) + +#define WORDS2BYTES(_priv, _wn) ((_wn) * ((_priv)->nbits / 8)) +#define BYTES2WORDS(_priv, _bn) ((_bn) / ((_priv)->nbits / 8)) + +#define setbits(bs, a) modifyreg32(a, 0, bs) +#define resetbits(bs, a) modifyreg32(a, bs, 0) + +/* SPI Slave controller hardware configuration */ + +struct spislave_config_s +{ + int32_t width; /* SPI Slave default width */ + enum spi_slave_mode_e mode; /* SPI Slave default mode */ + + uint8_t id; /* SPI device ID: SPIx {2,3} */ + uint8_t cs_pin; /* GPIO configuration for CS */ + uint8_t mosi_pin; /* GPIO configuration for MOSI */ + uint8_t miso_pin; /* GPIO configuration for MISO */ + uint8_t clk_pin; /* GPIO configuration for CLK */ + uint8_t periph; /* Peripheral ID */ + uint8_t irq; /* Interrupt ID */ + uint32_t clk_bit; /* Clock enable bit */ + uint32_t rst_bit; /* SPI reset bit */ +#ifdef ESP32S3_SPI_DMA + bool dma_used; /* Enable DMA channel */ + uint32_t dma_clk_bit; /* DMA clock enable bit */ + uint32_t dma_rst_bit; /* DMA reset bit */ + uint8_t dma_periph; /* DMA peripheral */ +#endif + uint32_t cs_insig; /* SPI CS input signal index */ + uint32_t cs_outsig; /* SPI CS output signal index */ + uint32_t mosi_insig; /* SPI MOSI input signal index */ + uint32_t mosi_outsig; /* SPI MOSI output signal index */ + uint32_t miso_insig; /* SPI MISO input signal index */ + uint32_t miso_outsig; /* SPI MISO output signal index */ + uint32_t clk_insig; /* SPI CLK input signal index */ + uint32_t clk_outsig; /* SPI CLK output signal index */ +}; + +struct spislave_priv_s +{ + /* Externally visible part of the SPI Slave controller interface */ + + struct spi_slave_ctrlr_s ctrlr; + + /* Reference to SPI Slave device interface */ + + struct spi_slave_dev_s *dev; + + /* Port configuration */ + + const struct spislave_config_s *config; + int refs; /* Reference count */ + int cpu; /* CPU ID */ + int cpuint; /* SPI interrupt ID */ +#ifdef ESP32S3_SPI_DMA + int32_t dma_channel; /* Channel assigned by the GDMA driver */ + + /* DMA RX/TX description */ + + struct esp32s3_dmadesc_s *dma_rxdesc; + struct esp32s3_dmadesc_s *dma_txdesc; + + uint32_t rx_dma_offset; /* Offset of DMA RX buffer */ +#endif + enum spi_slave_mode_e mode; /* Current SPI Slave hardware mode */ + uint8_t nbits; /* Current configured bit width */ + uint32_t tx_length; /* Location of next TX value */ + + /* SPI Slave TX queue buffer */ + + uint8_t tx_buffer[SPI_SLAVE_BUFSIZE]; + uint32_t rx_length; /* Location of next RX value */ + + /* SPI Slave RX queue buffer */ + + uint8_t rx_buffer[SPI_SLAVE_BUFSIZE]; + + /* Flag that indicates whether SPI Slave is currently processing */ + + bool is_processing; + + /* Flag that indicates whether SPI Slave TX is currently enabled */ + + bool is_tx_enabled; + + spinlock_t lock; /* Device specific lock. */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* SPI Slave controller interrupt handlers */ + +static int spislave_cs_interrupt(int irq, void *context, void *arg); +static int spislave_periph_interrupt(int irq, void *context, void *arg); + +/* SPI Slave controller internal functions */ + +static void spislave_setmode(struct spi_slave_ctrlr_s *ctrlr, + enum spi_slave_mode_e mode); +static void spislave_setbits(struct spi_slave_ctrlr_s *ctrlr, int nbits); +static void spislave_store_result(struct spislave_priv_s *priv, + uint32_t recv_bytes); +static void spislave_evict_sent_data(struct spislave_priv_s *priv, + uint32_t sent_bytes); +#ifdef ESP32S3_SPI_DMA +static void spislave_setup_rx_dma(struct spislave_priv_s *priv); +static void spislave_setup_tx_dma(struct spislave_priv_s *priv); +static void spislave_prepare_next_rx(struct spislave_priv_s *priv); +static void spislave_prepare_next_tx(struct spislave_priv_s *priv); +#else +static void spislave_write_tx_buffer(struct spislave_priv_s *priv); +#endif +static void spislave_initialize(struct spi_slave_ctrlr_s *ctrlr); +static void spislave_deinitialize(struct spi_slave_ctrlr_s *ctrlr); + +/* SPI Slave controller operations */ + +static void spislave_bind(struct spi_slave_ctrlr_s *ctrlr, + struct spi_slave_dev_s *dev, + enum spi_slave_mode_e mode, + int nbits); +static void spislave_unbind(struct spi_slave_ctrlr_s *ctrlr); +static int spislave_enqueue(struct spi_slave_ctrlr_s *ctrlr, + const void *data, + size_t nwords); +static bool spislave_qfull(struct spi_slave_ctrlr_s *ctrlr); +static void spislave_qflush(struct spi_slave_ctrlr_s *ctrlr); +static size_t spislave_qpoll(struct spi_slave_ctrlr_s *ctrlr); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* SPI2 private data */ + +#ifdef CONFIG_ESP32S3_SPI2 +static const struct spislave_config_s esp32s3_spi2slave_config = +{ + .width = SPI_SLAVE_DEFAULT_WIDTH, + .mode = SPI_SLAVE_DEFAULT_MODE, + .id = 2, + .cs_pin = CONFIG_ESP32S3_SPI2_CSPIN, + .mosi_pin = CONFIG_ESP32S3_SPI2_MOSIPIN, + .miso_pin = CONFIG_ESP32S3_SPI2_MISOPIN, + .clk_pin = CONFIG_ESP32S3_SPI2_CLKPIN, + .periph = ESP32S3_PERIPH_SPI2, + .irq = ESP32S3_IRQ_SPI2, + .clk_bit = SYSTEM_SPI2_CLK_EN, + .rst_bit = SYSTEM_SPI2_RST, +#ifdef CONFIG_ESP32S3_SPI2_DMA + .dma_used = true, + .dma_clk_bit = SYSTEM_SPI2_DMA_CLK_EN, + .dma_rst_bit = SYSTEM_SPI2_DMA_RST, + .dma_periph = ESP32S3_DMA_PERIPH_SPI2, +#endif + .cs_insig = FSPICS0_IN_IDX, + .cs_outsig = FSPICS0_OUT_IDX, + .mosi_insig = FSPID_IN_IDX, + .mosi_outsig = FSPID_OUT_IDX, + .miso_insig = FSPIQ_IN_IDX, + .miso_outsig = FSPIQ_OUT_IDX, + .clk_insig = FSPICLK_IN_IDX, + .clk_outsig = FSPICLK_OUT_IDX +}; + +static const struct spi_slave_ctrlrops_s esp32s3_spi2slave_ops = +{ + .bind = spislave_bind, + .unbind = spislave_unbind, + .enqueue = spislave_enqueue, + .qfull = spislave_qfull, + .qflush = spislave_qflush, + .qpoll = spislave_qpoll +}; + +#ifdef CONFIG_ESP32S3_SPI2_DMA + +/* SPI DMA RX/TX description buffer */ + +static struct esp32s3_dmadesc_s esp32s3_spi2_dma_rxdesc[SPI_DMA_DESC_NUM]; +static struct esp32s3_dmadesc_s esp32s3_spi2_dma_txdesc[SPI_DMA_DESC_NUM]; +#endif + +static struct spislave_priv_s esp32s3_spi2slave_priv = +{ + .ctrlr = + { + .ops = &esp32s3_spi2slave_ops + }, + .dev = NULL, + .config = &esp32s3_spi2slave_config, + .refs = 0, + .cpu = -1, + .cpuint = -ENOMEM, +#ifdef CONFIG_ESP32S3_SPI2_DMA + .dma_channel = -ENOMEM, + .dma_rxdesc = esp32s3_spi2_dma_rxdesc, + .dma_txdesc = esp32s3_spi2_dma_txdesc, + .rx_dma_offset = 0, +#endif + .mode = SPISLAVE_MODE0, + .nbits = 0, + .tx_length = 0, + .tx_buffer = + { + 0 + }, + .rx_length = 0, + .rx_buffer = + { + 0 + }, + .is_processing = false, + .is_tx_enabled = false +}; +#endif /* CONFIG_ESP32S3_SPI2 */ + +#ifdef CONFIG_ESP32S3_SPI3 +static const struct spislave_config_s esp32s3_spi3slave_config = +{ + .width = SPI_SLAVE_DEFAULT_WIDTH, + .mode = SPI_SLAVE_DEFAULT_MODE, + .id = 3, + .cs_pin = CONFIG_ESP32S3_SPI3_CSPIN, + .mosi_pin = CONFIG_ESP32S3_SPI3_MOSIPIN, + .miso_pin = CONFIG_ESP32S3_SPI3_MISOPIN, + .clk_pin = CONFIG_ESP32S3_SPI3_CLKPIN, + .periph = ESP32S3_PERIPH_SPI3, + .irq = ESP32S3_IRQ_SPI3, + .clk_bit = SYSTEM_SPI3_CLK_EN, + .rst_bit = SYSTEM_SPI3_RST, +#ifdef CONFIG_ESP32S3_SPI3_DMA + .dma_used = true, + .dma_clk_bit = SYSTEM_SPI3_DMA_CLK_EN, + .dma_rst_bit = SYSTEM_SPI3_DMA_RST, + .dma_periph = ESP32S3_DMA_PERIPH_SPI3, +#endif + .cs_insig = SPI3_CS0_IN_IDX, + .cs_outsig = SPI3_CS0_OUT_IDX, + .mosi_insig = SPI3_D_IN_IDX, + .mosi_outsig = SPI3_D_OUT_IDX, + .miso_insig = SPI3_Q_IN_IDX, + .miso_outsig = SPI3_Q_OUT_IDX, + .clk_insig = SPI3_CLK_IN_IDX, + .clk_outsig = SPI3_CLK_OUT_IDX +}; + +static const struct spi_slave_ctrlrops_s esp32s3_spi3slave_ops = +{ + .bind = spislave_bind, + .unbind = spislave_unbind, + .enqueue = spislave_enqueue, + .qfull = spislave_qfull, + .qflush = spislave_qflush, + .qpoll = spislave_qpoll +}; + +#ifdef CONFIG_ESP32S3_SPI3_DMA + +/* SPI DMA RX/TX description buffer */ + +static struct esp32s3_dmadesc_s esp32s3_spi3_dma_rxdesc[SPI_DMA_DESC_NUM]; +static struct esp32s3_dmadesc_s esp32s3_spi3_dma_txdesc[SPI_DMA_DESC_NUM]; +#endif + +static struct spislave_priv_s esp32s3_spi3slave_priv = +{ + .ctrlr = + { + .ops = &esp32s3_spi3slave_ops + }, + .dev = NULL, + .config = &esp32s3_spi3slave_config, + .refs = 0, + .cpu = -1, + .cpuint = -ENOMEM, +#ifdef CONFIG_ESP32S3_SPI3_DMA + .dma_channel = -ENOMEM, + .dma_rxdesc = esp32s3_spi3_dma_rxdesc, + .dma_txdesc = esp32s3_spi3_dma_txdesc, + .rx_dma_offset = 0, +#endif + .mode = SPISLAVE_MODE0, + .nbits = 0, + .tx_length = 0, + .tx_buffer = + { + 0 + }, + .rx_length = 0, + .rx_buffer = + { + 0 + }, + .is_processing = false, + .is_tx_enabled = false +}; +#endif /* CONFIG_ESP32S3_SPI3 */ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: spislave_peripheral_reset + * + * Description: + * Reset the SPI Slave peripheral before next transaction. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static inline void spislave_peripheral_reset(struct spislave_priv_s *priv) +{ + setbits(SPI_SOFT_RESET_M, SPI_SLAVE_REG(priv->config->id)); + resetbits(SPI_SOFT_RESET_M, SPI_SLAVE_REG(priv->config->id)); +} + +/**************************************************************************** + * Name: spislave_cpu_tx_fifo_reset + * + * Description: + * Reset the BUF TX AFIFO, which is used to send data out in SPI Slave + * CPU-controlled mode transfer. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static inline void spislave_cpu_tx_fifo_reset(struct spislave_priv_s *priv) +{ + setbits(SPI_BUF_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); + resetbits(SPI_BUF_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); +} + +/**************************************************************************** + * Name: spislave_dma_tx_fifo_reset + * + * Description: + * Reset the DMA TX AFIFO, which is used to send data out in SPI Slave + * DMA-controlled mode transfer. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +static inline void spislave_dma_tx_fifo_reset(struct spislave_priv_s *priv) +{ + setbits(SPI_DMA_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); + resetbits(SPI_DMA_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); +} +#endif + +/**************************************************************************** + * Name: spislave_dma_rx_fifo_reset + * + * Description: + * Reset the RX AFIFO, which is used to receive data in SPI Slave mode + * transfer. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +static inline void spislave_dma_rx_fifo_reset(struct spislave_priv_s *priv) +{ + setbits(SPI_RX_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); + resetbits(SPI_RX_AFIFO_RST_M, SPI_DMA_CONF_REG(priv->config->id)); +} +#endif + +/**************************************************************************** + * Name: spislave_setmode + * + * Description: + * Set the SPI Slave mode. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * mode - Requested SPI Slave mode + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_setmode(struct spi_slave_ctrlr_s *ctrlr, + enum spi_slave_mode_e mode) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + + spiinfo("mode=%d\n", mode); + + /* Has the mode changed? */ + + if (mode != priv->mode) + { + uint32_t ck_idle_edge; + uint32_t rsck_i_edge; + uint32_t tsck_i_edge; + uint32_t clk_mode_13; + + switch (mode) + { + case SPISLAVE_MODE0: /* CPOL=0; CPHA=0 */ + ck_idle_edge = 0; + rsck_i_edge = 0; + tsck_i_edge = 0; + clk_mode_13 = 0; + break; + + case SPISLAVE_MODE1: /* CPOL=0; CPHA=1 */ + ck_idle_edge = 0; + rsck_i_edge = 1; + tsck_i_edge = 1; + clk_mode_13 = 1; + break; + + case SPISLAVE_MODE2: /* CPOL=1; CPHA=0 */ + ck_idle_edge = 1; + rsck_i_edge = 1; + tsck_i_edge = 1; + clk_mode_13 = 0; + break; + + case SPISLAVE_MODE3: /* CPOL=1; CPHA=1 */ + ck_idle_edge = 1; + rsck_i_edge = 0; + tsck_i_edge = 0; + clk_mode_13 = 1; + break; + + default: + spierr("Invalid mode: %d\n", mode); + DEBUGPANIC(); + return; + } + + modifyreg32(SPI_MISC_REG(priv->config->id), + SPI_CK_IDLE_EDGE_M, + VALUE_TO_FIELD(ck_idle_edge, SPI_CK_IDLE_EDGE)); + + modifyreg32(SPI_USER_REG(priv->config->id), + SPI_RSCK_I_EDGE_M | SPI_TSCK_I_EDGE_M, + VALUE_TO_FIELD(rsck_i_edge, SPI_RSCK_I_EDGE) | + VALUE_TO_FIELD(tsck_i_edge, SPI_TSCK_I_EDGE)); + + modifyreg32(SPI_SLAVE_REG(priv->config->id), + SPI_CLK_MODE_13_M | SPI_RSCK_DATA_OUT_M, + VALUE_TO_FIELD(clk_mode_13, SPI_CLK_MODE_13)); + + priv->mode = mode; + } +} + +/**************************************************************************** + * Name: spislave_setbits + * + * Description: + * Set the number of bits per word. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * nbits - The number of bits in an SPI word + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_setbits(struct spi_slave_ctrlr_s *ctrlr, int nbits) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + + spiinfo("nbits=%d\n", nbits); + + priv->nbits = nbits; +} + +/**************************************************************************** + * Name: spislave_cs_interrupt + * + * Description: + * Handler for the GPIO interrupt which is triggered when the chip select + * has toggled to inactive state (active high). + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info + * arg - SPI Slave controller private data + * + * Returned Value: + * Standard interrupt return value. + * + ****************************************************************************/ + +static int spislave_cs_interrupt(int irq, void *context, void *arg) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)arg; + + if (priv->is_processing) + { + priv->is_processing = false; + SPIS_DEV_SELECT(priv->dev, false); + } + + return 0; +} + +/**************************************************************************** + * Name: spislave_store_result + * + * Description: + * Fetch data from the SPI hardware data buffer and record the length. + * This is a post transaction operation. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * recv_bytes - Number of received bytes + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_store_result(struct spislave_priv_s *priv, + uint32_t recv_bytes) +{ + uint32_t remaining_space = SPI_SLAVE_BUFSIZE - priv->rx_length; + uint32_t bytes_to_copy = recv_bytes; + + if (bytes_to_copy > remaining_space) + { + spiwarn("RX buffer full! Discarded %" PRIu32 " received bytes\n", + bytes_to_copy - remaining_space); + + bytes_to_copy = remaining_space; + } + +#ifdef ESP32S3_SPI_DMA + if (bytes_to_copy) + { + if ((priv->rx_dma_offset != priv->rx_length)) + { + memmove(priv->rx_buffer + priv->rx_length, + priv->rx_buffer + priv->rx_dma_offset, + bytes_to_copy); + + priv->rx_dma_offset = priv->rx_length; + } + + priv->rx_length += bytes_to_copy; + } +#else + /* If DMA is not enabled, software should copy incoming data from data + * buffer registers to receive buffer. + */ + + if (bytes_to_copy) + { + /* Set data_buf_reg with the address of the first data buffer + * register (W0). + */ + + uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id); + + /* Read received data words from SPI hardware data buffer. */ + + for (int i = 0; i < bytes_to_copy; i += sizeof(uint32_t)) + { + uint32_t rbytes = MIN(bytes_to_copy - i, sizeof(uintptr_t)); + uint32_t r_wd = getreg32(data_buf_reg); + + memcpy(priv->rx_buffer + priv->rx_length + i, &r_wd, rbytes); + + /* Update data_buf_reg to point to the next data buffer register. */ + + data_buf_reg += sizeof(uintptr_t); + } + + priv->rx_length += bytes_to_copy; + } +#endif +} + +/**************************************************************************** + * Name: spislave_prepare_next_rx + * + * Description: + * Prepare the SPI Slave controller for receiving data on the next + * transaction. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +static void spislave_prepare_next_rx(struct spislave_priv_s *priv) +{ + if (priv->rx_length < SPI_SLAVE_BUFSIZE) + { + spislave_setup_rx_dma(priv); + } +} +#endif + +/**************************************************************************** + * Name: spislave_evict_sent_data + * + * Description: + * Evict from the TX buffer data sent on the latest transaction and update + * the length. This is a post transaction operation. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * sent_bytes - Number of transmitted bytes + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_evict_sent_data(struct spislave_priv_s *priv, + uint32_t sent_bytes) +{ + if (sent_bytes < priv->tx_length) + { + priv->tx_length -= sent_bytes; + + memmove(priv->tx_buffer, priv->tx_buffer + sent_bytes, + priv->tx_length); + + memset(priv->tx_buffer + priv->tx_length, 0, sent_bytes); + } + else + { + priv->tx_length = 0; + } +} + +/**************************************************************************** + * Name: spislave_write_tx_buffer + * + * Description: + * Write to SPI Slave peripheral hardware data buffer. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifndef ESP32S3_SPI_DMA +static void spislave_write_tx_buffer(struct spislave_priv_s *priv) +{ + /* Initialize data_buf_reg with the address of the first data buffer + * register (W0). + */ + + uintptr_t data_buf_reg = SPI_W0_REG(priv->config->id); + + uint32_t transfer_size = MIN(SPI_SLAVE_HW_BUF_SIZE, priv->tx_length); + + /* Write data words to hardware data buffer. + * SPI peripheral contains 16 registers (W0 - W15). + */ + + for (int i = 0; i < transfer_size; i += sizeof(uint32_t)) + { + uint32_t w_wd = UINT32_MAX; + + memcpy(&w_wd, priv->tx_buffer + i, sizeof(uint32_t)); + + putreg32(w_wd, data_buf_reg); + + /* Update data_buf_reg to point to the next data buffer register. */ + + data_buf_reg += sizeof(uintptr_t); + } +} +#endif + +/**************************************************************************** + * Name: spislave_setup_rx_dma + * + * Description: + * Configure the SPI Slave peripheral to perform the next RX data transfer + * via DMA. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +static void spislave_setup_rx_dma(struct spislave_priv_s *priv) +{ + uint32_t length = SPI_SLAVE_BUFSIZE - priv->rx_length; + + esp32s3_dma_setup(priv->dma_channel, + false, + priv->dma_rxdesc, + SPI_DMA_DESC_NUM, + priv->rx_buffer + priv->rx_length, + length); + + priv->rx_dma_offset = priv->rx_length; + + spislave_dma_rx_fifo_reset(priv); + + spislave_peripheral_reset(priv); + + /* Clear input FIFO full error */ + + setbits(SPI_DMA_INFIFO_FULL_ERR_INT_CLR_M, + SPI_DMA_INT_CLR_REG(priv->config->id)); + + /* Enable SPI DMA RX */ + + setbits(SPI_DMA_RX_ENA_M, SPI_DMA_CONF_REG(priv->config->id)); + + esp32s3_dma_enable(priv->dma_channel, false); +} +#endif + +/**************************************************************************** + * Name: spislave_setup_tx_dma + * + * Description: + * Configure the SPI Slave peripheral to perform the next TX data transfer + * via DMA. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +static void spislave_setup_tx_dma(struct spislave_priv_s *priv) +{ + esp32s3_dma_setup(priv->dma_channel, + true, + priv->dma_txdesc, + SPI_DMA_DESC_NUM, + priv->tx_buffer, + SPI_SLAVE_BUFSIZE); + + spislave_dma_tx_fifo_reset(priv); + + spislave_peripheral_reset(priv); + + /* Clear output FIFO empty error */ + + setbits(SPI_DMA_OUTFIFO_EMPTY_ERR_INT_CLR_M, + SPI_DMA_INT_CLR_REG(priv->config->id)); + + /* Enable SPI DMA TX */ + + setbits(SPI_DMA_TX_ENA_M, SPI_DMA_CONF_REG(priv->config->id)); + + esp32s3_dma_enable(priv->dma_channel, true); +} +#endif + +/**************************************************************************** + * Name: spislave_prepare_next_tx + * + * Description: + * Prepare the SPI Slave controller for transmitting data on the next + * transaction. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_prepare_next_tx(struct spislave_priv_s *priv) +{ + if (priv->tx_length != 0) + { +#ifdef ESP32S3_SPI_DMA + if (priv->config->dma_used) + { + spislave_setup_tx_dma(priv); + } +#else + spislave_peripheral_reset(priv); + + spislave_write_tx_buffer(priv); + + spislave_cpu_tx_fifo_reset(priv); +#endif + + priv->is_tx_enabled = true; + } + else + { + spiwarn("TX buffer empty! Disabling TX for next transaction\n"); + +#ifdef ESP32S3_SPI_DMA + if (!priv->config->dma_used) + { + spislave_cpu_tx_fifo_reset(priv); + } +#else + spislave_cpu_tx_fifo_reset(priv); +#endif + + priv->is_tx_enabled = false; + } +} + +/**************************************************************************** + * Name: spislave_periph_interrupt + * + * Description: + * Handler for the SPI Slave controller interrupt which is triggered when a + * transfer is finished. + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info + * arg - SPI Slave controller private data + * + * Returned Value: + * Standard interrupt return value. + * + ****************************************************************************/ + +static int spislave_periph_interrupt(int irq, void *context, void *arg) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)arg; + + uint32_t regval = getreg32(SPI_SLAVE1_REG(priv->config->id)); + uint32_t transfer_size = REG_MASK(regval, SPI_SLV_DATA_BITLEN) / 8; + + if (!priv->is_processing) + { + SPIS_DEV_SELECT(priv->dev, true); + priv->is_processing = true; + } + + /* RX process */ + + if (transfer_size > 0) + { + spislave_store_result(priv, transfer_size); + } + +#ifdef ESP32S3_SPI_DMA + if (priv->config->dma_used) + { + spislave_prepare_next_rx(priv); + } +#endif + + /* TX process */ + + if (priv->is_tx_enabled && transfer_size > 0) + { + spislave_evict_sent_data(priv, transfer_size); + } + + spislave_prepare_next_tx(priv); + + if (priv->is_processing && esp32s3_gpioread(priv->config->cs_pin)) + { + priv->is_processing = false; + SPIS_DEV_SELECT(priv->dev, false); + } + + /* Clear the trans_done interrupt flag */ + + setbits(SPI_TRANS_DONE_INT_CLR_M, SPI_DMA_INT_CLR_REG(priv->config->id)); + + /* Trigger the start of user-defined transaction */ + + setbits(SPI_USR_M, SPI_CMD_REG(priv->config->id)); + + return 0; +} + +/**************************************************************************** + * Name: spislave_dma_init + * + * Description: + * Initialize ESP32-S3 SPI Slave connection to GDMA engine. + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef ESP32S3_SPI_DMA +void spislave_dma_init(struct spislave_priv_s *priv) +{ + /* Enable GDMA clock for the SPI peripheral */ + + setbits(priv->config->dma_clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + + /* Reset GDMA for the SPI peripheral */ + + resetbits(priv->config->dma_rst_bit, SYSTEM_PERIP_RST_EN0_REG); + + /* Initialize GDMA controller */ + + esp32s3_dma_init(); + + /* Request a GDMA channel for SPI peripheral */ + + priv->dma_channel = esp32s3_dma_request(priv->config->dma_periph, 1, 1, + true); + if (priv->dma_channel < 0) + { + spierr("Failed to allocate GDMA channel\n"); + + DEBUGPANIC(); + } + + /* Disable segment transaction mode for SPI Slave */ + + resetbits(SPI_DMA_SLV_SEG_TRANS_EN_M, SPI_DMA_CONF_REG(priv->config->id)); + + /* Configure DMA In-Link EOF to be generated by trans_done */ + + resetbits(SPI_RX_EOF_EN_M, SPI_DMA_CONF_REG(priv->config->id)); +} +#endif + +/**************************************************************************** + * Name: spislave_gpio_initialize + * + * Description: + * Initialize ESP32-S3 SPI Slave GPIO + * + * Input Parameters: + * priv - Private SPI Slave controller structure + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_gpio_initialize(struct spislave_priv_s *priv) +{ + const struct spislave_config_s *config = priv->config; + + esp32s3_gpiowrite(config->cs_pin, 1); + esp32s3_gpiowrite(config->mosi_pin, 1); + esp32s3_gpiowrite(config->miso_pin, 1); + esp32s3_gpiowrite(config->clk_pin, 1); + +#if SPI_VIA_IOMUX != 0 + if (priv->config->id == 0) + { + esp32s3_configgpio(config->cs_pin, INPUT_FUNCTION_5 | PULLUP); + esp32s3_gpio_matrix_out(config->cs_pin, SIG_GPIO_OUT_IDX, 0, 0); + + esp32s3_configgpio(config->mosi_pin, INPUT_FUNCTION_5 | PULLUP); + esp32s3_gpio_matrix_out(config->mosi_pin, SIG_GPIO_OUT_IDX, 0, 0); + + esp32s3_configgpio(config->miso_pin, OUTPUT_FUNCTION_5 | PULLUP); + esp32s3_gpio_matrix_out(config->miso_pin, SIG_GPIO_OUT_IDX, 0, 0); + + esp32s3_configgpio(config->clk_pin, INPUT_FUNCTION_5 | PULLUP); + esp32s3_gpio_matrix_out(config->clk_pin, SIG_GPIO_OUT_IDX, 0, 0); + } + else + { + esp32s3_configgpio(config->cs_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->cs_pin, config->cs_insig, 0); + + esp32s3_configgpio(config->mosi_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->mosi_pin, config->mosi_insig, 0); + + esp32s3_configgpio(config->miso_pin, OUTPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_out(config->miso_pin, config->miso_outsig, 0, 0); + + esp32s3_configgpio(config->clk_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->clk_pin, config->clk_insig, 0); + } +#else + esp32s3_configgpio(config->cs_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->cs_pin, config->cs_insig, 0); + + esp32s3_configgpio(config->mosi_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->mosi_pin, config->mosi_insig, 0); + + esp32s3_configgpio(config->miso_pin, OUTPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_out(config->miso_pin, config->miso_outsig, 0, 0); + + esp32s3_configgpio(config->clk_pin, INPUT_FUNCTION_2 | PULLUP); + esp32s3_gpio_matrix_in(config->clk_pin, config->clk_insig, 0); +#endif +} + +/**************************************************************************** + * Name: spislave_initialize + * + * Description: + * Initialize ESP32-S3 SPI Slave hardware interface. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_initialize(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + const struct spislave_config_s *config = priv->config; + + spiinfo("ctrlr=%p\n", ctrlr); + + spislave_gpio_initialize(priv); + + setbits(config->clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + resetbits(config->rst_bit, SYSTEM_PERIP_RST_EN0_REG); + + /* Configure SPI Slave peripheral */ + + putreg32(0, SPI_CLOCK_REG(priv->config->id)); + + putreg32(SPI_DOUTDIN_M, SPI_USER_REG(priv->config->id)); + + putreg32(0, SPI_CTRL_REG(priv->config->id)); + + putreg32(SPI_SLAVE_MODE_M, SPI_SLAVE_REG(priv->config->id)); + + spislave_peripheral_reset(priv); + + /* Use all 64 bytes of the SPI hardware data buffer */ + + resetbits(SPI_USR_MISO_HIGHPART_M | SPI_USR_MOSI_HIGHPART_M, + SPI_USER_REG(priv->config->id)); + + /* Disable interrupts */ + + resetbits(SPI_INT_MASK, SPI_DMA_INT_ENA_REG(priv->config->id)); + +#ifdef ESP32S3_SPI_DMA + if (priv->config->dma_used) + { + spislave_dma_init(priv); + } +#endif + + esp32s3_gpioirqenable(ESP32S3_PIN2IRQ(config->cs_pin), GPIO_INTR_POSEDGE); + + /* Force a transaction done interrupt. + * This interrupt won't fire yet because we initialized the SPI interrupt + * as disabled. This way, we can just enable the SPI interrupt and the + * interrupt handler will kick in, handling any transactions that are + * queued. + */ + + setbits(SPI_TRANS_DONE_INT_RAW_M, SPI_DMA_INT_RAW_REG(priv->config->id)); + setbits(SPI_TRANS_DONE_INT_ENA_M, SPI_DMA_INT_ENA_REG(priv->config->id)); +} + +/**************************************************************************** + * Name: spislave_deinitialize + * + * Description: + * Deinitialize ESP32-S3 SPI Slave hardware interface. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_deinitialize(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + + esp32s3_gpioirqdisable(ESP32S3_PIN2IRQ(priv->config->cs_pin)); + + /* Disable the trans_done interrupt */ + + resetbits(SPI_TRANS_DONE_INT_ENA_M, SPI_DMA_INT_ENA_REG(priv->config->id)); + +#ifdef ESP32S3_SPI_DMA + if (priv->config->dma_used) + { + resetbits(priv->config->dma_clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + } + + priv->rx_dma_offset = 0; +#endif + + setbits(priv->config->clk_bit, SYSTEM_PERIP_RST_EN0_REG); + resetbits(priv->config->clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + + priv->mode = SPISLAVE_MODE0; + priv->nbits = 0; + priv->tx_length = 0; + priv->rx_length = 0; + priv->is_processing = false; + priv->is_tx_enabled = false; +} + +/**************************************************************************** + * Name: spislave_bind + * + * Description: + * Bind the SPI Slave device interface to the SPI Slave controller + * interface and configure the SPI interface. Upon return, the SPI + * slave controller driver is fully operational and ready to perform + * transfers. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * dev - SPI Slave device interface instance + * mode - The SPI mode requested + * nbits - The number of bits requests. + * If value is greater than 0, then it implies MSB first + * If value is less than 0, then it implies LSB first with -nbits + * + * Returned Value: + * None. + * + * Assumptions: + * This implementation currently supports only positive "nbits" values, + * i.e., it always configures the SPI Slave controller driver as MSB first. + * + ****************************************************************************/ + +static void spislave_bind(struct spi_slave_ctrlr_s *ctrlr, + struct spi_slave_dev_s *dev, + enum spi_slave_mode_e mode, + int nbits) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + const void *data = NULL; + irqstate_t flags; + size_t num_words; + + spiinfo("ctrlr=%p dev=%p mode=%d nbits=%d\n", ctrlr, dev, mode, nbits); + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev == NULL); + DEBUGASSERT(dev != NULL); + DEBUGASSERT(nbits > 0); + + flags = spin_lock_irqsave(&priv->lock); + + priv->dev = dev; + + SPIS_DEV_SELECT(dev, false); + + SPIS_DEV_CMDDATA(dev, false); + +#ifdef ESP32S3_SPI_DMA + priv->rx_dma_offset = 0; +#endif + + priv->rx_length = 0; + priv->tx_length = 0; + priv->is_tx_enabled = false; + + spislave_initialize(ctrlr); + + spislave_setmode(ctrlr, mode); + spislave_setbits(ctrlr, nbits); + + num_words = SPIS_DEV_GETDATA(dev, &data); + + if (data != NULL && num_words > 0) + { + size_t num_bytes = WORDS2BYTES(priv, num_words); + memcpy(priv->tx_buffer, data, num_bytes); + priv->tx_length += num_bytes; + } + + /* Enable the CPU interrupt that is linked to the SPI Slave controller */ + + up_enable_irq(priv->config->irq); + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: spislave_unbind + * + * Description: + * Un-bind the SPI Slave device interface from the SPI Slave controller + * interface. Reset the SPI interface and restore the SPI Slave + * controller driver to its initial state. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_unbind(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev != NULL); + + spiinfo("Unbinding %p\n", priv->dev); + + flags = spin_lock_irqsave(&priv->lock); + + up_disable_irq(priv->config->irq); + + esp32s3_gpioirqdisable(ESP32S3_PIN2IRQ(priv->config->cs_pin)); + + /* Disable the trans_done interrupt */ + + resetbits(SPI_TRANS_DONE_INT_ENA_M, SPI_DMA_INT_ENA_REG(priv->config->id)); + +#ifdef ESP32S3_SPI_DMA + if (priv->config->dma_used) + { + resetbits(priv->config->dma_clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + } +#endif + + resetbits(priv->config->clk_bit, SYSTEM_PERIP_CLK_EN0_REG); + + priv->dev = NULL; + + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: spislave_enqueue + * + * Description: + * Enqueue the next value to be shifted out from the interface. This adds + * the word to the controller driver for a subsequent transfer but has no + * effect on any in-process or currently "committed" transfers. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * data - Pointer to the command/data mode data to be shifted out. + * The data width must be aligned to the nbits parameter which was + * previously provided to the bind() method. + * len - Number of units of "nbits" wide to enqueue, + * "nbits" being the data width previously provided to the bind() + * method. + * + * Returned Value: + * Number of data items successfully queued, or a negated errno: + * - "len" if all the data was successfully queued + * - "0..len-1" if queue is full + * - "-errno" in any other error + * + ****************************************************************************/ + +static int spislave_enqueue(struct spi_slave_ctrlr_s *ctrlr, + const void *data, + size_t len) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + size_t num_bytes = WORDS2BYTES(priv, len); + size_t bufsize; + irqstate_t flags; + int enqueued_words; + + spiinfo("ctrlr=%p, data=%p, num_bytes=%zu\n", ctrlr, data, num_bytes); + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev != NULL); + + flags = spin_lock_irqsave(&priv->lock); + + bufsize = SPI_SLAVE_BUFSIZE - priv->tx_length; + if (bufsize == 0) + { + spin_unlock_irqrestore(&priv->lock, flags); + return -ENOSPC; + } + + num_bytes = MIN(num_bytes, bufsize); + memcpy(priv->tx_buffer + priv->tx_length, data, num_bytes); + priv->tx_length += num_bytes; + + enqueued_words = BYTES2WORDS(priv, num_bytes); + + if (!priv->is_processing) + { + spislave_prepare_next_tx(priv); + } + + spin_unlock_irqrestore(&priv->lock, flags); + + return enqueued_words; +} + +/**************************************************************************** + * Name: spislave_qfull + * + * Description: + * Return true if the queue is full or false if there is space to add an + * additional word to the queue. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * true if the output queue is full, false otherwise. + * + ****************************************************************************/ + +static bool spislave_qfull(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + irqstate_t flags; + bool is_full; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev != NULL); + + spiinfo("ctrlr=%p\n", ctrlr); + + flags = spin_lock_irqsave(&priv->lock); + is_full = priv->tx_length == SPI_SLAVE_BUFSIZE; + spin_unlock_irqrestore(&priv->lock, flags); + + return is_full; +} + +/**************************************************************************** + * Name: spislave_qflush + * + * Description: + * Discard all saved values in the output queue. On return from this + * function the output queue will be empty. Any in-progress or otherwise + * "committed" output values may not be flushed. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * None. + * + ****************************************************************************/ + +static void spislave_qflush(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + irqstate_t flags; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev != NULL); + + spiinfo("ctrlr=%p\n", ctrlr); + + flags = spin_lock_irqsave(&priv->lock); + priv->tx_length = 0; + priv->is_tx_enabled = false; + spin_unlock_irqrestore(&priv->lock, flags); +} + +/**************************************************************************** + * Name: spislave_qpoll + * + * Description: + * Tell the controller to output all the receive queue data. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * Number of units of width "nbits" left in the RX queue. If the device + * accepted all the data, the return value will be 0. + * + ****************************************************************************/ + +static size_t spislave_qpoll(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + irqstate_t flags; + uint32_t tmp; + uint32_t recv_n; + size_t remaining_words; + + DEBUGASSERT(priv != NULL); + DEBUGASSERT(priv->dev != NULL); + + spiinfo("ctrlr=%p\n", ctrlr); + + flags = spin_lock_irqsave(&priv->lock); + + tmp = SPIS_DEV_RECEIVE(priv->dev, priv->rx_buffer, + BYTES2WORDS(priv, priv->rx_length)); + recv_n = WORDS2BYTES(priv, tmp); + if (recv_n < priv->rx_length) + { + /* If the upper layer does not receive all of the data from the receive + * buffer, move the remaining data to the head of the buffer. + */ + + priv->rx_length -= recv_n; + memmove(priv->rx_buffer, priv->rx_buffer + recv_n, priv->rx_length); + } + else + { + priv->rx_length = 0; + } + + remaining_words = BYTES2WORDS(priv, priv->rx_length); + + spin_unlock_irqrestore(&priv->lock, flags); + + return remaining_words; +} + +/**************************************************************************** + * Name: esp32s3_spislave_ctrlr_initialize + * + * Description: + * Initialize the selected SPI Slave bus. + * + * Input Parameters: + * port - Port number (for hardware that has multiple SPI Slave interfaces) + * + * Returned Value: + * Valid SPI Slave controller structure reference on success; + * NULL on failure. + * + ****************************************************************************/ + +struct spi_slave_ctrlr_s *esp32s3_spislave_ctrlr_initialize(int port) +{ + struct spi_slave_ctrlr_s *spislave_dev; + struct spislave_priv_s *priv; + irqstate_t flags; + + switch (port) + { +#ifdef CONFIG_ESP32S3_SPI2 + case ESP32S3_SPI2: + priv = &esp32s3_spi2slave_priv; + break; +#endif +#ifdef CONFIG_ESP32S3_SPI3 + case ESP32S3_SPI3: + priv = &esp32s3_spi3slave_priv; + break; +#endif + default: + return NULL; + } + + spislave_dev = (struct spi_slave_ctrlr_s *)priv; + + flags = spin_lock_irqsave(&priv->lock); + + if ((volatile int)priv->refs != 0) + { + spin_unlock_irqrestore(&priv->lock, flags); + + return spislave_dev; + } + + /* Attach IRQ for CS pin interrupt */ + + DEBUGVERIFY(irq_attach(ESP32S3_PIN2IRQ(priv->config->cs_pin), + spislave_cs_interrupt, + priv)); + + priv->cpu = up_cpu_index(); + priv->cpuint = esp32s3_setup_irq(priv->cpu, + priv->config->periph, + ESP32S3_INT_PRIO_DEF, + ESP32S3_CPUINT_LEVEL); + if (priv->cpuint < 0) + { + /* Failed to allocate a CPU interrupt of this type. */ + + spin_unlock_irqrestore(&priv->lock, flags); + + return NULL; + } + + if (irq_attach(priv->config->irq, spislave_periph_interrupt, priv) != OK) + { + /* Failed to attach IRQ, so CPU interrupt must be freed. */ + + esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint); + spin_unlock_irqrestore(&priv->lock, flags); + + return NULL; + } + + priv->refs++; + + spin_unlock_irqrestore(&priv->lock, flags); + + return spislave_dev; +} + +/**************************************************************************** + * Name: esp32s3_spislave_ctrlr_uninitialize + * + * Description: + * Uninitialize an SPI Slave bus. + * + * Input Parameters: + * ctrlr - SPI Slave controller interface instance + * + * Returned Value: + * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * + ****************************************************************************/ + +int esp32s3_spislave_ctrlr_uninitialize(struct spi_slave_ctrlr_s *ctrlr) +{ + struct spislave_priv_s *priv = (struct spislave_priv_s *)ctrlr; + irqstate_t flags; + + DEBUGASSERT(ctrlr != NULL); + + if (priv->refs == 0) + { + return ERROR; + } + + flags = spin_lock_irqsave(&priv->lock); + + if (--priv->refs) + { + spin_unlock_irqrestore(&priv->lock, flags); + return OK; + } + + up_disable_irq(priv->config->irq); + esp32s3_teardown_irq(priv->cpu, priv->config->periph, priv->cpuint); + priv->cpuint = -ENOMEM; + + spislave_deinitialize(ctrlr); + + spin_unlock_irqrestore(&priv->lock, flags); + + return OK; +} + +#endif /* defined(CONFIG_ESP32S3_SPI) && defined (CONFIG_SPI_SLAVE) */