diff --git a/arch/risc-v/src/esp32c3/Kconfig b/arch/risc-v/src/esp32c3/Kconfig index bce1ec8f70..99f02133c8 100644 --- a/arch/risc-v/src/esp32c3/Kconfig +++ b/arch/risc-v/src/esp32c3/Kconfig @@ -323,6 +323,29 @@ config ESP32C3_SPI_UDCS if ESP32C3_SPI2 +config ESP32C3_SPI2_DMA + bool "SPI2 use GDMA" + default n + depends on ESP32C3_DMA + ---help--- + Enable support for transfers using the GDMA engine. + +config ESP32C3_SPI2_DMADESC_NUM + int "SPI2 Master GDMA maximum number of descriptors" + default 2 + depends on ESP32C3_SPI2_DMA + ---help--- + Configure the maximum number of out-link/in-link descriptors to + be chained for a GDMA transfer. + +config ESP32C3_SPI2_DMATHRESHOLD + int "SPI2 GDMA threshold" + default 64 + depends on ESP32C3_SPI2_DMA + ---help--- + When SPI GDMA is enabled, GDMA transfers whose size are below the + defined threshold will be performed by polling logic. + config ESP32C3_SPI2_CSPIN int "SPI2 CS Pin" default 10 diff --git a/arch/risc-v/src/esp32c3/esp32c3_spi.c b/arch/risc-v/src/esp32c3/esp32c3_spi.c index d65055b1a5..6010d25a0a 100644 --- a/arch/risc-v/src/esp32c3/esp32c3_spi.c +++ b/arch/risc-v/src/esp32c3/esp32c3_spi.c @@ -32,17 +32,24 @@ #include #include #include +#include #include #include +#include #include #include #include #include "esp32c3_spi.h" +#include "esp32c3_irq.h" #include "esp32c3_gpio.h" +#ifdef CONFIG_ESP32C3_SPI2_DMA +#include "esp32c3_dma.h" +#endif + #include "riscv_arch.h" #include "hardware/esp32c3_gpio_sigmap.h" #include "hardware/esp32c3_pinmap.h" @@ -62,6 +69,18 @@ # define SPI_HAVE_SWCS FALSE #endif +#ifdef CONFIG_ESP32C3_SPI2_DMA + +/* SPI DMA RX/TX number of descriptors */ + +#define SPI_DMA_DESC_NUM (CONFIG_ESP32C3_SPI2_DMADESC_NUM) + +/* SPI DMA reset before exchange */ + +#define SPI_DMA_RESET_MASK (SPI_DMA_AFIFO_RST_M | SPI_RX_AFIFO_RST_M) + +#endif + /* Verify whether SPI has been assigned IOMUX pins. * Otherwise, SPI signals will be routed via GPIO Matrix. */ @@ -119,8 +138,16 @@ struct esp32c3_spi_config_s uint8_t mosi_pin; /* GPIO configuration for MOSI */ uint8_t miso_pin; /* GPIO configuration for MISO */ uint8_t clk_pin; /* GPIO configuration for CLK */ +#ifdef CONFIG_ESP32C3_SPI2_DMA + uint8_t periph; /* Peripheral ID */ + uint8_t irq; /* Interrupt ID */ +#endif uint32_t clk_bit; /* Clock enable bit */ uint32_t rst_bit; /* SPI reset bit */ +#ifdef CONFIG_ESP32C3_SPI2_DMA + uint32_t dma_clk_bit; /* DMA clock enable bit */ + uint32_t dma_rst_bit; /* DMA reset bit */ +#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 */ @@ -142,6 +169,11 @@ struct esp32c3_spi_priv_s const struct esp32c3_spi_config_s *config; int refs; /* Reference count */ sem_t exclsem; /* Held while chip is selected for mutual exclusion */ +#ifdef CONFIG_ESP32C3_SPI2_DMA + sem_t sem_isr; /* Interrupt wait semaphore */ + int cpuint; /* SPI interrupt ID */ + int32_t dma_channel; /* Channel assigned by the GDMA driver */ +#endif uint32_t frequency; /* Requested clock frequency */ uint32_t actual; /* Actual clock frequency */ enum spi_mode_e mode; /* Actual SPI hardware mode */ @@ -170,6 +202,19 @@ static uint32_t esp32c3_spi_send(FAR struct spi_dev_s *dev, uint32_t wd); static void esp32c3_spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, FAR void *rxbuffer, size_t nwords); +#ifdef CONFIG_ESP32C3_SPI2_DMA +static int esp32c3_spi_interrupt(int irq, void *context, FAR void *arg); +static int esp32c3_spi_sem_waitdone(FAR struct esp32c3_spi_priv_s *priv); +static void esp32c3_spi_dma_exchange(FAR struct esp32c3_spi_priv_s *priv, + FAR const void *txbuffer, + FAR void *rxbuffer, + uint32_t nwords); +#else +static void esp32c3_spi_poll_exchange(FAR struct esp32c3_spi_priv_s *priv, + FAR const void *txbuffer, + FAR void *rxbuffer, + size_t nwords); +#endif #ifndef CONFIG_SPI_EXCHANGE static void esp32c3_spi_sndblock(FAR struct spi_dev_s *dev, FAR const void *txbuffer, @@ -181,6 +226,9 @@ static void esp32c3_spi_recvblock(FAR struct spi_dev_s *dev, #ifdef CONFIG_SPI_TRIGGER static int esp32c3_spi_trigger(FAR struct spi_dev_s *dev); #endif +#ifdef CONFIG_ESP32C3_SPI2_DMA +static void esp32c3_spi_dma_init(FAR struct spi_dev_s *dev); +#endif static void esp32c3_spi_init(FAR struct spi_dev_s *dev); static void esp32c3_spi_deinit(FAR struct spi_dev_s *dev); @@ -198,8 +246,16 @@ static const struct esp32c3_spi_config_s esp32c3_spi2_config = .mosi_pin = CONFIG_ESP32C3_SPI2_MOSIPIN, .miso_pin = CONFIG_ESP32C3_SPI2_MISOPIN, .clk_pin = CONFIG_ESP32C3_SPI2_CLKPIN, +#ifdef CONFIG_ESP32C3_SPI2_DMA + .periph = ESP32C3_PERIPH_SPI2, + .irq = ESP32C3_IRQ_SPI2, +#endif .clk_bit = SYSTEM_SPI2_CLK_EN, .rst_bit = SYSTEM_SPI2_RST, +#ifdef CONFIG_ESP32C3_SPI2_DMA + .dma_clk_bit = SYSTEM_SPI2_DMA_CLK_EN, + .dma_rst_bit = SYSTEM_SPI2_DMA_RST, +#endif .cs_insig = FSPICS0_IN_IDX, .cs_outsig = FSPICS0_OUT_IDX, .mosi_insig = FSPID_IN_IDX, @@ -243,20 +299,34 @@ static const struct spi_ops_s esp32c3_spi2_ops = static struct esp32c3_spi_priv_s esp32c3_spi2_priv = { - .spi_dev = - { - .ops = &esp32c3_spi2_ops - }, - .config = &esp32c3_spi2_config, - .refs = 0, - .exclsem = SEM_INITIALIZER(0), - .frequency = 0, - .actual = 0, - .mode = 0, - .nbits = 0 + .spi_dev = + { + .ops = &esp32c3_spi2_ops + }, + .config = &esp32c3_spi2_config, + .refs = 0, + .exclsem = SEM_INITIALIZER(0), +#ifdef CONFIG_ESP32C3_SPI2_DMA + .sem_isr = SEM_INITIALIZER(0), + .cpuint = -ENOMEM, + .dma_channel = -1, +#endif + .frequency = 0, + .actual = 0, + .mode = 0, + .nbits = 0 }; #endif /* CONFIG_ESP32C3_SPI2 */ +#ifdef CONFIG_ESP32C3_SPI2_DMA + +/* SPI DMA RX/TX description */ + +static struct esp32c3_dmadesc_s dma_rxdesc[SPI_DMA_DESC_NUM]; +static struct esp32c3_dmadesc_s dma_txdesc[SPI_DMA_DESC_NUM]; + +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -272,7 +342,7 @@ static struct esp32c3_spi_priv_s esp32c3_spi2_priv = * bits - Bits to be set * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -294,7 +364,7 @@ static inline void esp32c3_spi_set_regbits(uint32_t addr, uint32_t bits) * bits - Bits to be cleared * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -313,10 +383,10 @@ static inline void esp32c3_spi_clr_regbits(uint32_t addr, uint32_t bits) * * Input Parameters: * dev - Device-specific state data - * lock - true: Lock spi bus, false: unlock SPI bus + * lock - true: Lock SPI bus, false: unlock SPI bus * * Returned Value: - * The result of lock or unlock the SPI device + * The result of lock or unlock the SPI device. * ****************************************************************************/ @@ -337,6 +407,37 @@ static int esp32c3_spi_lock(FAR struct spi_dev_s *dev, bool lock) return ret; } +/**************************************************************************** + * Name: esp32c3_spi_sem_waitdone + * + * Description: + * Wait for a transfer to complete. + * + * Input Parameters: + * priv - SPI private state data + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32C3_SPI2_DMA +static int esp32c3_spi_sem_waitdone(FAR struct esp32c3_spi_priv_s *priv) +{ + int ret; + struct timespec abstime; + + clock_gettime(CLOCK_REALTIME, &abstime); + + abstime.tv_sec += 10; + abstime.tv_nsec += 0; + + ret = nxsem_timedwait_uninterruptible(&priv->sem_isr, &abstime); + + return ret; +} +#endif + /**************************************************************************** * Name: esp32c3_spi_select * @@ -356,7 +457,7 @@ static int esp32c3_spi_lock(FAR struct spi_dev_s *dev, bool lock) * selected - true: slave selected, false: slave de-selected * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -509,7 +610,7 @@ static uint32_t esp32c3_spi_setfrequency(FAR struct spi_dev_s *dev, * mode - The requested SPI mode * * Returned Value: - * none + * None. * ****************************************************************************/ @@ -578,7 +679,7 @@ static void esp32c3_spi_setmode(FAR struct spi_dev_s *dev, * nbits - The number of bits in an SPI word. * * Returned Value: - * none + * None. * ****************************************************************************/ @@ -617,6 +718,115 @@ static int esp32c3_spi_hwfeatures(FAR struct spi_dev_s *dev, } #endif +/**************************************************************************** + * Name: esp32c3_spi_dma_exchange + * + * Description: + * Exchange a block of data from SPI by DMA. + * + * Input Parameters: + * priv - SPI private state data + * txbuffer - A pointer to the buffer of data to be sent + * rxbuffer - A pointer to the buffer in which to receive data + * nwords - the length of data that to be exchanged in units of words. + * The wordsize is determined by the number of bits-per-word + * selected for the SPI interface. If nbits <= 8, the data is + * packed into uint8_t's; if nbits >8, the data is packed into + * uint16_t's + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32C3_SPI2_DMA +static void esp32c3_spi_dma_exchange(FAR struct esp32c3_spi_priv_s *priv, + FAR const void *txbuffer, + FAR void *rxbuffer, + uint32_t nwords) +{ + const uint32_t total = nwords * (priv->nbits / 8); + const int32_t channel = priv->dma_channel; + uint32_t bytes = total; + uint32_t n; + uint8_t *tp; + uint8_t *rp; + + DEBUGASSERT((txbuffer != NULL) || (rxbuffer != NULL)); + + spiinfo("nwords=%" PRIu32 "\n", nwords); + + tp = (uint8_t *)txbuffer; + rp = (uint8_t *)rxbuffer; + + if (tp == NULL) + { + tp = rp; + } + + esp32c3_spi_clr_regbits(SPI_DMA_INT_RAW_REG, SPI_TRANS_DONE_INT_RAW_M); + + esp32c3_spi_set_regbits(SPI_DMA_INT_ENA_REG, SPI_TRANS_DONE_INT_ENA_M); + + while (bytes != 0) + { + /* Reset SPI DMA TX FIFO */ + + esp32c3_spi_set_regbits(SPI_DMA_CONF_REG, SPI_DMA_RESET_MASK); + esp32c3_spi_clr_regbits(SPI_DMA_CONF_REG, SPI_DMA_RESET_MASK); + + /* Enable SPI DMA TX */ + + esp32c3_spi_set_regbits(SPI_DMA_CONF_REG, SPI_DMA_TX_ENA_M); + + n = esp32c3_dma_setup(channel, true, dma_txdesc, SPI_DMA_DESC_NUM, + tp, bytes); + esp32c3_dma_enable(channel, true); + + putreg32((n * 8 - 1), SPI_MS_DLEN_REG); + esp32c3_spi_set_regbits(SPI_USER_REG, SPI_USR_MOSI_M); + + tp += n; + + if (rp != NULL) + { + /* Enable SPI DMA RX */ + + esp32c3_spi_set_regbits(SPI_DMA_CONF_REG, SPI_DMA_RX_ENA_M); + + esp32c3_dma_setup(channel, false, dma_rxdesc, SPI_DMA_DESC_NUM, + rp, bytes); + esp32c3_dma_enable(channel, false); + + esp32c3_spi_set_regbits(SPI_USER_REG, SPI_USR_MISO_M); + + rp += n; + } + else + { + esp32c3_spi_clr_regbits(SPI_USER_REG, SPI_USR_MISO_M); + } + + /* Trigger start of user-defined transaction for master. */ + + esp32c3_spi_set_regbits(SPI_CMD_REG, SPI_UPDATE_M); + + while ((getreg32(SPI_CMD_REG) & SPI_UPDATE_M) != 0) + { + ; + } + + esp32c3_spi_set_regbits(SPI_CMD_REG, SPI_USR_M); + + esp32c3_spi_sem_waitdone(priv); + + bytes -= n; + } + + esp32c3_spi_clr_regbits(SPI_DMA_INT_ENA_REG, SPI_TRANS_DONE_INT_ENA_M); +} +#endif + /**************************************************************************** * Name: esp32c3_spi_poll_send * @@ -629,7 +839,7 @@ static int esp32c3_spi_hwfeatures(FAR struct spi_dev_s *dev, * number of bits selected for the SPI interface. * * Returned Value: - * Received value + * Received value. * ****************************************************************************/ @@ -679,7 +889,7 @@ static uint32_t esp32c3_spi_poll_send(FAR struct esp32c3_spi_priv_s *priv, * number of bits selected for the SPI interface. * * Returned Value: - * Received value + * Received value. * ****************************************************************************/ @@ -707,7 +917,7 @@ static uint32_t esp32c3_spi_send(FAR struct spi_dev_s *dev, uint32_t wd) * uint16_t's * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -836,7 +1046,7 @@ static void esp32c3_spi_poll_exchange(FAR struct esp32c3_spi_priv_s *priv, * uint16_t's * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -847,7 +1057,18 @@ static void esp32c3_spi_exchange(FAR struct spi_dev_s *dev, { FAR struct esp32c3_spi_priv_s *priv = (FAR struct esp32c3_spi_priv_s *)dev; - esp32c3_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords); +#ifdef CONFIG_ESP32C3_SPI2_DMA + size_t thld = CONFIG_ESP32C3_SPI2_DMATHRESHOLD; + + if (nwords > thld) + { + esp32c3_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords); + } + else +#endif + { + esp32c3_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords); + } } #ifndef CONFIG_SPI_EXCHANGE @@ -868,7 +1089,7 @@ static void esp32c3_spi_exchange(FAR struct spi_dev_s *dev, * packed into uint16_t's * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -897,7 +1118,7 @@ static void esp32c3_spi_sndblock(FAR struct spi_dev_s *dev, * packed into uint16_t's * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -934,6 +1155,55 @@ static int esp32c3_spi_trigger(FAR struct spi_dev_s *dev) } #endif +/**************************************************************************** + * Name: esp32c3_spi_dma_init + * + * Description: + * Initialize ESP32-C3 SPI connection to GDMA engine. + * + * Input Parameters: + * dev - Device-specific state data + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32C3_SPI2_DMA +void esp32c3_spi_dma_init(FAR struct spi_dev_s *dev) +{ + FAR struct esp32c3_spi_priv_s *priv = (FAR struct esp32c3_spi_priv_s *)dev; + + /* Enable GDMA clock for the SPI peripheral */ + + modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, 0, priv->config->dma_clk_bit); + + /* Reset GDMA for the SPI peripheral */ + + modifyreg32(SYSTEM_PERIP_RST_EN0_REG, priv->config->dma_rst_bit, 0); + + /* Initialize GDMA controller */ + + esp32c3_dma_init(); + + /* Request a GDMA channel for SPI peripheral */ + + priv->dma_channel = esp32c3_dma_request(ESP32C3_DMA_PERIPH_SPI, 1, 1, + true); + if (priv->dma_channel < 0) + { + spierr("Failed to allocate GDMA channel\n"); + + DEBUGASSERT(false); + } + + /* Disable segment transaction mode for SPI Master */ + + putreg32((SPI_SLV_RX_SEG_TRANS_CLR_EN_M | SPI_SLV_TX_SEG_TRANS_CLR_EN_M), + SPI_DMA_CONF_REG); +} +#endif + /**************************************************************************** * Name: esp32c3_spi_init * @@ -944,7 +1214,7 @@ static int esp32c3_spi_trigger(FAR struct spi_dev_s *dev) * dev - Device-specific state data * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -1015,6 +1285,13 @@ static void esp32c3_spi_init(FAR struct spi_dev_s *dev) putreg32(0, SPI_CTRL_REG); putreg32(VALUE_MASK(0, SPI_CS_HOLD_TIME), SPI_USER1_REG); +#ifdef CONFIG_ESP32C3_SPI2_DMA + nxsem_init(&priv->sem_isr, 0, 0); + nxsem_set_protocol(&priv->sem_isr, SEM_PRIO_NONE); + + esp32c3_spi_dma_init(dev); +#endif + esp32c3_spi_setfrequency(dev, config->clk_freq); esp32c3_spi_setbits(dev, config->width); esp32c3_spi_setmode(dev, config->mode); @@ -1030,7 +1307,7 @@ static void esp32c3_spi_init(FAR struct spi_dev_s *dev) * dev - Device-specific state data * * Returned Value: - * None + * None. * ****************************************************************************/ @@ -1038,6 +1315,10 @@ static void esp32c3_spi_deinit(FAR struct spi_dev_s *dev) { FAR struct esp32c3_spi_priv_s *priv = (FAR struct esp32c3_spi_priv_s *)dev; +#ifdef CONFIG_ESP32C3_SPI2_DMA + modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->dma_clk_bit, 0); +#endif + modifyreg32(SYSTEM_PERIP_RST_EN0_REG, 0, priv->config->clk_bit); modifyreg32(SYSTEM_PERIP_CLK_EN0_REG, priv->config->clk_bit, 0); @@ -1047,6 +1328,34 @@ static void esp32c3_spi_deinit(FAR struct spi_dev_s *dev) priv->nbits = 0; } +/**************************************************************************** + * Name: esp32c3_spi_interrupt + * + * Description: + * Common SPI DMA interrupt handler. + * + * Input Parameters: + * irq - Number of the IRQ that generated the interrupt + * context - Interrupt register state save info + * arg - SPI controller private data + * + * Returned Value: + * Standard interrupt return value. + * + ****************************************************************************/ + +#ifdef CONFIG_ESP32C3_SPI2_DMA +static int esp32c3_spi_interrupt(int irq, void *context, FAR void *arg) +{ + FAR struct esp32c3_spi_priv_s *priv = (FAR struct esp32c3_spi_priv_s *)arg; + + esp32c3_spi_clr_regbits(SPI_DMA_INT_RAW_REG, SPI_TRANS_DONE_INT_RAW_M); + nxsem_post(&priv->sem_isr); + + return 0; +} +#endif + /**************************************************************************** * Name: esp32c3_spibus_initialize * @@ -1057,7 +1366,7 @@ static void esp32c3_spi_deinit(FAR struct spi_dev_s *dev) * port - Port number (for hardware that has multiple SPI interfaces) * * Returned Value: - * Valid SPI device structure reference on success; NULL on failure + * Valid SPI device structure reference on success; NULL on failure. * ****************************************************************************/ @@ -1089,6 +1398,42 @@ FAR struct spi_dev_s *esp32c3_spibus_initialize(int port) return spi_dev; } +#ifdef CONFIG_ESP32C3_SPI2_DMA + if (priv->cpuint != -ENOMEM) + { + /* Disable the provided CPU Interrupt to configure it. */ + + up_disable_irq(priv->cpuint); + } + + priv->cpuint = esp32c3_request_irq(priv->config->periph, + ESP32C3_INT_PRIO_DEF, + ESP32C3_INT_LEVEL); + if (priv->cpuint < 0) + { + /* Failed to allocate a CPU interrupt of this type. */ + + leave_critical_section(flags); + + return NULL; + } + + if (irq_attach(priv->config->irq, esp32c3_spi_interrupt, priv) != OK) + { + /* Failed to attach IRQ, so CPU interrupt must be freed. */ + + esp32c3_free_cpuint(priv->config->periph); + priv->cpuint = -ENOMEM; + leave_critical_section(flags); + + return NULL; + } + + /* Enable the CPU interrupt that is linked to the SPI device. */ + + up_enable_irq(priv->cpuint); +#endif + esp32c3_spi_init(spi_dev); priv->refs++; @@ -1108,7 +1453,7 @@ FAR struct spi_dev_s *esp32c3_spibus_initialize(int port) * dev - Device-specific state data * * Returned Value: - * Zero (OK) is returned on success. Otherwise -1 (ERROR). + * Zero (OK) is returned on success. Otherwise -1 (ERROR). * ****************************************************************************/ @@ -1134,6 +1479,14 @@ int esp32c3_spibus_uninitialize(FAR struct spi_dev_s *dev) leave_critical_section(flags); +#ifdef CONFIG_ESP32C3_SPI2_DMA + up_disable_irq(priv->cpuint); + esp32c3_free_cpuint(priv->config->periph); + priv->cpuint = -ENOMEM; + + nxsem_destroy(&priv->sem_isr); +#endif + esp32c3_spi_deinit(dev); nxsem_destroy(&priv->exclsem);