arch/arm/src/stm32h7/stm32_spi.c: Corrections for SPI master driver

This fixes the following 3 issues:

1. Wait for send to complete in exchange

Before shutting down the SPI, we have to wait for send to complete; not only
DMA, since DMA just puts data to the SPI fifo. It is not yet out of SPI.

When doing exchange with both send & receive this is not an issue because when
receive dma has completed, it is certain that also the send is.

This can be accomplished by completing the transfer in SPI TXC interrupt
instead of DMA callback.

2. Fix TXDMAEN and RXDMAEN placement

According to the spec, the RXDMAEN must be enabled before
enabling DMA requests for Tx and Rx in DMA registers, and TXDMAEN
after that.

Cleaner place to do this is in spi_dmarxstart and spi_dmatxstart, where
also the dma requests are enabled. This also handles properly the
simplex modes.

3. Remove bus signal glitches when shutting off SPI block

Use AFCNTR to avoid glitches in SPI lines while turning SPI block
on/off during calls to exchange.

Signed-off-by: Jukka Laitinen <jukka.laitinen@intel.com>
This commit is contained in:
Jukka Laitinen 2020-05-29 15:42:09 +03:00 committed by David Sidrane
parent 91779e997a
commit fe44ce0f3f
2 changed files with 97 additions and 67 deletions

View File

@ -397,6 +397,22 @@
#define SPI_SR_CTSIZE_SHIFT (16) /* Bits 16-31: number of data frames remaining in current TSIZE session */
#define SPI_SR_CTSIZE_MASK (1 << SPI_SR_CTSIZE_SHIFT)
/* SPI/I2S interrupt/status flags interrupt enable register */
#define SPI_IER_RXPIE (1 << 0) /* Bit 0: RXP Interrupt enable */
#define SPI_IER_TXPIE (1 << 1) /* Bit 1: TXP interrupt enable */
#define SPI_IER_DXPIE (1 << 2) /* Bit 2: DXP interrupt enable */
#define SPI_IER_EOTIE (1 << 3) /* Bit 3: EOT, SUSP and TXC interrupt enable */
#define SPI_IER_TXTFIE (1 << 4) /* Bit 4: TXTFIE interrupt enable */
#define SPI_IER_UDRIE (1 << 5) /* Bit 5: UDR interrupt enable */
#define SPI_IER_OVRIE (1 << 6) /* Bit 6: OVR interrupt enable */
#define SPI_IER_CRCEIE (1 << 7) /* Bit 7: CRC error interrupt enable */
#define SPI_IER_TIFREIE (1 << 8) /* Bit 8: TIFRE interrupt enable */
#define SPI_IER_MODFIE (1 << 9) /* Bit 9: mode fault interrupt enable */
#define SPI_IER_TSERFIE (1 << 10) /* Bit 10: additional number of transactions
* reload interrupt enable
*/
/* SPI/I2S interrupt/status flags clear register */
/* Bits 0-2: Reserved */

View File

@ -234,9 +234,7 @@ struct stm32_spidev_s
struct spi_dev_s spidev; /* Externally visible part of the SPI interface */
uint32_t spibase; /* SPIn base address */
uint32_t spiclock; /* Clocking for the SPI module */
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
uint8_t spiirq; /* SPI IRQ number */
#endif
#ifdef CONFIG_STM32H7_SPI_DMA
volatile uint8_t rxresult; /* Result of the RX DMA */
volatile uint8_t txresult; /* Result of the RX DMA */
@ -291,11 +289,8 @@ static inline void spi_dumpregs(FAR struct stm32_spidev_s *priv);
static int spi_dmarxwait(FAR struct stm32_spidev_s *priv);
static int spi_dmatxwait(FAR struct stm32_spidev_s *priv);
static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv);
static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv);
static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr,
void *arg);
static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr,
void *arg);
static void spi_dmarxsetup(FAR struct stm32_spidev_s *priv,
FAR void *rxbuffer,
FAR void *rxdummy,
@ -396,9 +391,7 @@ static struct stm32_spidev_s g_spi1dev =
},
.spibase = STM32_SPI1_BASE,
.spiclock = SPI123_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI1,
#endif
#ifdef CONFIG_STM32H7_SPI1_DMA
.rxch = DMAMAP_SPI1_RX,
.txch = DMAMAP_SPI1_TX,
@ -464,9 +457,7 @@ static struct stm32_spidev_s g_spi2dev =
},
.spibase = STM32_SPI2_BASE,
.spiclock = SPI123_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI2,
#endif
#ifdef CONFIG_STM32H7_SPI2_DMA
.rxch = DMAMAP_SPI2_RX,
.txch = DMAMAP_SPI2_TX,
@ -532,9 +523,7 @@ static struct stm32_spidev_s g_spi3dev =
},
.spibase = STM32_SPI3_BASE,
.spiclock = SPI123_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI3,
#endif
#ifdef CONFIG_STM32H7_SPI3_DMA
.rxch = DMAMAP_SPI3_RX,
.txch = DMAMAP_SPI3_TX,
@ -600,9 +589,7 @@ static struct stm32_spidev_s g_spi4dev =
},
.spibase = STM32_SPI4_BASE,
.spiclock = SPI45_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI4,
#endif
#ifdef CONFIG_STM32H7_SPI4_DMA
.rxch = DMAMAP_SPI4_RX,
.txch = DMAMAP_SPI4_TX,
@ -668,9 +655,7 @@ static struct stm32_spidev_s g_spi5dev =
},
.spibase = STM32_SPI5_BASE,
.spiclock = SPI45_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI5,
#endif
#ifdef CONFIG_STM32H7_SPI5_DMA
.rxch = DMAMAP_SPI5_RX,
.txch = DMAMAP_SPI5_TX,
@ -736,9 +721,7 @@ static struct stm32_spidev_s g_spi6dev =
},
.spibase = STM32_SPI6_BASE,
.spiclock = SPI6_KERNEL_CLOCK_FREQ,
#ifdef CONFIG_STM32H7_SPI_INTERRUPTS
.spiirq = STM32_IRQ_SPI6,
#endif
#ifdef CONFIG_STM32H7_SPI6_DMA
.rxch = DMAMAP_SPI6_RX,
.txch = DMAMAP_SPI6_TX,
@ -846,6 +829,22 @@ static inline void spi_putreg(FAR struct stm32_spidev_s *priv,
putreg32(value, priv->spibase + offset);
}
/****************************************************************************
* Name: spi_modifyreg
*
* Description:
* Modify an SPI register
*
****************************************************************************/
static void spi_modifyreg(FAR struct stm32_spidev_s *priv, uint32_t offset,
uint32_t clrbits, uint32_t setbits)
{
/* Only 32-bit registers */
modifyreg32(priv->spibase + offset, clrbits, setbits);
}
/****************************************************************************
* Name: spi_readword
*
@ -996,6 +995,39 @@ static void spi_dumpregs(FAR struct stm32_spidev_s *priv)
}
#endif
/****************************************************************************
* Name: spi_interrupt
*
* Description:
* In DMA transfer mode, handle the transmission complete interrupt
*
****************************************************************************/
static int spi_interrupt(int irq, void *context, void *arg)
{
FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg;
uint32_t sr = spi_getreg(priv, STM32_SPI_SR_OFFSET);
if (sr & SPI_SR_TXC)
{
/* Disable interrupt. This needs to be done
* before disabling SPI to avoid spurious TXC interrupt
* (ERRATA: Q2.14.4 TXP interrupt occurring while SPI/I2Sdisabled")
* There is no need to clear the interrupt since it is disabled and
* SPI will be disabled after transmit as well.
*/
spi_modifyreg(priv, STM32_SPI_IER_OFFSET, SPI_IER_EOTIE, 0);
/* Set result and release wait semaphore */
priv->txresult = 0x80;
nxsem_post(&priv->txsem);
}
return 0;
}
/****************************************************************************
* Name: spi_dmarxwait
*
@ -1056,6 +1088,15 @@ static int spi_dmatxwait(FAR struct stm32_spidev_s *priv)
return OK;
}
/* Enable TXC interrupt. This needs to be done after starting TX-dma AND
* when spi is enabled to avoid spurious TXC interrupt (ERRATA:
* "2.14.4 TXP interrupt occurring while SPI/I2Sdisabled")
* It is fine to enable this interrupt even if the transfer already did
* complete; in this case the irq will just fire right away
*/
spi_modifyreg(priv, STM32_SPI_IER_OFFSET, 0, SPI_IER_EOTIE);
/* Take the semaphore (perhaps waiting). If the result is zero, then the
* DMA must not really have completed???
*/
@ -1091,21 +1132,6 @@ static inline void spi_dmarxwakeup(FAR struct stm32_spidev_s *priv)
}
#endif
/****************************************************************************
* Name: spi_dmatxwakeup
*
* Description:
* Signal that DMA is complete
*
****************************************************************************/
#ifdef CONFIG_STM32H7_SPI_DMA
static inline void spi_dmatxwakeup(FAR struct stm32_spidev_s *priv)
{
nxsem_post(&priv->txsem);
}
#endif
/****************************************************************************
* Name: spi_dmarxcallback
*
@ -1126,26 +1152,6 @@ static void spi_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
}
#endif
/****************************************************************************
* Name: spi_dmatxcallback
*
* Description:
* Called when the RX DMA completes
*
****************************************************************************/
#ifdef CONFIG_STM32H7_SPI_DMA
static void spi_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg)
{
FAR struct stm32_spidev_s *priv = (FAR struct stm32_spidev_s *)arg;
/* Wake-up the SPI driver */
priv->txresult = isr | 0x080; /* OR'ed with 0x80 to assure non-zero */
spi_dmatxwakeup(priv);
}
#endif
/****************************************************************************
* Name: spi_dmarxsetup
*
@ -1287,6 +1293,7 @@ static void spi_dmarxstart(FAR struct stm32_spidev_s *priv)
}
priv->rxresult = 0;
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_RXDMAEN);
stm32_dmastart(priv->rxdma, spi_dmarxcallback, priv, false);
}
#endif
@ -1310,18 +1317,11 @@ static void spi_dmatxstart(FAR struct stm32_spidev_s *priv)
}
priv->txresult = 0;
stm32_dmastart(priv->txdma, spi_dmatxcallback, priv, false);
stm32_dmastart(priv->txdma, NULL, priv, false);
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN);
}
#endif
static void spi_modifyreg(FAR struct stm32_spidev_s *priv, uint32_t offset,
uint32_t clrbits, uint32_t setbits)
{
/* Only 32-bit registers */
modifyreg32(priv->spibase + offset, clrbits, setbits);
}
/****************************************************************************
* Name: spi_lock
*
@ -2051,8 +2051,6 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
*/
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);
@ -2070,9 +2068,6 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
stm32_dmasetup(priv->txdma, &txdmacfg);
}
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, 0, SPI_CFG1_TXDMAEN |
SPI_CFG1_RXDMAEN);
#ifdef CONFIG_SPI_TRIGGER
/* Is deferred triggering in effect? */
@ -2114,6 +2109,11 @@ static void spi_exchange(FAR struct spi_dev_s *dev, FAR const void *txbuffer,
* bits in the SPI_CFG1 register, if DMA Tx and/or DMA Rx are used.
*/
/* TX transmission must be completed before shutting down the SPI */
DEBUGASSERT(priv->config != SIMPLEX_RX ?
(spi_getreg(priv, STM32_SPI_SR_OFFSET) & SPI_SR_TXC) : 1);
spi_enable(priv, false);
spi_modifyreg(priv, STM32_SPI_CFG1_OFFSET, SPI_CFG1_TXDMAEN |
SPI_CFG1_RXDMAEN, 0);
@ -2360,7 +2360,7 @@ static void spi_bus_initialize(struct stm32_spidev_s *priv)
clrbits = SPI_CFG2_CPHA | SPI_CFG2_CPOL | SPI_CFG2_LSBFRST |
SPI_CFG2_COMM_MASK;
setbits = SPI_CFG2_MASTER | SPI_CFG2_SSM;
setbits = SPI_CFG2_AFCNTR | SPI_CFG2_MASTER | SPI_CFG2_SSM;
switch (priv->config)
{
@ -2434,6 +2434,20 @@ static void spi_bus_initialize(struct stm32_spidev_s *priv)
}
#endif
/* Attach the IRQ to the driver */
if (irq_attach(priv->spiirq, spi_interrupt, priv))
{
/* We could not attach the ISR to the interrupt */
spierr("ERROR: Can't initialize spi irq\n");
return;
}
/* Enable SPI interrupts */
up_enable_irq(priv->spiirq);
/* Enable SPI */
spi_enable(priv, true);