Backport stm32f7:Serial fix for dropped data

1) Revert the inherited dma bug from the stm32
       see df9ae3c13f
       for details.

       2) Most all CR1-CR3 settings can not be configured while UE
          is true. Threfore we make all operation atomic and disable
          UE and restore it's originalstate on exit.

       N.B. This backport omits all upstream breaking changes.
This commit is contained in:
David Sidrane 2017-03-31 10:04:39 -10:00 committed by Lorenz Meier
parent ba1de2cc0b
commit 0b91fd0e20
1 changed files with 364 additions and 0 deletions

View File

@ -0,0 +1,364 @@
diff --git NuttX/nuttx/arch/arm/src/stm32f7/stm32_serial.c NuttX/nuttx/arch/arm/src/stm32f7/stm32_serial.c
index 1f5445a..a51bb02 100644
--- NuttX/nuttx/arch/arm/src/stm32f7/stm32_serial.c
+++ NuttX/nuttx/arch/arm/src/stm32f7/stm32_serial.c
@@ -1,8 +1,9 @@
/****************************************************************************
* arch/arm/src/stm32f7/stm32_serial.c
*
- * Copyright (C) 2015-2016 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
+ * Copyright (C) 2015-2017 Gregory Nutt. All rights reserved.
+ * Authors: Gregory Nutt <gnutt@nuttx.org>
+ * David Sidrane <david_s5@nscdg.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -189,16 +190,6 @@
CONFIG_USART_DMAPRIO | \
DMA_SCR_PBURST_SINGLE | \
DMA_SCR_MBURST_SINGLE)
-# ifdef CONFIG_SERIAL_IFLOWCONTROL
-# define SERIAL_DMA_IFLOW_CONTROL_WORD \
- (DMA_SCR_DIR_P2M | \
- DMA_SCR_MINC | \
- DMA_SCR_PSIZE_8BITS | \
- DMA_SCR_MSIZE_8BITS | \
- CONFIG_USART_DMAPRIO | \
- DMA_SCR_PBURST_SINGLE | \
- DMA_SCR_MBURST_SINGLE)
-# endif
#endif /* SERIAL_HAVE_DMA */
/* Power management definitions */
@@ -288,8 +279,7 @@ struct up_dev_s
#ifdef SERIAL_HAVE_DMA
DMA_HANDLE rxdma; /* currently-open receive DMA stream */
bool rxenable; /* DMA-based reception en/disable */
- uint16_t rxdmain; /* Next byte in the DMA where hardware will write */
- uint16_t rxdmaout; /* Next byte in the DMA buffer to be read */
+ uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */
char *const rxfifo; /* Receive DMA buffer */
#endif
@@ -1174,7 +1164,23 @@ static void up_set_format(struct uart_dev_s *dev)
uint32_t regval;
uint32_t usartdiv8;
uint32_t cr1;
+ uint32_t cr1_ue;
uint32_t brr;
+ irqstate_t flags;
+
+
+ flags = enter_critical_section();
+
+ /* Get the original state of UE */
+
+ cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET);
+ cr1_ue = cr1 & USART_CR1_UE;
+ cr1 &= ~USART_CR1_UE;
+
+ /* Disable UE as the format bits and baud rate registers can not be
+ * updated while UE = 1 */
+
+ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1);
/* In case of oversampling by 8, the equation is:
*
@@ -1194,7 +1200,6 @@ static void up_set_format(struct uart_dev_s *dev)
/* Use oversamply by 8 only if the divisor is small. But what is small? */
- cr1 = up_serialin(priv, STM32_USART_CR1_OFFSET);
if (usartdiv8 > 100)
{
/* Use usartdiv16 */
@@ -1223,30 +1228,43 @@ static void up_set_format(struct uart_dev_s *dev)
/* Configure parity mode */
- regval = up_serialin(priv, STM32_USART_CR1_OFFSET);
- regval &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0);
+ cr1 &= ~(USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0 | USART_CR1_M1);
if (priv->parity == 1) /* Odd parity */
{
- regval |= (USART_CR1_PCE | USART_CR1_PS);
+ cr1 |= (USART_CR1_PCE | USART_CR1_PS);
}
else if (priv->parity == 2) /* Even parity */
{
- regval |= USART_CR1_PCE;
+ cr1 |= USART_CR1_PCE;
}
- /* Configure word length (Default: 8-bits) */
+ /* Configure word length (parity uses one of configured bits)
+ *
+ * Default: 1 start, 8 data (no parity), n stop, OR
+ * 1 start, 7 data + parity, n stop
+ */
- if (priv->bits == 7)
+ if (priv->bits == 9 || (priv->bits == 8 && priv->parity != 0))
{
- regval |= USART_CR1_M1;
+ /* Select: 1 start, 8 data + parity, n stop, OR
+ * 1 start, 9 data (no parity), n stop.
+ */
+
+ cr1 |= USART_CR1_M0;
}
- else if (priv->bits == 9)
+ else if (priv->bits == 7 && priv->parity == 0)
{
- regval |= USART_CR1_M0;
+ /* Select: 1 start, 7 data (no parity), n stop, OR
+ */
+
+ cr1 |= USART_CR1_M1;
}
+ /* Else Select: 1 start, 7 data + parity, n stop, OR
+ * 1 start, 8 data (no parity), n stop.
+ */
- up_serialout(priv, STM32_USART_CR1_OFFSET, regval);
+ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1);
/* Configure STOP bits */
@@ -1265,7 +1283,8 @@ static void up_set_format(struct uart_dev_s *dev)
regval = up_serialin(priv, STM32_USART_CR3_OFFSET);
regval &= ~(USART_CR3_CTSE | USART_CR3_RTSE);
-#if defined(CONFIG_SERIAL_IFLOWCONTROL) && !defined(CONFIG_STM32F7_FLOWCONTROL_BROKEN)
+#if defined(CONFIG_SERIAL_IFLOWCONTROL) && \
+ !defined(CONFIG_STM32F7_FLOWCONTROL_BROKEN)
if (priv->iflow && (priv->rts_gpio != 0))
{
regval |= USART_CR3_RTSE;
@@ -1280,6 +1299,9 @@ static void up_set_format(struct uart_dev_s *dev)
#endif
up_serialout(priv, STM32_USART_CR3_OFFSET, regval);
+ up_serialout(priv, STM32_USART_CR1_OFFSET, cr1 | cr1_ue);
+ leave_critical_section(flags);
+
}
#endif /* CONFIG_SUPPRESS_UART_CONFIG */
@@ -1508,35 +1530,19 @@ static int up_dma_setup(struct uart_dev_s *dev)
priv->rxdma = stm32_dmachannel(priv->rxdma_channel);
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->iflow)
- {
- /* Configure for non-circular DMA reception into the RX FIFO */
+ /* Configure for circular DMA reception into the RX FIFO */
- stm32_dmasetup(priv->rxdma,
- priv->usartbase + STM32_USART_RDR_OFFSET,
- (uint32_t)priv->rxfifo,
- RXDMA_BUFFER_SIZE,
- SERIAL_DMA_IFLOW_CONTROL_WORD);
- }
- else
-#endif
- {
- /* Configure for circular DMA reception into the RX FIFO */
-
- stm32_dmasetup(priv->rxdma,
- priv->usartbase + STM32_USART_RDR_OFFSET,
- (uint32_t)priv->rxfifo,
- RXDMA_BUFFER_SIZE,
- SERIAL_DMA_CONTROL_WORD);
- }
+ stm32_dmasetup(priv->rxdma,
+ priv->usartbase + STM32_USART_RDR_OFFSET,
+ (uint32_t)priv->rxfifo,
+ RXDMA_BUFFER_SIZE,
+ SERIAL_DMA_CONTROL_WORD);
/* Reset our DMA shadow pointer to match the address just
* programmed above.
*/
- priv->rxdmaout = 0;
- priv->rxdmain = 0;
+ priv->rxdmanext = 0;
/* Enable receive DMA for the UART */
@@ -1544,26 +1550,12 @@ static int up_dma_setup(struct uart_dev_s *dev)
regval |= USART_CR3_DMAR;
up_serialout(priv, STM32_USART_CR3_OFFSET, regval);
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->iflow)
- {
- /* Start the DMA channel, and arrange for callbacks at the full point
- * in the FIFO. After buffer gets full, hardware flow-control kicks
- * in and DMA transfer is stopped.
- */
-
- stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, false);
- }
- else
-#endif
- {
- /* Start the DMA channel, and arrange for callbacks at the half and
- * full points in the FIFO. This ensures that we have half a FIFO
- * worth of time to claim bytes before they are overwritten.
- */
+ /* Start the DMA channel, and arrange for callbacks at the half and
+ * full points in the FIFO. This ensures that we have half a FIFO
+ * worth of time to claim bytes before they are overwritten.
+ */
- stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true);
- }
+ stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, true);
return OK;
}
@@ -2258,49 +2250,27 @@ static bool up_rxflowcontrol(struct uart_dev_s *dev,
static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status)
{
struct up_dev_s *priv = (struct up_dev_s *)dev->priv;
- uint32_t rxdmain;
int c = 0;
/* If additional bytes have been added to the DMA buffer, then we will need
* to invalidate the DMA buffer before reading the byte.
*/
- rxdmain = up_dma_nextrx(priv);
- if (rxdmain != priv->rxdmain)
+ if (up_dma_nextrx(priv) != priv->rxdmanext)
{
/* Invalidate the DMA buffer */
arch_invalidate_dcache((uintptr_t)priv->rxfifo,
(uintptr_t)priv->rxfifo + RXDMA_BUFFER_SIZE - 1);
- /* Since DMA is ongoing, there are lots of race conditions here. We
- * just have to hope that the rxdmaout stays well ahead of rxdmain.
- */
+ /* Now read from the DMA buffer */
- priv->rxdmain = rxdmain;
- }
+ c = priv->rxfifo[priv->rxdmanext];
- /* Now check if there are any bytes to read from the DMA buffer */
-
- if (rxdmain != priv->rxdmaout)
- {
- c = priv->rxfifo[priv->rxdmaout];
-
- priv->rxdmaout++;
- if (priv->rxdmaout == RXDMA_BUFFER_SIZE)
+ priv->rxdmanext++;
+ if (priv->rxdmanext == RXDMA_BUFFER_SIZE)
{
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->iflow)
- {
- /* RX DMA buffer full. RX paused, RTS line pulled up to prevent
- * more input data from other end.
- */
- }
- else
-#endif
- {
- priv->rxdmaout = 0;
- }
+ priv->rxdmanext = 0;
}
}
@@ -2309,41 +2279,6 @@ static int up_dma_receive(struct uart_dev_s *dev, unsigned int *status)
#endif
/****************************************************************************
- * Name: up_dma_reenable
- *
- * Description:
- * Call to re-enable RX DMA.
- *
- ****************************************************************************/
-
-#if defined(SERIAL_HAVE_DMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
-static void up_dma_reenable(struct up_dev_s *priv)
-{
- /* Configure for non-circular DMA reception into the RX FIFO */
-
- stm32_dmasetup(priv->rxdma,
- priv->usartbase + STM32_USART_RDR_OFFSET,
- (uint32_t)priv->rxfifo,
- RXDMA_BUFFER_SIZE,
- SERIAL_DMA_IFLOW_CONTROL_WORD);
-
- /* Reset our DMA shadow pointer to match the address just programmed
- * above.
- */
-
- priv->rxdmaout = 0;
- priv->rxdmain = 0;
-
- /* Start the DMA channel, and arrange for callbacks at the full point in
- * the FIFO. After buffer gets full, hardware flow-control kicks in and
- * DMA transfer is stopped.
- */
-
- stm32_dmastart(priv->rxdma, up_dma_rxcallback, (void *)priv, false);
-}
-#endif
-
-/****************************************************************************
* Name: up_dma_rxint
*
* Description:
@@ -2365,15 +2300,6 @@ static void up_dma_rxint(struct uart_dev_s *dev, bool enable)
*/
priv->rxenable = enable;
-
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->iflow && priv->rxenable && (priv->rxdmaout == RXDMA_BUFFER_SIZE))
- {
- /* Re-enable RX DMA. */
-
- up_dma_reenable(priv);
- }
-#endif
}
#endif
@@ -2394,7 +2320,7 @@ static bool up_dma_rxavailable(struct uart_dev_s *dev)
* do not match, then there are bytes to be received.
*/
- return (up_dma_nextrx(priv) != priv->rxdmaout);
+ return (up_dma_nextrx(priv) != priv->rxdmanext);
}
#endif
@@ -2582,16 +2508,6 @@ static void up_dma_rxcallback(DMA_HANDLE handle, uint8_t status, void *arg)
if (priv->rxenable && up_dma_rxavailable(&priv->dev))
{
uart_recvchars(&priv->dev);
-
-#ifdef CONFIG_SERIAL_IFLOWCONTROL
- if (priv->iflow && priv->rxenable &&
- (priv->rxdmaout == RXDMA_BUFFER_SIZE))
- {
- /* Re-enable RX DMA. */
-
- up_dma_reenable(priv);
- }
-#endif
}
}
#endif