From fc3ab3e085ce5e904d26db674cdee4c8c5cb1068 Mon Sep 17 00:00:00 2001 From: David Sidrane Date: Thu, 2 Apr 2020 12:06:58 -0700 Subject: [PATCH] stm32h7:Fix DMA Overrun error --- arch/arm/src/stm32h7/stm32_spi.c | 78 ++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/arch/arm/src/stm32h7/stm32_spi.c b/arch/arm/src/stm32h7/stm32_spi.c index e56bde788a..8c304b5b0a 100644 --- a/arch/arm/src/stm32h7/stm32_spi.c +++ b/arch/arm/src/stm32h7/stm32_spi.c @@ -1451,16 +1451,6 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) spi_modifyreg(priv, STM32_SPI_CFG2_OFFSET, clrbits, setbits); -#ifdef CONFIG_STM32H7_SPI_DMA - /* Enabling SPI causes a spurious received character indication - * which confuse the DMA controller so we disable DMA during that - * enabling; and flush the SPI RX FIFO before re-enabling DMA. - */ - - spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, SPI_CFG1_RXDMAEN | - SPI_CFG1_TXDMAEN, 0); -#endif - /* Re-enable SPI */ spi_enable(priv, true); @@ -1469,19 +1459,9 @@ static void spi_setmode(FAR struct spi_dev_s *dev, enum spi_mode_e mode) { /* Flush SPI read FIFO */ - spi_getreg(priv, STM32_SPI_TXDR_OFFSET); + spi_getreg(priv, STM32_SPI_RXDR_OFFSET); } -#ifdef CONFIG_STM32H7_SPI_DMA - - /* Re-enable DMA (with SPI disabled) */ - - spi_enable(priv, false); - spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_RXDMAEN | - SPI_CFG1_TXDMAEN); - spi_enable(priv, true); -#endif - /* Save the mode so that subsequent re-configurations will be faster */ priv->mode = mode; @@ -1913,6 +1893,22 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, static uint8_t rxdummy[4] __attribute__((aligned(4))); static const uint16_t txdummy = 0xffff; + /* When starting communication using DMA, to prevent DMA channel + * management raising error events, these steps must be followed in + * order: + * 1. Enable DMA Rx buffer in the RXDMAEN bit in the SPI_CFG1 register, + * if DMA Rx is used. + * 2. Enable DMA requests for Tx and Rx in DMA registers, if the DMA is + * used. + * 3. Enable DMA Tx buffer in the TXDMAEN bit in the SPI_CFG1 register, + * if DMA Tx is used. + * 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); @@ -1920,6 +1916,9 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, spi_dmarxsetup(priv, rxbuffer, (uint16_t *)rxdummy, nbytes); spi_dmatxsetup(priv, txbuffer, &txdummy, nbytes); + spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN | + SPI_CFG1_RXDMAEN); + /* Flush cache to physical memory */ if (txbuffer) @@ -1927,19 +1926,17 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, up_flush_dcache((uintptr_t)txbuffer, (uintptr_t)txbuffer + nbytes); } - /* REVISIT: Master transfer start */ - - spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_CSTART); - #ifdef CONFIG_SPI_TRIGGER /* Is deferred triggering in effect? */ if (!priv->defertrig) { - /* No.. Start the DMAs */ + /* No.. Start the DMAs then the SPI */ spi_dmarxstart(priv); spi_dmatxstart(priv); + spi_enable(priv, true); + spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_CSTART); } else { @@ -1948,20 +1945,32 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer, priv->trigarmed = true; } #else - /* Start the DMAs */ + /* Start the DMAs then the SPI */ spi_dmarxstart(priv); spi_dmatxstart(priv); + spi_enable(priv, true); + spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_CSTART); #endif /* Then wait for each to complete */ - ret = spi_dmarxwait(priv); - if (ret >= 0) - { - ret = spi_dmatxwait(priv); - UNUSED(ret); - } + spi_dmarxwait(priv); + spi_dmatxwait(priv); + + /* To close communication it is mandatory to follow these steps in + * order: + * 1. Disable DMA request for Tx and Rx in the DMA registers, if the + * DMA issued. + * 2. Disable the SPI by following the SPI disable procedure. + * 3. Disable DMA Tx and Rx buffers by clearing the TXDMAEN and RXDMAEN + * bits in the SPI_CFG1 register, if DMA Tx and/or DMA Rx are used. + */ + + spi_enable(priv, false); + spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, SPI_CFG1_TXDMAEN | + SPI_CFG1_RXDMAEN, 0); + spi_enable(priv, true); #ifdef CONFIG_SPI_TRIGGER priv->trigarmed = false; @@ -2012,7 +2021,8 @@ static int spi_trigger(FAR struct spi_dev_s *dev) spi_dmarxstart(priv); spi_dmatxstart(priv); - + spi_enable(priv, true); + spi_modifyreg(priv, STM32_SPI_CR1_OFFSET, 0, SPI_CR1_CSTART); return OK; #else return -ENOSYS;