esp32[c3|c6|h2]: Add SPI master DMA support
This commit is contained in:
parent
871cd306c5
commit
f5b63cea18
@ -907,6 +907,29 @@ config ESPRESSIF_SPI_UDCS
|
||||
|
||||
if ESPRESSIF_SPI2
|
||||
|
||||
config ESPRESSIF_SPI2_DMA
|
||||
bool "SPI2 use GDMA"
|
||||
default n
|
||||
depends on ESPRESSIF_DMA
|
||||
---help---
|
||||
Enable support for transfers using the GDMA engine.
|
||||
|
||||
config ESPRESSIF_SPI2_DMADESC_NUM
|
||||
int "SPI2 Master GDMA maximum number of descriptors"
|
||||
default 2
|
||||
depends on ESPRESSIF_SPI2_DMA
|
||||
---help---
|
||||
Configure the maximum number of out-link/in-link descriptors to
|
||||
be chained for a GDMA transfer.
|
||||
|
||||
config ESPRESSIF_SPI2_DMATHRESHOLD
|
||||
int "SPI2 GDMA threshold"
|
||||
default 64
|
||||
depends on ESPRESSIF_SPI2_DMA
|
||||
---help---
|
||||
When SPI GDMA is enabled, GDMA transfers whose size are below the
|
||||
defined threshold will be performed by polling logic.
|
||||
|
||||
config ESPRESSIF_SPI2_SLAVE
|
||||
bool "SPI2 Slave mode"
|
||||
default n
|
||||
|
@ -48,6 +48,10 @@
|
||||
#include "esp_irq.h"
|
||||
#include "esp_gpio.h"
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
#include "esp_dma.h"
|
||||
#endif
|
||||
|
||||
#include "riscv_internal.h"
|
||||
|
||||
#include "hal/spi_hal.h"
|
||||
@ -69,6 +73,11 @@
|
||||
# define MISO_PIN_ATTR (INPUT_FUNCTION_2 | PULLUP)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_CHIP_ESP32C6) || defined(CONFIG_ARCH_CHIP_ESP32H2)
|
||||
# define SPI2_INTR_SOURCE GSPI2_INTR_SOURCE
|
||||
# define ESP_IRQ_SPI2 ESP_IRQ_GSPI2
|
||||
#endif
|
||||
|
||||
#define SPI2_IOMUX_MISOPIN 2
|
||||
#define SPI2_IOMUX_MOSIPIN 7
|
||||
#define SPI2_IOMUX_CLKPIN 6
|
||||
@ -82,6 +91,14 @@
|
||||
# define SPI_HAVE_SWCS FALSE
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
|
||||
/* SPI DMA RX/TX number of descriptors */
|
||||
|
||||
#define SPI_DMA_DESC_NUM (CONFIG_ESPRESSIF_SPI2_DMADESC_NUM)
|
||||
|
||||
#endif
|
||||
|
||||
/* Verify whether SPI has been assigned IOMUX pins.
|
||||
* Otherwise, SPI signals will be routed via GPIO Matrix.
|
||||
*/
|
||||
@ -116,19 +133,23 @@
|
||||
|
||||
struct esp_spi_config_s
|
||||
{
|
||||
uint32_t width; /* SPI default width */
|
||||
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 */
|
||||
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 */
|
||||
uint32_t width; /* SPI default width */
|
||||
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 */
|
||||
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 */
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
uint8_t periph; /* Peripheral ID */
|
||||
uint8_t irq; /* Interrupt ID */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct esp_spi_priv_s
|
||||
@ -142,6 +163,11 @@ struct esp_spi_priv_s
|
||||
const struct esp_spi_config_s *config;
|
||||
int refs; /* Reference count */
|
||||
mutex_t lock; /* Held while chip is selected for mutual exclusion */
|
||||
#ifdef CONFIG_ESPRESSIF_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
|
||||
uint8_t nbits; /* Actual SPI send/receive bits once transmission */
|
||||
uint8_t id; /* ID number of SPI interface */
|
||||
uint8_t module; /* Module ID of SPI interface */
|
||||
@ -173,10 +199,19 @@ static uint32_t esp_spi_send(struct spi_dev_s *dev, uint32_t wd);
|
||||
static void esp_spi_exchange(struct spi_dev_s *dev,
|
||||
const void *txbuffer,
|
||||
void *rxbuffer, size_t nwords);
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
static int esp_spi_interrupt(int irq, void *context, void *arg);
|
||||
static int esp_spi_sem_waitdone(struct esp_spi_priv_s *priv);
|
||||
static void esp_spi_dma_exchange(struct esp_spi_priv_s *priv,
|
||||
const void *txbuffer,
|
||||
void *rxbuffer,
|
||||
uint32_t nwords);
|
||||
#else
|
||||
static void esp_spi_poll_exchange(struct esp_spi_priv_s *priv,
|
||||
const void *txbuffer,
|
||||
void *rxbuffer,
|
||||
size_t nwords);
|
||||
#endif
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
static void esp_spi_sndblock(struct spi_dev_s *dev,
|
||||
const void *txbuffer,
|
||||
@ -188,6 +223,9 @@ static void esp_spi_recvblock(struct spi_dev_s *dev,
|
||||
#ifdef CONFIG_SPI_TRIGGER
|
||||
static int esp_spi_trigger(struct spi_dev_s *dev);
|
||||
#endif
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
static void esp_spi_dma_init(struct spi_dev_s *dev);
|
||||
#endif
|
||||
static void esp_spi_init(struct spi_dev_s *dev);
|
||||
static void esp_spi_deinit(struct spi_dev_s *dev);
|
||||
|
||||
@ -244,6 +282,10 @@ static const struct esp_spi_config_s esp_spi2_config =
|
||||
.mosi_pin = CONFIG_ESPRESSIF_SPI2_MOSIPIN,
|
||||
.miso_pin = CONFIG_ESPRESSIF_SPI2_MISOPIN,
|
||||
.clk_pin = CONFIG_ESPRESSIF_SPI2_CLKPIN,
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
.periph = SPI2_INTR_SOURCE,
|
||||
.irq = ESP_IRQ_SPI2,
|
||||
#endif
|
||||
.cs_insig = FSPICS0_IN_IDX,
|
||||
.cs_outsig = FSPICS0_OUT_IDX,
|
||||
.mosi_insig = FSPID_IN_IDX,
|
||||
@ -301,9 +343,23 @@ static struct esp_spi_priv_s esp_spi2_priv =
|
||||
.dev_cfg = &dev_cfg,
|
||||
.ctx = &ctx,
|
||||
.timing_param = &timing_param,
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
.sem_isr = SEM_INITIALIZER(0),
|
||||
.cpuint = -ENOMEM,
|
||||
.dma_channel = -1,
|
||||
#endif
|
||||
};
|
||||
#endif /* CONFIG_ESPRESSIF_SPI2 */
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
|
||||
/* SPI DMA RX/TX description */
|
||||
|
||||
static struct esp_dmadesc_s dma_rxdesc[SPI_DMA_DESC_NUM];
|
||||
static struct esp_dmadesc_s dma_txdesc[SPI_DMA_DESC_NUM];
|
||||
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
@ -340,6 +396,27 @@ static int esp_spi_lock(struct spi_dev_s *dev, bool lock)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spi_sem_waitdone
|
||||
*
|
||||
* Description:
|
||||
* Wait for a transfer to complete.
|
||||
*
|
||||
* Input Parameters:
|
||||
* priv - SPI private state data
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
static int esp_spi_sem_waitdone(struct esp_spi_priv_s *priv)
|
||||
{
|
||||
return nxsem_tickwait_uninterruptible(&priv->sem_isr, SEC2TICK(10));
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spi_select
|
||||
*
|
||||
@ -509,6 +586,114 @@ static int esp_spi_hwfeatures(struct spi_dev_s *dev,
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_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_ESPRESSIF_SPI2_DMA
|
||||
static void esp_spi_dma_exchange(struct esp_spi_priv_s *priv,
|
||||
const void *txbuffer,
|
||||
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 *)txbuffer;
|
||||
uint8_t *rp = (uint8_t *)rxbuffer;
|
||||
spi_hal_trans_config_t trans =
|
||||
{
|
||||
0
|
||||
};
|
||||
|
||||
if (tp == NULL)
|
||||
{
|
||||
tp = rp;
|
||||
}
|
||||
|
||||
spi_hal_setup_device(priv->ctx, priv->dev_cfg);
|
||||
spi_ll_clear_int_stat(priv->ctx->hw);
|
||||
|
||||
trans.cs_keep_active = priv->dev_cfg->cs_hold;
|
||||
trans.line_mode.data_lines = 2;
|
||||
trans.line_mode.addr_lines = 1;
|
||||
trans.line_mode.cmd_lines = 1;
|
||||
|
||||
while (bytes != 0)
|
||||
{
|
||||
/* Reset SPI DMA TX FIFO */
|
||||
|
||||
spi_ll_cpu_rx_fifo_reset(priv->ctx->hw);
|
||||
|
||||
/* Enable SPI DMA TX */
|
||||
|
||||
spi_ll_dma_tx_fifo_reset(priv->ctx->hw);
|
||||
spi_ll_dma_tx_enable(priv->ctx->hw, 1);
|
||||
|
||||
n = esp_dma_setup(channel, true, dma_txdesc,
|
||||
SPI_DMA_DESC_NUM, tp, bytes);
|
||||
esp_dma_enable(channel, true);
|
||||
|
||||
/* Write data words to data buffer registers.
|
||||
* SPI peripheral contains 16 registers (W0 - W15).
|
||||
*/
|
||||
|
||||
trans.tx_bitlen = n * 8;
|
||||
trans.rx_bitlen = n * 8;
|
||||
trans.rcv_buffer = (uint8_t *)rp;
|
||||
trans.send_buffer = (uint8_t *)tp;
|
||||
priv->ctx->trans_config = trans;
|
||||
|
||||
spi_ll_set_mosi_bitlen(priv->ctx->hw, (n * 8));
|
||||
spi_ll_enable_mosi(priv->ctx->hw, true);
|
||||
|
||||
tp += n;
|
||||
|
||||
if (rp != NULL)
|
||||
{
|
||||
/* Enable SPI DMA RX */
|
||||
|
||||
spi_ll_dma_rx_enable(priv->ctx->hw, 1);
|
||||
esp_dma_setup(channel, false, dma_rxdesc, SPI_DMA_DESC_NUM,
|
||||
rp, bytes);
|
||||
esp_dma_enable(channel, false);
|
||||
spi_ll_enable_miso(priv->ctx->hw, true);
|
||||
|
||||
rp += n;
|
||||
}
|
||||
else
|
||||
{
|
||||
spi_ll_enable_miso(priv->ctx->hw, false);
|
||||
}
|
||||
|
||||
spi_ll_master_user_start(priv->ctx->hw);
|
||||
|
||||
esp_spi_sem_waitdone(priv);
|
||||
bytes -= n;
|
||||
}
|
||||
|
||||
spi_ll_clear_int_stat(priv->ctx->hw);
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spi_poll_send
|
||||
*
|
||||
@ -681,7 +866,18 @@ static void esp_spi_exchange(struct spi_dev_s *dev,
|
||||
{
|
||||
struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)dev;
|
||||
|
||||
esp_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
size_t thld = CONFIG_ESPRESSIF_SPI2_DMATHRESHOLD;
|
||||
|
||||
if (nwords > thld)
|
||||
{
|
||||
esp_spi_dma_exchange(priv, txbuffer, rxbuffer, nwords);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
esp_spi_poll_exchange(priv, txbuffer, rxbuffer, nwords);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SPI_EXCHANGE
|
||||
@ -768,6 +964,41 @@ static int esp_spi_trigger(struct spi_dev_s *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spi_dma_init
|
||||
*
|
||||
* Description:
|
||||
* Initialize SPI connection to GDMA engine.
|
||||
*
|
||||
* Input Parameters:
|
||||
* dev - Device-specific state data
|
||||
*
|
||||
* Returned Value:
|
||||
* None.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
void esp_spi_dma_init(struct spi_dev_s *dev)
|
||||
{
|
||||
struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)dev;
|
||||
|
||||
/* Initialize GDMA controller */
|
||||
|
||||
esp_dma_init();
|
||||
|
||||
/* Request a GDMA channel for SPI peripheral */
|
||||
|
||||
priv->dma_channel = esp_dma_request(ESPRESSIF_DMA_PERIPH_M2M, 1, 1, true);
|
||||
if (priv->dma_channel < 0)
|
||||
{
|
||||
spierr("Failed to allocate GDMA channel\n");
|
||||
|
||||
DEBUGPANIC();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spi_init
|
||||
*
|
||||
@ -828,7 +1059,22 @@ static void esp_spi_init(struct spi_dev_s *dev)
|
||||
|
||||
periph_module_enable(priv->module);
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
esp_spi_dma_init(dev);
|
||||
|
||||
priv->ctx->hw = SPI_LL_GET_HW(priv->id);
|
||||
priv->cfg->dma_enabled = true;
|
||||
priv->cfg->dmadesc_rx = (lldesc_t *)dma_rxdesc;
|
||||
priv->cfg->dmadesc_tx = (lldesc_t *)dma_txdesc;
|
||||
priv->cfg->rx_dma_chan = priv->dma_channel;
|
||||
priv->cfg->tx_dma_chan = priv->dma_channel;
|
||||
|
||||
spi_ll_master_init(priv->ctx->hw);
|
||||
spi_ll_enable_int(priv->ctx->hw);
|
||||
spi_ll_set_mosi_delay(priv->ctx->hw, 0, 0);
|
||||
#else
|
||||
spi_hal_init(priv->ctx, priv->id, priv->cfg);
|
||||
#endif
|
||||
|
||||
priv->dev_cfg->timing_conf.clock_source = SPI_CLK_SRC_DEFAULT;
|
||||
|
||||
@ -863,6 +1109,34 @@ static void esp_spi_deinit(struct spi_dev_s *dev)
|
||||
priv->nbits = 0;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_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_ESPRESSIF_SPI2_DMA
|
||||
static int esp_spi_interrupt(int irq, void *context, void *arg)
|
||||
{
|
||||
struct esp_spi_priv_s *priv = (struct esp_spi_priv_s *)arg;
|
||||
|
||||
spi_ll_clear_intr(priv->ctx->hw, SPI_LL_INTR_TRANS_DONE);
|
||||
nxsem_post(&priv->sem_isr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: esp_spibus_initialize
|
||||
*
|
||||
@ -903,6 +1177,41 @@ struct spi_dev_s *esp_spibus_initialize(int port)
|
||||
return spi_dev;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
if (priv->cpuint != -ENOMEM)
|
||||
{
|
||||
/* Disable the provided CPU Interrupt to configure it. */
|
||||
|
||||
up_disable_irq(priv->config->irq);
|
||||
}
|
||||
|
||||
priv->cpuint = esp_setup_irq(priv->config->periph,
|
||||
ESP_IRQ_PRIORITY_DEFAULT,
|
||||
ESP_IRQ_TRIGGER_LEVEL);
|
||||
if (priv->cpuint < 0)
|
||||
{
|
||||
/* Failed to allocate a CPU interrupt of this type. */
|
||||
|
||||
nxmutex_unlock(&priv->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (irq_attach(priv->config->irq, esp_spi_interrupt, priv) != OK)
|
||||
{
|
||||
/* Failed to attach IRQ, so CPU interrupt must be freed. */
|
||||
|
||||
esp_teardown_irq(priv->config->periph, priv->cpuint);
|
||||
priv->cpuint = -ENOMEM;
|
||||
nxmutex_unlock(&priv->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Enable the CPU interrupt that is linked to the SPI device. */
|
||||
|
||||
up_enable_irq(priv->config->irq);
|
||||
#endif
|
||||
|
||||
esp_spi_init(spi_dev);
|
||||
priv->refs++;
|
||||
|
||||
@ -942,6 +1251,12 @@ int esp_spibus_uninitialize(struct spi_dev_s *dev)
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESPRESSIF_SPI2_DMA
|
||||
up_disable_irq(priv->config->irq);
|
||||
esp_teardown_irq(priv->config->periph, priv->cpuint);
|
||||
priv->cpuint = -ENOMEM;
|
||||
#endif
|
||||
|
||||
esp_spi_deinit(dev);
|
||||
nxmutex_unlock(&priv->lock);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user