diff --git a/arch/xtensa/src/esp32s2/esp32s2_spi.c b/arch/xtensa/src/esp32s2/esp32s2_spi.c index 4606b191a0cc0..b591ea8e79f03 100644 --- a/arch/xtensa/src/esp32s2/esp32s2_spi.c +++ b/arch/xtensa/src/esp32s2/esp32s2_spi.c @@ -80,7 +80,7 @@ /* SPI DMA reset before exchange */ -#define SPI_DMA_RESET_MASK (SPI_DMA_AFIFO_RST_M | SPI_RX_AFIFO_RST_M) +#define SPI_DMA_RESET_MASK (SPI_AHBM_RST | SPI_AHBM_FIFO_RST) #endif @@ -96,6 +96,13 @@ #define SPI_DEFAULT_MODE (SPIDEV_MODE0) +/* Helper for applying the mask for a given register field. + * Mask is determined by the macros suffixed with _V and _S from the + * peripheral register description. + */ + +#define VALUE_MASK(_val, _field) ((_val & (_field##_V)) << (_field##_S)) + /* SPI Maximum buffer size in bytes */ #define SPI_MAX_BUF_SIZE (64) @@ -359,7 +366,7 @@ static struct esp32s2_spi_priv_s esp32s2_spi2_priv = .cpuint = -ENOMEM, .dma_channel = -1, .dma_rxdesc = spi2_dma_rxdesc, - .dma_txdesc = spi2_dma_rxdesc, + .dma_txdesc = spi2_dma_txdesc, #endif .frequency = 0, .actual = 0, @@ -444,7 +451,7 @@ static struct esp32s2_spi_priv_s esp32s2_spi3_priv = .cpuint = -ENOMEM, .dma_channel = -1, .dma_rxdesc = spi3_dma_rxdesc, - .dma_txdesc = spi3_dma_rxdesc, + .dma_txdesc = spi3_dma_txdesc, #endif .frequency = 0, .actual = 0, @@ -897,88 +904,193 @@ static int esp32s2_spi_hwfeatures(struct spi_dev_s *dev, ****************************************************************************/ #if defined(CONFIG_ESP32S2_SPI2_DMA) || defined(CONFIG_ESP32S2_SPI3_DMA) +/**************************************************************************** + * Name: esp32s2_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. + * + ****************************************************************************/ + static void esp32s2_spi_dma_exchange(struct esp32s2_spi_priv_s *priv, const void *txbuffer, void *rxbuffer, uint32_t nwords) { - const struct esp32s2_dmadesc_s *dma_rxdesc = priv->dma_rxdesc; - const struct esp32s2_dmadesc_s *dma_txdesc = priv->dma_txdesc; const uint32_t total = nwords * (priv->nbits / 8); - const int32_t channel = priv->dma_channel; - const uint32_t id = priv->config->id; uint32_t bytes = total; - uint32_t n; uint8_t *tp; uint8_t *rp; + uint32_t n; + uint32_t regval; + struct esp32s2_dmadesc_s *dma_tx_desc; + struct esp32s2_dmadesc_s *dma_rx_desc; +#if defined(CONFIG_ESP32S2_SPIRAM) && defined(CONFIG_ESP32S2_SPI3_DMA) + uint8_t *alloctp = NULL; + uint8_t *allocrp = NULL; +#endif + + /* Define these constants outside transfer loop to avoid wasting CPU time + * with register offset calculation. + */ + + const uint32_t id = priv->config->id; + const uintptr_t spi_dma_in_link_reg = SPI_DMA_IN_LINK_REG(id); + const uintptr_t spi_dma_out_link_reg = SPI_DMA_OUT_LINK_REG(id); + const uintptr_t spi_slave_reg = SPI_SLAVE_REG(id); + const uintptr_t spi_dma_conf_reg = SPI_DMA_CONF_REG(id); + const uintptr_t spi_mosi_dlen_reg = SPI_MOSI_DLEN_REG(id); + const uintptr_t spi_miso_dlen_reg = SPI_MISO_DLEN_REG(id); + const uintptr_t spi_user_reg = SPI_USER_REG(id); + const uintptr_t spi_cmd_reg = SPI_CMD_REG(id); DEBUGASSERT((txbuffer != NULL) || (rxbuffer != NULL)); spiinfo("nwords=%" PRIu32 "\n", nwords); - tp = (uint8_t *)txbuffer; - rp = (uint8_t *)rxbuffer; +#if defined(CONFIG_ESP32S2_SPIRAM) && defined(CONFIG_ESP32S2_SPI3_DMA) + if (esp32s2_ptr_extram(txbuffer) && (id == 3)) + { +# ifdef CONFIG_MM_KERNEL_HEAP + alloctp = kmm_malloc(total); +# elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) + alloctp = xtensa_imm_malloc(total); +# endif + + DEBUGASSERT(alloctp != NULL); + memcpy(alloctp, txbuffer, total); + tp = alloctp; + } + else +#endif + { + tp = (uint8_t *)txbuffer; + } + +#if defined(CONFIG_ESP32S2_SPIRAM) && defined(CONFIG_ESP32S2_SPI3_DMA) + if (esp32s2_ptr_extram(rxbuffer) && (id == 3)) + { +# ifdef CONFIG_MM_KERNEL_HEAP + allocrp = kmm_malloc(total); +# elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) + allocrp = xtensa_imm_malloc(total); +# endif + + DEBUGASSERT(allocrp != NULL); + rp = allocrp; + } + else +#endif + { + rp = (uint8_t *)rxbuffer; + } if (tp == NULL) { tp = rp; } - esp32s2_spi_set_regbits(SPI_DMA_INT_CLR_REG(id), - SPI_IN_DONE_INT_CLR_M); +#ifdef CONFIG_ESP32S2_SPI2_DMA + if (id == 2) + { + dma_tx_desc = spi2_dma_txdesc; + dma_rx_desc = spi2_dma_rxdesc; + } +#endif - esp32s2_spi_set_regbits(SPI_DMA_INT_ENA_REG(id), - SPI_IN_DONE_INT_ENA_M); +#ifdef CONFIG_ESP32S2_SPI3_DMA + if (id == 3) + { + dma_tx_desc = spi3_dma_txdesc; + dma_rx_desc = spi3_dma_rxdesc; + } +#endif + + esp32s2_spi_clr_regbits(spi_slave_reg, SPI_TRANS_DONE_M); + esp32s2_spi_set_regbits(spi_slave_reg, SPI_INT_EN_M); while (bytes != 0) { - /* Enable SPI DMA TX */ + putreg32(0, spi_dma_in_link_reg); + putreg32(0, spi_dma_out_link_reg); - esp32s2_spi_set_regbits(SPI_DMA_CONF_REG(id), - SPI_DMA_TX_ENA_M); + esp32s2_spi_set_regbits(spi_slave_reg, SPI_SOFT_RESET_M); + esp32s2_spi_clr_regbits(spi_slave_reg, SPI_SOFT_RESET_M); - n = esp32s2_dma_setup(channel, true, dma_txdesc, SPI_DMA_DESC_NUM, - tp, bytes); - esp32s2_dma_enable(channel, true); + esp32s2_spi_set_regbits(spi_dma_conf_reg, SPI_DMA_RESET_MASK); + esp32s2_spi_clr_regbits(spi_dma_conf_reg, SPI_DMA_RESET_MASK); - putreg32((n * 8 - 1), SPI_MOSI_DLEN_REG(id)); - esp32s2_spi_set_regbits(SPI_USER_REG(id), - SPI_USR_MOSI_M); + n = esp32s2_dma_init(dma_tx_desc, SPI_DMA_DESC_NUM, tp, bytes); + + regval = VALUE_MASK((uintptr_t)dma_tx_desc, SPI_OUTLINK_ADDR); + regval |= SPI_OUTLINK_START_M; + putreg32(regval, spi_dma_out_link_reg); + putreg32((n * 8 - 1), spi_mosi_dlen_reg); + esp32s2_spi_set_regbits(spi_user_reg, SPI_USR_MOSI_M); tp += n; if (rp != NULL) { - /* Enable SPI DMA RX */ - - esp32s2_spi_set_regbits(SPI_DMA_CONF_REG(id), - SPI_DMA_RX_ENA_M); - - esp32s2_dma_setup(channel, false, dma_rxdesc, SPI_DMA_DESC_NUM, - rp, bytes); - esp32s2_dma_enable(channel, false); + esp32s2_dma_init(dma_rx_desc, SPI_DMA_DESC_NUM, rp, bytes); - esp32s2_spi_set_regbits(SPI_USER_REG(id), - SPI_USR_MISO_M); + regval = VALUE_MASK((uintptr_t)dma_rx_desc, SPI_INLINK_ADDR); + regval |= SPI_INLINK_START_M; + putreg32(regval, spi_dma_in_link_reg); + putreg32((n * 8 - 1), spi_miso_dlen_reg); + esp32s2_spi_set_regbits(spi_user_reg, SPI_USR_MISO_M); rp += n; } else { - esp32s2_spi_clr_regbits(SPI_USER_REG(id), SPI_USR_MISO_M); + esp32s2_spi_clr_regbits(spi_user_reg, SPI_USR_MISO_M); } - /* Trigger start of user-defined transaction for master. */ - - esp32s2_spi_set_regbits(SPI_CMD_REG(id), SPI_USR_M); + esp32s2_spi_set_regbits(spi_cmd_reg, SPI_USR_M); esp32s2_spi_sem_waitdone(priv); bytes -= n; } - esp32s2_spi_clr_regbits(SPI_DMA_INT_ENA_REG(id), - SPI_IN_DONE_INT_ENA_M); + esp32s2_spi_clr_regbits(spi_slave_reg, SPI_INT_EN_M); + +#if defined(CONFIG_ESP32S2_SPIRAM) && defined(CONFIG_ESP32S2_SPI3_DMA) + if (allocrp) + { + memcpy(rxbuffer, allocrp, total); +# ifdef CONFIG_MM_KERNEL_HEAP + kmm_free(allocrp); +# elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) + xtensa_imm_free(allocrp); +# endif + } +#endif + +#if defined(CONFIG_ESP32S2_SPIRAM) && defined(CONFIG_ESP32S2_SPI3_DMA) + if (alloctp) + { +# ifdef CONFIG_MM_KERNEL_HEAP + kmm_free(alloctp); +# elif defined(CONFIG_XTENSA_IMEM_USE_SEPARATE_HEAP) + xtensa_imm_free(alloctp); +# endif + } +#endif } #endif @@ -1321,6 +1433,7 @@ void esp32s2_spi_dma_init(struct spi_dev_s *dev) { struct esp32s2_spi_priv_s *priv = (struct esp32s2_spi_priv_s *)dev; const uint32_t id = priv->config->id; + uint32_t regval; /* Enable DMA clock for the SPI peripheral */ @@ -1330,9 +1443,12 @@ void esp32s2_spi_dma_init(struct spi_dev_s *dev) modifyreg32(SYSTEM_PERIP_RST_EN0_REG, priv->config->dma_rst_bit, 0); - /* Initialize DMA controller */ + /* Enable DMA burst */ - esp32s2_dma_init(); + regval = SPI_OUT_DATA_BURST_EN_M | + SPI_INDSCR_BURST_EN_M | + SPI_OUTDSCR_BURST_EN_M; + putreg32(regval, SPI_DMA_CONF_REG(id)); /* Disable segment transaction mode for SPI Master */ diff --git a/arch/xtensa/src/esp32s2/hardware/esp32s2_spi.h b/arch/xtensa/src/esp32s2/hardware/esp32s2_spi.h index 750d4853ce681..cb948e6764b3c 100644 --- a/arch/xtensa/src/esp32s2/hardware/esp32s2_spi.h +++ b/arch/xtensa/src/esp32s2/hardware/esp32s2_spi.h @@ -1015,6 +1015,15 @@ #define SPI_INT_RD_BUF_DONE_EN_V 0x00000001 #define SPI_INT_RD_BUF_DONE_EN_S 5 +/* SPI_INT_EN : R/W ;bitpos:[11:5] ;default: 7'b001_0000 ; */ + +/* Description: Interrupt enable bits for the 7 sources */ + +#define SPI_INT_EN 0x0000007F +#define SPI_INT_EN_M ((SPI_INT_EN_V)<<(SPI_INT_EN_S)) +#define SPI_INT_EN_V 0x7F +#define SPI_INT_EN_S 5 + /* SPI_TRANS_DONE : R/W; bitpos: [4]; default: 0; * The interrupt raw bit for the completion of any operation in both the * master mode and the slave mode. Can not be changed by CONF_buf.