stm32h7:stm32_spi Restores internal DMA buffer broken in 574b25
574b25 broke the internal DMA buffers usage that solved the following problem: The DMA capable interface does not know the buffers extent. It calulates it from size. The user may need to transfer less than a cachline bytes, but STILL have a DMA cabable buffer. The user is therefore foreced to transfer more data then needed to "trick" the DMA cabable function. This is a wast of bus bandwith and may not work will all devices. The internal buffer, solve this issue. stm32h7:stm32_spi review changes Added sugestion from jlaitine to support RX only.
This commit is contained in:
parent
f47151f8a7
commit
1b8c072802
@ -515,7 +515,7 @@ static const struct spi_ops_s g_sp3iops =
|
|||||||
#ifdef CONFIG_SPI_CALLBACK
|
#ifdef CONFIG_SPI_CALLBACK
|
||||||
.registercallback = stm32_spi3register, /* provided externally */
|
.registercallback = stm32_spi3register, /* provided externally */
|
||||||
#else
|
#else
|
||||||
.registercallback = 0, /* not implemented */
|
.registercallback = 0, /* not implemented */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -583,7 +583,7 @@ static const struct spi_ops_s g_sp4iops =
|
|||||||
#ifdef CONFIG_SPI_CALLBACK
|
#ifdef CONFIG_SPI_CALLBACK
|
||||||
.registercallback = stm32_spi4register, /* provided externally */
|
.registercallback = stm32_spi4register, /* provided externally */
|
||||||
#else
|
#else
|
||||||
.registercallback = 0, /* not implemented */
|
.registercallback = 0, /* not implemented */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -651,7 +651,7 @@ static const struct spi_ops_s g_sp5iops =
|
|||||||
#ifdef CONFIG_SPI_CALLBACK
|
#ifdef CONFIG_SPI_CALLBACK
|
||||||
.registercallback = stm32_spi5register, /* provided externally */
|
.registercallback = stm32_spi5register, /* provided externally */
|
||||||
#else
|
#else
|
||||||
.registercallback = 0, /* not implemented */
|
.registercallback = 0, /* not implemented */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -719,7 +719,7 @@ static const struct spi_ops_s g_sp6iops =
|
|||||||
#ifdef CONFIG_SPI_CALLBACK
|
#ifdef CONFIG_SPI_CALLBACK
|
||||||
.registercallback = stm32_spi6register, /* provided externally */
|
.registercallback = stm32_spi6register, /* provided externally */
|
||||||
#else
|
#else
|
||||||
.registercallback = 0, /* not implemented */
|
.registercallback = 0, /* not implemented */
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1924,6 +1924,7 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
static uint8_t rxdummy[ARMV7M_DCACHE_LINESIZE]
|
static uint8_t rxdummy[ARMV7M_DCACHE_LINESIZE]
|
||||||
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
|
__attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
|
||||||
static const uint16_t txdummy = 0xffff;
|
static const uint16_t txdummy = 0xffff;
|
||||||
|
FAR void * orig_rxbuffer = rxbuffer;
|
||||||
|
|
||||||
DEBUGASSERT(priv != NULL);
|
DEBUGASSERT(priv != NULL);
|
||||||
|
|
||||||
@ -1955,39 +1956,60 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup the DMA configs */
|
/* If this bus uses the driver's DMA aligned buffers (priv->txbuf != NULL)
|
||||||
|
* we will incur 2 copies. However the copy cost is much less the non
|
||||||
|
* DMA transfer time. Having the buffer in the driver ensures DMA can be
|
||||||
|
* used. This is needed because the stm32_dmacapable API does not support
|
||||||
|
* passing the buffer extent. So the only extent calculated is buffer
|
||||||
|
* plus the transfer size. The sizes can be less than a cache line size,
|
||||||
|
* and not aligned and are typically greater then 4 bytes, which is
|
||||||
|
* about the break even point for the DMA IO overhead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Does bus support in driver DMA buffering? */
|
||||||
|
|
||||||
|
if (priv->txbuf)
|
||||||
|
{
|
||||||
|
if (nbytes > priv->buflen)
|
||||||
|
{
|
||||||
|
/* Buffer is too big for internal DMA buffer so fall back. */
|
||||||
|
|
||||||
|
spi_exchange_nodma(dev, txbuffer, rxbuffer, nwords);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* First copy: If provided, copy caller's buffer to the internal DMA
|
||||||
|
* txbuf
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (txbuffer)
|
||||||
|
{
|
||||||
|
memcpy(priv->txbuf, txbuffer, nbytes);
|
||||||
|
|
||||||
|
/* Adjust pointers to internal DMA buffers */
|
||||||
|
|
||||||
|
txbuffer = priv->txbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* orig_rxbuffer holds the callers return buffer */
|
||||||
|
|
||||||
|
rxbuffer = rxbuffer ? priv->rxbuf : rxbuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Setup the DMA configs using the internal or external set of buffers */
|
||||||
|
|
||||||
spi_dmatxsetup(priv, txbuffer, &txdummy, nwords, &txdmacfg);
|
spi_dmatxsetup(priv, txbuffer, &txdummy, nwords, &txdmacfg);
|
||||||
spi_dmarxsetup(priv, rxbuffer, (uint16_t *)rxdummy, nwords, &rxdmacfg);
|
spi_dmarxsetup(priv, rxbuffer, (uint16_t *)rxdummy, nwords, &rxdmacfg);
|
||||||
|
|
||||||
#ifdef CONFIG_STM32H7_DMACAPABLE
|
#ifdef CONFIG_STM32H7_DMACAPABLE
|
||||||
|
|
||||||
/* Setup DMAs */
|
/* Test for DMA capability of only callers buffers, internal buffers are
|
||||||
|
* guaranteed capable.
|
||||||
/* If this bus uses a in driver buffers we will incur 2 copies,
|
|
||||||
* The copy cost is << less the non DMA transfer time and having
|
|
||||||
* the buffer in the driver ensures DMA can be used. This is bacause
|
|
||||||
* the API does not support passing the buffer extent so the only
|
|
||||||
* extent is buffer + the transfer size. These can sizes be less than
|
|
||||||
* the cache line size, and not aligned and tyicaly greater then 4
|
|
||||||
* bytes, which is about the break even point for the DMA IO overhead.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (txbuffer && priv->txbuf)
|
if ((priv->config != SIMPLEX_RX && priv->txbuf == 0 &&
|
||||||
{
|
|
||||||
if (nbytes > priv->buflen)
|
|
||||||
{
|
|
||||||
nbytes = priv->buflen;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(priv->txbuf, txbuffer, nbytes);
|
|
||||||
txbuffer = priv->txbuf;
|
|
||||||
rxbuffer = rxbuffer ? priv->rxbuf : rxbuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((priv->config != SIMPLEX_RX &&
|
|
||||||
!stm32_dmacapable(priv->txdma, &txdmacfg)) ||
|
!stm32_dmacapable(priv->txdma, &txdmacfg)) ||
|
||||||
(priv->config != SIMPLEX_TX &&
|
(priv->config != SIMPLEX_TX && priv->rxbuf == 0 &&
|
||||||
!stm32_dmacapable(priv->rxdma, &rxdmacfg)))
|
!stm32_dmacapable(priv->rxdma, &rxdmacfg)))
|
||||||
{
|
{
|
||||||
/* Unsupported memory region fall back to non-DMA method. */
|
/* Unsupported memory region fall back to non-DMA method. */
|
||||||
@ -2009,29 +2031,6 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
* 4. Enable the SPI by setting the SPE bit.
|
* 4. Enable the SPI by setting the SPE bit.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
spi_enable(priv, false);
|
|
||||||
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, SPI_CFG1_TXDMAEN,
|
|
||||||
SPI_CFG1_RXDMAEN);
|
|
||||||
|
|
||||||
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n",
|
|
||||||
txbuffer, rxbuffer, nwords);
|
|
||||||
DEBUGASSERT(priv->spibase != 0);
|
|
||||||
|
|
||||||
/* Setup the DMA */
|
|
||||||
|
|
||||||
if (priv->config != SIMPLEX_RX)
|
|
||||||
{
|
|
||||||
stm32_dmasetup(priv->txdma, &txdmacfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priv->config != SIMPLEX_TX)
|
|
||||||
{
|
|
||||||
stm32_dmasetup(priv->rxdma, &rxdmacfg);
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN |
|
|
||||||
SPI_CFG1_RXDMAEN);
|
|
||||||
|
|
||||||
/* Flush cache to physical memory */
|
/* Flush cache to physical memory */
|
||||||
|
|
||||||
if (txbuffer)
|
if (txbuffer)
|
||||||
@ -2045,6 +2044,35 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
(uintptr_t)rxbuffer + nbytes);
|
(uintptr_t)rxbuffer + nbytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* N.B. the H7 tri-states the clock output on SPI disable and
|
||||||
|
* unfortunately the Chip Select (CS) is active. So we keep
|
||||||
|
* the device disabled for the minimum time, that meets the
|
||||||
|
* setup sequence criteria.
|
||||||
|
*/
|
||||||
|
|
||||||
|
spi_enable(priv, false);
|
||||||
|
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, SPI_CFG1_TXDMAEN,
|
||||||
|
SPI_CFG1_RXDMAEN);
|
||||||
|
|
||||||
|
spiinfo("txbuffer=%p rxbuffer=%p nwords=%d\n",
|
||||||
|
txbuffer, rxbuffer, nwords);
|
||||||
|
DEBUGASSERT(priv->spibase != 0);
|
||||||
|
|
||||||
|
/* Setup the DMA */
|
||||||
|
|
||||||
|
if (priv->config != SIMPLEX_TX)
|
||||||
|
{
|
||||||
|
stm32_dmasetup(priv->rxdma, &rxdmacfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (priv->config != SIMPLEX_RX)
|
||||||
|
{
|
||||||
|
stm32_dmasetup(priv->txdma, &txdmacfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN |
|
||||||
|
SPI_CFG1_RXDMAEN);
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_TRIGGER
|
#ifdef CONFIG_SPI_TRIGGER
|
||||||
/* Is deferred triggering in effect? */
|
/* Is deferred triggering in effect? */
|
||||||
|
|
||||||
@ -2078,7 +2106,7 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
spi_dmatxwait(priv);
|
spi_dmatxwait(priv);
|
||||||
|
|
||||||
/* To close communication it is mandatory to follow these steps in
|
/* To close communication it is mandatory to follow these steps in
|
||||||
* order:
|
* this order:
|
||||||
* 1. Disable DMA request for Tx and Rx in the DMA registers, if the
|
* 1. Disable DMA request for Tx and Rx in the DMA registers, if the
|
||||||
* DMA issued.
|
* DMA issued.
|
||||||
* 2. Disable the SPI by following the SPI disable procedure.
|
* 2. Disable the SPI by following the SPI disable procedure.
|
||||||
@ -2091,10 +2119,17 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
|
|||||||
SPI_CFG1_RXDMAEN, 0);
|
SPI_CFG1_RXDMAEN, 0);
|
||||||
spi_enable(priv, true);
|
spi_enable(priv, true);
|
||||||
|
|
||||||
|
/* Second copy: Copy the DMA internal buffer to caller's buffer */
|
||||||
|
|
||||||
|
if (orig_rxbuffer && priv->rxbuf)
|
||||||
|
{
|
||||||
|
memcpy(orig_rxbuffer, priv->rxbuf, nbytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_SPI_TRIGGER
|
#ifdef CONFIG_SPI_TRIGGER
|
||||||
priv->trigarmed = false;
|
priv->trigarmed = false;
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_STM32H7_SPI_DMA */
|
#endif /* CONFIG_STM32H7_SPI_DMA */
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user