arch/xtensa/esp32s2: Fix SPI DMA implementation
This commit is contained in:
parent
b2ff151282
commit
4bcf454ae5
@ -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_dma_init(dma_rx_desc, SPI_DMA_DESC_NUM, rp, bytes);
|
||||
|
||||
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_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 */
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user