imxrt:SPI add DMA support
This commit is contained in:
parent
fa58381e58
commit
55aaba53fc
@ -1809,6 +1809,55 @@ config IMXRT_EDMA_EDBG
|
|||||||
|
|
||||||
endmenu # eDMA Global Configuration
|
endmenu # eDMA Global Configuration
|
||||||
|
|
||||||
|
menu "LPSPI Configuration"
|
||||||
|
depends on IMXRT_LPSPI
|
||||||
|
|
||||||
|
config IMXRT_LPSPI_DMA
|
||||||
|
bool "LPSPI DMA"
|
||||||
|
depends on IMXRT_EDMA
|
||||||
|
default n
|
||||||
|
---help---
|
||||||
|
Use DMA to improve LPSPI transfer performance.
|
||||||
|
|
||||||
|
config IMXRT_LPSPI_DMATHRESHOLD
|
||||||
|
int "LPSPI DMA threshold"
|
||||||
|
default 4
|
||||||
|
depends on IMXRT_LPSPI_DMA
|
||||||
|
---help---
|
||||||
|
When SPI DMA is enabled, small DMA transfers will still be performed
|
||||||
|
by polling logic. But we need a threshold value to determine what
|
||||||
|
is small.
|
||||||
|
|
||||||
|
config IMXRT_LPSPI1_DMA
|
||||||
|
bool "LPSPI1 DMA"
|
||||||
|
default n
|
||||||
|
depends on IMXRT_LPSPI1 && IMXRT_LPSPI_DMA
|
||||||
|
---help---
|
||||||
|
Use DMA to improve LPSPI1 transfer performance.
|
||||||
|
|
||||||
|
config IMXRT_LPSPI2_DMA
|
||||||
|
bool "LPSPI2 DMA"
|
||||||
|
default n
|
||||||
|
depends on IMXRT_LPSPI2 && IMXRT_LPSPI_DMA
|
||||||
|
---help---
|
||||||
|
Use DMA to improve LPSPI2 transfer performance.
|
||||||
|
|
||||||
|
config IMXRT_LPSPI3_DMA
|
||||||
|
bool "LPSPI3 DMA"
|
||||||
|
default n
|
||||||
|
depends on IMXRT_LPSPI3 && IMXRT_LPSPI_DMA
|
||||||
|
---help---
|
||||||
|
Use DMA to improve LPSPI3 transfer performance.
|
||||||
|
|
||||||
|
config IMXRT_LPSPI4_DMA
|
||||||
|
bool "LPSPI4 DMA"
|
||||||
|
default n
|
||||||
|
depends on IMXRT_LPSPI4 && IMXRT_LPSPI_DMA
|
||||||
|
---help---
|
||||||
|
Use DMA to improve SPI4 transfer performance.
|
||||||
|
|
||||||
|
endmenu # LPSPI Configuration
|
||||||
|
|
||||||
if PM
|
if PM
|
||||||
|
|
||||||
config IMXRT_PM_SERIAL_ACTIVITY
|
config IMXRT_PM_SERIAL_ACTIVITY
|
||||||
|
@ -74,6 +74,9 @@
|
|||||||
#include "hardware/imxrt_ccm.h"
|
#include "hardware/imxrt_ccm.h"
|
||||||
#include "imxrt_periphclks.h"
|
#include "imxrt_periphclks.h"
|
||||||
|
|
||||||
|
#include "hardware/imxrt_dmamux.h"
|
||||||
|
#include "imxrt_edma.h"
|
||||||
|
|
||||||
#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \
|
#if defined(CONFIG_IMXRT_LPSPI1) || defined(CONFIG_IMXRT_LPSPI2) || \
|
||||||
defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4)
|
defined(CONFIG_IMXRT_LPSPI3) || defined(CONFIG_IMXRT_LPSPI4)
|
||||||
|
|
||||||
@ -89,16 +92,15 @@
|
|||||||
# error "Interrupt driven SPI not yet supported"
|
# error "Interrupt driven SPI not yet supported"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_IMXRT_LPSPI_DMA)
|
|
||||||
# error "DMA mode is not yet supported"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Can't have both interrupt driven SPI and SPI DMA */
|
/* Can't have both interrupt driven SPI and SPI DMA */
|
||||||
|
|
||||||
#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA)
|
#if defined(CONFIG_IMXRT_LPSPI_INTERRUPTS) && defined(CONFIG_IMXRT_LPSPI_DMA)
|
||||||
# error "Cannot enable both interrupt mode and DMA mode for SPI"
|
# error "Cannot enable both interrupt mode and DMA mode for SPI"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SPI_SR_CLEAR (LPSPI_SR_WCF | LPSPI_SR_FCF | LPSPI_SR_TCF | \
|
||||||
|
LPSPI_SR_TEF | LPSPI_SR_REF | LPSPI_SR_DMF)
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Private Types
|
* Private Types
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -115,6 +117,16 @@ struct imxrt_lpspidev_s
|
|||||||
uint32_t actual; /* Actual clock frequency */
|
uint32_t actual; /* Actual clock frequency */
|
||||||
int8_t nbits; /* Width of word in bits */
|
int8_t nbits; /* Width of word in bits */
|
||||||
uint8_t mode; /* Mode 0,1,2,3 */
|
uint8_t mode; /* Mode 0,1,2,3 */
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
volatile uint32_t rxresult; /* Result of the RX DMA */
|
||||||
|
volatile uint32_t txresult; /* Result of the TX DMA */
|
||||||
|
const uint16_t rxch; /* The RX DMA channel number */
|
||||||
|
const uint16_t txch; /* The TX DMA channel number */
|
||||||
|
DMACH_HANDLE rxdma; /* DMA channel handle for RX transfers */
|
||||||
|
DMACH_HANDLE txdma; /* DMA channel handle for TX transfers */
|
||||||
|
sem_t rxsem; /* Wait for RX DMA to complete */
|
||||||
|
sem_t txsem; /* Wait for TX DMA to complete */
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
enum imxrt_delay_e
|
enum imxrt_delay_e
|
||||||
@ -149,6 +161,21 @@ static inline void imxrt_lpspi_master_set_delay_scaler(
|
|||||||
uint32_t scaler,
|
uint32_t scaler,
|
||||||
enum imxrt_delay_e type);
|
enum imxrt_delay_e type);
|
||||||
|
|
||||||
|
/* DMA support */
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static int spi_dmarxwait(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
static int spi_dmatxwait(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
static inline void spi_dmarxwakeup(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
static inline void spi_dmatxwakeup(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg,
|
||||||
|
bool done, int result);
|
||||||
|
static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg,
|
||||||
|
bool done, int result);
|
||||||
|
static inline void spi_dmarxstart(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
static inline void spi_dmatxstart(FAR struct imxrt_lpspidev_s *priv);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* SPI methods */
|
/* SPI methods */
|
||||||
|
|
||||||
static int imxrt_lpspi_lock(struct spi_dev_s *dev, bool lock);
|
static int imxrt_lpspi_lock(struct spi_dev_s *dev, bool lock);
|
||||||
@ -221,9 +248,9 @@ static struct imxrt_lpspidev_s g_lpspi1dev =
|
|||||||
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
||||||
.spiirq = IMXRT_IRQ_LPSPI1,
|
.spiirq = IMXRT_IRQ_LPSPI1,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
#ifdef CONFIG_IMXRT_LPSPI1_DMA
|
||||||
.rxch = DMAMAP_LPSPI1_RX,
|
.rxch = IMXRT_DMACHAN_LPSPI1_RX,
|
||||||
.txch = DMAMAP_LPSPI1_TX,
|
.txch = IMXRT_DMACHAN_LPSPI1_TX,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -267,9 +294,9 @@ static struct imxrt_lpspidev_s g_lpspi2dev =
|
|||||||
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
||||||
.spiirq = IMXRT_IRQ_LPSPI2,
|
.spiirq = IMXRT_IRQ_LPSPI2,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
#ifdef CONFIG_IMXRT_LPSPI2_DMA
|
||||||
.rxch = DMAMAP_LPSPI2_RX,
|
.rxch = IMXRT_DMACHAN_LPSPI2_RX,
|
||||||
.txch = DMAMAP_LPSPI2_TX,
|
.txch = IMXRT_DMACHAN_LPSPI2_TX,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -313,9 +340,9 @@ static struct imxrt_lpspidev_s g_lpspi3dev =
|
|||||||
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
||||||
.spiirq = IMXRT_IRQ_LPSPI3,
|
.spiirq = IMXRT_IRQ_LPSPI3,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
#ifdef CONFIG_IMXRT_LPSPI3_DMA
|
||||||
.rxch = DMAMAP_LPSPI3_RX,
|
.rxch = IMXRT_DMACHAN_LPSPI3_RX,
|
||||||
.txch = DMAMAP_LPSPI3_TX,
|
.txch = IMXRT_DMACHAN_LPSPI3_TX,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -359,9 +386,9 @@ static struct imxrt_lpspidev_s g_lpspi4dev =
|
|||||||
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
#ifdef CONFIG_IMXRT_LPSPI_INTERRUPTS
|
||||||
.spiirq = IMXRT_IRQ_LPSPI4,
|
.spiirq = IMXRT_IRQ_LPSPI4,
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
#ifdef CONFIG_IMXRT_LPSPI4_DMA
|
||||||
.rxch = DMAMAP_LPSPI4_RX,
|
.rxch = IMXRT_DMACHAN_LPSPI4_RX,
|
||||||
.txch = DMAMAP_LPSPI4_TX,
|
.txch = IMXRT_DMACHAN_LPSPI4_TX,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
@ -596,7 +623,7 @@ imxrt_lpspi_9to16bitmode(struct imxrt_lpspidev_s *priv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: imxrt_lpspi_modifyreg
|
* Name: imxrt_lpspi_modifyreg32
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* Clear and set bits in register
|
* Clear and set bits in register
|
||||||
@ -1269,7 +1296,6 @@ static uint32_t imxrt_lpspi_send(struct spi_dev_s *dev, uint32_t wd)
|
|||||||
*
|
*
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
#if !defined(CONFIG_IMXRT_LPSPI_DMA) || defined(CONFIG_IMXRT_DMACAPABLE)
|
|
||||||
#if !defined(CONFIG_IMXRT_LPSPI_DMA)
|
#if !defined(CONFIG_IMXRT_LPSPI_DMA)
|
||||||
static void imxrt_lpspi_exchange(struct spi_dev_s *dev,
|
static void imxrt_lpspi_exchange(struct spi_dev_s *dev,
|
||||||
const void *txbuffer,
|
const void *txbuffer,
|
||||||
@ -1355,7 +1381,159 @@ static void imxrt_lpspi_exchange_nodma(struct spi_dev_s *dev,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif /* !CONFIG_IMXRT_LPSPI_DMA || CONFIG_IMXRT_DMACAPABLE */
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_exchange (with DMA capability)
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Exchange a block of data on SPI using DMA
|
||||||
|
*
|
||||||
|
* Input Parameters:
|
||||||
|
* dev - Device-specific state data
|
||||||
|
* txbuffer - A pointer to the buffer of data to be sent
|
||||||
|
* rxbuffer - A pointer to a buffer in which to receive data
|
||||||
|
* nwords - the length of data 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_IMXRT_LPSPI_DMA
|
||||||
|
static void imxrt_lpspi_exchange(FAR struct spi_dev_s * dev,
|
||||||
|
FAR const void * txbuffer,
|
||||||
|
FAR void * rxbuffer, size_t nwords)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
size_t adjust;
|
||||||
|
ssize_t nbytes;
|
||||||
|
static uint8_t rxdummy[4] aligned_data(4);
|
||||||
|
static const uint16_t txdummy = 0xffff;
|
||||||
|
uint32_t regval;
|
||||||
|
FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)dev;
|
||||||
|
|
||||||
|
DEBUGASSERT(priv != NULL);
|
||||||
|
DEBUGASSERT(priv && priv->spibase);
|
||||||
|
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n", txbuffer, rxbuffer, nwords);
|
||||||
|
|
||||||
|
/* Convert the number of word to a number of bytes */
|
||||||
|
|
||||||
|
nbytes = (priv->nbits > 8) ? nwords << 2 : nwords;
|
||||||
|
|
||||||
|
/* Invalid DMA channels fall back to non-DMA method. */
|
||||||
|
|
||||||
|
if (priv->rxdma == NULL || priv->txdma == NULL
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMATHRESHOLD
|
||||||
|
/* If this is a small SPI transfer, then let
|
||||||
|
* imxrt_lpspi_exchange_nodma() do the work.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|| nbytes <= CONFIG_IMXRT_LPSPI_DMATHRESHOLD
|
||||||
|
#endif
|
||||||
|
)
|
||||||
|
{
|
||||||
|
imxrt_lpspi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ERR050456 workaround: Reset FIFOs using CR[RST] bit */
|
||||||
|
|
||||||
|
regval = imxrt_lpspi_getreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET);
|
||||||
|
|
||||||
|
imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET,
|
||||||
|
LPSPI_CR_RTF | LPSPI_CR_RRF,
|
||||||
|
LPSPI_CR_RTF | LPSPI_CR_RRF);
|
||||||
|
|
||||||
|
imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_CFGR1_OFFSET, regval);
|
||||||
|
|
||||||
|
/* Clear all status bits */
|
||||||
|
|
||||||
|
imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_SR_OFFSET, SPI_SR_CLEAR);
|
||||||
|
|
||||||
|
/* disable DMA */
|
||||||
|
|
||||||
|
imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_DER_OFFSET, 0);
|
||||||
|
|
||||||
|
if (txbuffer)
|
||||||
|
{
|
||||||
|
up_clean_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rxbuffer)
|
||||||
|
{
|
||||||
|
up_invalidate_dcache((uintptr_t)rxbuffer,
|
||||||
|
(uintptr_t)rxbuffer + nbytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up the DMA */
|
||||||
|
|
||||||
|
adjust = (priv->nbits > 8) ? 2 : 1;
|
||||||
|
|
||||||
|
struct imxrt_edma_xfrconfig_s config;
|
||||||
|
|
||||||
|
config.saddr = priv->spibase + IMXRT_LPSPI_RDR_OFFSET;
|
||||||
|
config.daddr = (uint32_t) (rxbuffer ? rxbuffer : rxdummy);
|
||||||
|
config.soff = 0;
|
||||||
|
config.doff = rxbuffer ? adjust : 0;
|
||||||
|
config.iter = nbytes;
|
||||||
|
config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE;
|
||||||
|
config.ssize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
|
||||||
|
config.dsize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
|
||||||
|
config.nbytes = adjust;
|
||||||
|
#ifdef CONFIG_KINETIS_EDMA_ELINK
|
||||||
|
config.linkch = NULL;
|
||||||
|
#endif
|
||||||
|
imxrt_dmach_xfrsetup(priv->rxdma, &config);
|
||||||
|
|
||||||
|
config.saddr = (uint32_t) (txbuffer ? txbuffer : &txdummy);
|
||||||
|
config.daddr = priv->spibase + IMXRT_LPSPI_TDR_OFFSET;
|
||||||
|
config.soff = txbuffer ? adjust : 0;
|
||||||
|
config.doff = 0;
|
||||||
|
config.iter = nbytes;
|
||||||
|
config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE;
|
||||||
|
config.ssize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
|
||||||
|
config.dsize = adjust == 1 ? EDMA_8BIT : EDMA_16BIT;
|
||||||
|
config.nbytes = adjust;
|
||||||
|
#ifdef CONFIG_KINETIS_EDMA_ELINK
|
||||||
|
config.linkch = NULL;
|
||||||
|
#endif
|
||||||
|
imxrt_dmach_xfrsetup(priv->txdma, &config);
|
||||||
|
|
||||||
|
/* Start the DMAs */
|
||||||
|
|
||||||
|
spi_dmarxstart(priv);
|
||||||
|
spi_dmatxstart(priv);
|
||||||
|
|
||||||
|
/* Invoke SPI DMA */
|
||||||
|
|
||||||
|
imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_DER_OFFSET,
|
||||||
|
0, LPSPI_DER_TDDE | LPSPI_DER_RDDE);
|
||||||
|
|
||||||
|
/* Then wait for each to complete */
|
||||||
|
|
||||||
|
ret = spi_dmarxwait(priv);
|
||||||
|
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
ret = spi_dmatxwait(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset any status */
|
||||||
|
|
||||||
|
imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_SR_OFFSET,
|
||||||
|
imxrt_lpspi_getreg32(priv,
|
||||||
|
IMXRT_LPSPI_SR_OFFSET));
|
||||||
|
|
||||||
|
/* Disable DMA */
|
||||||
|
|
||||||
|
imxrt_lpspi_putreg32(priv, IMXRT_LPSPI_DER_OFFSET, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_IMXRT_SPI_DMA */
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: imxrt_lpspi_sndblock
|
* Name: imxrt_lpspi_sndblock
|
||||||
@ -1542,6 +1720,174 @@ static void imxrt_lpspi_bus_initialize(struct imxrt_lpspidev_s *priv)
|
|||||||
imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN);
|
imxrt_lpspi_modifyreg32(priv, IMXRT_LPSPI_CR_OFFSET, 0, LPSPI_CR_MEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmarxwait
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Wait for DMA to complete.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static int spi_dmarxwait(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Take the semaphore (perhaps waiting). If the result is zero, then the
|
||||||
|
* DMA must not really have completed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = nxsem_wait_uninterruptible(&priv->rxsem);
|
||||||
|
|
||||||
|
/* The only expected error is ECANCELED which would occur if the
|
||||||
|
* calling thread were canceled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(ret == OK || ret == -ECANCELED);
|
||||||
|
}
|
||||||
|
while (priv->rxresult == 0 && ret == OK);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmatxwait
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Wait for DMA to complete.
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static int spi_dmatxwait(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Take the semaphore (perhaps waiting). If the result is zero, then the
|
||||||
|
* DMA must not really have completed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
ret = nxsem_wait_uninterruptible(&priv->txsem);
|
||||||
|
|
||||||
|
/* The only expected error is ECANCELED which would occur if the
|
||||||
|
* calling thread were canceled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
DEBUGASSERT(ret == OK || ret == -ECANCELED);
|
||||||
|
}
|
||||||
|
while (priv->txresult == 0 && ret == OK);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmarxwakeup
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Signal that DMA is complete
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static inline void spi_dmarxwakeup(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
nxsem_post(&priv->rxsem);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmatxwakeup
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Signal that DMA is complete
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static inline void spi_dmatxwakeup(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
nxsem_post(&priv->txsem);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmarxcallback
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Called when the RX DMA completes
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static void spi_dmarxcallback(DMACH_HANDLE handle, void *arg, bool done,
|
||||||
|
int result)
|
||||||
|
{
|
||||||
|
FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)arg;
|
||||||
|
|
||||||
|
priv->rxresult = result | 0x80000000; /* assure non-zero */
|
||||||
|
spi_dmarxwakeup(priv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmatxcallback
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Called when the RX DMA completes
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static void spi_dmatxcallback(DMACH_HANDLE handle, void *arg, bool done,
|
||||||
|
int result)
|
||||||
|
{
|
||||||
|
FAR struct imxrt_lpspidev_s *priv = (FAR struct imxrt_lpspidev_s *)arg;
|
||||||
|
|
||||||
|
/* Wake-up the SPI driver */
|
||||||
|
|
||||||
|
priv->txresult = result | 0x80000000; /* assure non-zero */
|
||||||
|
spi_dmatxwakeup(priv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmarxstart
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Start RX DMA
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static inline void spi_dmarxstart(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
priv->rxresult = 0;
|
||||||
|
imxrt_dmach_start(priv->rxdma, spi_dmarxcallback, priv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: spi_dmatxstart
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Start TX DMA
|
||||||
|
*
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
static inline void spi_dmatxstart(FAR struct imxrt_lpspidev_s *priv)
|
||||||
|
{
|
||||||
|
priv->txresult = 0;
|
||||||
|
imxrt_dmach_start(priv->txdma, spi_dmatxcallback, priv);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Public Functions
|
* Public Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -1694,9 +2040,39 @@ struct spi_dev_s *imxrt_lpspibus_initialize(int bus)
|
|||||||
spierr("ERROR: Unsupported SPI bus: %d\n", bus);
|
spierr("ERROR: Unsupported SPI bus: %d\n", bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IMXRT_LPSPI_DMA
|
||||||
|
/* Initialize the SPI semaphores that is used to wait for DMA completion.
|
||||||
|
* This semaphore is used for signaling and, hence, should not have
|
||||||
|
* priority inheritance enabled.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (priv->rxch && priv->txch)
|
||||||
|
{
|
||||||
|
if (priv->txdma == NULL && priv->rxdma == NULL)
|
||||||
|
{
|
||||||
|
nxsem_init(&priv->rxsem, 0, 0);
|
||||||
|
nxsem_init(&priv->txsem, 0, 0);
|
||||||
|
|
||||||
|
nxsem_set_protocol(&priv->rxsem, SEM_PRIO_NONE);
|
||||||
|
nxsem_set_protocol(&priv->txsem, SEM_PRIO_NONE);
|
||||||
|
|
||||||
|
priv->txdma = imxrt_dmach_alloc(priv->txch | DMAMUX_CHCFG_ENBL,
|
||||||
|
0);
|
||||||
|
priv->rxdma = imxrt_dmach_alloc(priv->rxch | DMAMUX_CHCFG_ENBL,
|
||||||
|
0);
|
||||||
|
DEBUGASSERT(priv->rxdma && priv->txdma);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
priv->rxdma = NULL;
|
||||||
|
priv->txdma = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
leave_critical_section(flags);
|
leave_critical_section(flags);
|
||||||
|
|
||||||
return (struct spi_dev_s *)priv;
|
return (struct spi_dev_s *)priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* CONFIG_IMXRT_LPSPI1 */
|
#endif /* CONFIG_IMXRT_LPSPI1 || CONFIG_IMXRT_LPSPI2 || CONFIG_IMXRT_LPSPI3 || CONFIG_IMXRT_LPSPI4 */
|
||||||
|
Loading…
Reference in New Issue
Block a user