Skip to content

Commit

Permalink
arch/imx9_lpspi: Use DMA safe buffers to do the DMA transfers
Browse files Browse the repository at this point in the history
Using user allocated buffers for DMA transfers is not safe for two reasons:
- User space memory is virtual memory, DMA needs physical memory
- User memory buffer alignment cannot be guaranteed -> cache line ops
  are not safe
  • Loading branch information
pussuw committed Apr 25, 2024
1 parent 17c7771 commit 841378c
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 9 deletions.
7 changes: 7 additions & 0 deletions arch/arm64/src/imx9/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,13 @@ config IMX9_LPSPI_DMATHRESHOLD
by polling logic. But we need a threshold value to determine what
is small.

config IMX9_LPSPI_DMA_BUFFER_SIZE
int "LPSPI DMA buffer size"
default 4096
depends on IMX9_LPSPI_DMA
---help---
Set the LPSPI driver DMA buffer size.

config IMX9_LPSPI1_DMA
bool "LPSPI1 DMA"
default n
Expand Down
54 changes: 45 additions & 9 deletions arch/arm64/src/imx9/imx9_lpspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "arm64_internal.h"
#include "imx9_ccm.h"
#include "imx9_clockconfig.h"
#include "imx9_dma_alloc.h"
#include "imx9_gpio.h"
#include "imx9_iomuxc.h"
#include "imx9_lpspi.h"
Expand Down Expand Up @@ -131,6 +132,8 @@ struct imx9_lpspidev_s
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 */
void *txbuf; /* Driver DMA safe buffer for TX */
void *rxbuf; /* Driver DMA safe buffer for RX */
#endif
};

Expand Down Expand Up @@ -1305,13 +1308,13 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,
const void *txbuffer,
void *rxbuffer, size_t nwords)
{
struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev;
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;
struct imx9_lpspidev_s *priv = (struct imx9_lpspidev_s *)dev;

DEBUGASSERT(priv != NULL);
DEBUGASSERT(priv && priv->spibase);
Expand All @@ -1338,6 +1341,17 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,
return;
}

/* Check if the transfer is too long */

if (nbytes > CONFIG_IMX9_LPSPI_DMA_BUFFER_SIZE)
{
/* Transfer is too long, revert to slow non-DMA method */

spiwarn("frame %lu too long, fall back to no DMA transfer\n", nbytes);
imx9_lpspi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
return;
}

/* Disable SPI when we are configuring it */

imx9_lpspi_modifyreg32(priv, IMX9_LPSPI_CR_OFFSET, LPSPI_CR_MEN, 0);
Expand All @@ -1362,13 +1376,22 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,

if (txbuffer)
{
up_clean_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + nbytes);
/* Move the user data to device internal buffer */

memcpy(priv->txbuf, txbuffer, nbytes);

/* And flush it to RAM */

up_clean_dcache((uintptr_t)priv->txbuf,
(uintptr_t)priv->txbuf + nbytes);
}

if (rxbuffer)
{
up_invalidate_dcache((uintptr_t)rxbuffer,
(uintptr_t)rxbuffer + nbytes);
/* Prepare the RX buffer for DMA */

up_invalidate_dcache((uintptr_t)priv->rxbuf,
(uintptr_t)priv->rxbuf + nbytes);
}

/* Set up the DMA */
Expand All @@ -1378,7 +1401,7 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,
struct imx9_edma_xfrconfig_s config;

config.saddr = priv->spibase + IMX9_LPSPI_RDR_OFFSET;
config.daddr = (uintptr_t) (rxbuffer ? rxbuffer : rxdummy);
config.daddr = (uintptr_t) (rxbuffer ? priv->rxbuf : rxdummy);
config.soff = 0;
config.doff = rxbuffer ? adjust : 0;
config.iter = nbytes;
Expand All @@ -1391,7 +1414,7 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,
#endif
imx9_dmach_xfrsetup(priv->rxdma, &config);

config.saddr = (uintptr_t) (txbuffer ? txbuffer : &txdummy);
config.saddr = (uintptr_t) (txbuffer ? priv->txbuf : &txdummy);
config.daddr = priv->spibase + IMX9_LPSPI_TDR_OFFSET;
config.soff = txbuffer ? adjust : 0;
config.doff = 0;
Expand Down Expand Up @@ -1436,10 +1459,16 @@ static void imx9_lpspi_exchange(struct spi_dev_s *dev,

imx9_lpspi_putreg32(priv, IMX9_LPSPI_DER_OFFSET, 0);

if (rxbuffer)
if (rxbuffer && ret >= 0)
{
up_invalidate_dcache((uintptr_t)rxbuffer,
(uintptr_t)rxbuffer + nbytes);
/* Flush the RX data to ram */

up_invalidate_dcache((uintptr_t)priv->rxbuf,
(uintptr_t)priv->rxbuf + nbytes);

/* Copy data to user buffer */

memcpy(rxbuffer, priv->rxbuf, nbytes);
}
}

Expand Down Expand Up @@ -2044,6 +2073,13 @@ struct spi_dev_s *imx9_lpspibus_initialize(int bus)
priv->rxdma = imx9_dmach_alloc(priv->rxch, 0);
DEBUGASSERT(priv->rxdma && priv->txdma);
}

if (priv->txbuf == NULL && priv->rxbuf == NULL)
{
priv->txbuf = imx9_dma_alloc(CONFIG_IMX9_LPSPI_DMA_BUFFER_SIZE);
priv->rxbuf = imx9_dma_alloc(CONFIG_IMX9_LPSPI_DMA_BUFFER_SIZE);
DEBUGASSERT(priv->txbuf && priv->rxbuf);
}
}
else
{
Expand Down

0 comments on commit 841378c

Please sign in to comment.