Merged in juniskane/nuttx_stm32l4/stm32_serial_patches_pr (pull request #504)

Stm32, stm32l4 serial patches

* stm32: serial: add interface to get uart_dev_t by USART number, stm32_serial_get_uart

* stm32: serial: do not stop processing input in SW flow-control mode

* stm32l4: serial: do not stop processing input in SW flow-control mode

* stm32l4: serial: suspend serial for Stop mode

Approved-by: Gregory Nutt <gnutt@nuttx.org>
This commit is contained in:
Juha Niskanen 2017-10-09 16:20:24 +00:00 committed by Gregory Nutt
parent ecf6dda2c5
commit 798d03cb3d
4 changed files with 396 additions and 25 deletions

View File

@ -277,6 +277,10 @@ struct up_dev_s
uint16_t ie; /* Saved interrupt mask bits value */
uint16_t sr; /* Saved status bits */
/* Has been initialized and HW is setup. */
bool initialized;
/* If termios are supported, then the following fields may vary at
* runtime.
*/
@ -1559,6 +1563,11 @@ static int up_setup(struct uart_dev_s *dev)
/* Set up the cached interrupt enables value */
priv->ie = 0;
/* Mark device as initialized. */
priv->initialized = true;
return OK;
}
@ -1638,6 +1647,10 @@ static void up_shutdown(struct uart_dev_s *dev)
struct up_dev_s *priv = (struct up_dev_s *)dev->priv;
uint32_t regval;
/* Mark device as uninitialized. */
priv->initialized = false;
/* Disable all interrupts */
up_disableusartint(priv, NULL);
@ -2287,6 +2300,22 @@ static bool up_rxflowcontrol(struct uart_dev_s *dev,
/* Assert/de-assert nRTS set it high resume/stop sending */
stm32_gpiowrite(priv->rts_gpio, upper);
if (upper)
{
/* With heavy Rx traffic, RXNE might be set and data pending.
* Returning 'true' in such case would cause RXNE left unhandled
* and causing interrupt storm. Sending end might be also be slow
* to react on nRTS, and returning 'true' here would prevent
* processing that data.
*
* Therefore, return 'false' so input data is still being processed
* until sending end reacts on nRTS signal and stops sending more.
*/
return false;
}
return upper;
}
@ -2645,6 +2674,31 @@ static int up_pm_prepare(struct pm_callback_s *cb, int domain,
#ifdef USE_SERIALDRIVER
/****************************************************************************
* Name: stm32_serial_get_uart
*
* Description:
* Get serial driver structure for STM32 USART
*
****************************************************************************/
FAR uart_dev_t *stm32_serial_get_uart(int uart_num)
{
int uart_idx = uart_num - 1;
if (uart_idx < 0 || uart_idx >= STM32_NUSART || !uart_devs[uart_idx])
{
return NULL;
}
if (!uart_devs[uart_idx]->initialized)
{
return NULL;
}
return &uart_devs[uart_idx]->dev;
}
/****************************************************************************
* Name: up_earlyserialinit
*

View File

@ -41,6 +41,7 @@
************************************************************************************/
#include <nuttx/config.h>
#include <nuttx/serial/serial.h>
#include "chip.h"
@ -412,6 +413,16 @@ extern "C"
* Public Functions
************************************************************************************/
/************************************************************************************
* Name: stm32_serial_get_uart
*
* Description:
* Get serial driver structure for STM32 USART
*
************************************************************************************/
FAR uart_dev_t *stm32_serial_get_uart(int uart_num);
/************************************************************************************
* Name: stm32_serial_dma_poll
*

View File

@ -213,6 +213,12 @@ struct stm32l4_serial_s
bool initialized;
bool suspended; /* UART device has been suspended. */
/* Interrupt mask value stored before suspending for stop mode. */
uint16_t suspended_ie;
/* If termios are supported, then the following fields may vary at
* runtime.
*/
@ -262,6 +268,7 @@ struct stm32l4_serial_s
#ifdef SERIAL_HAVE_DMA
DMA_HANDLE rxdma; /* currently-open receive DMA stream */
bool rxenable; /* DMA-based reception en/disable */
bool rxdmasusp; /* Rx DMA suspended */
uint32_t rxdmanext; /* Next byte in the DMA buffer to be read */
char *const rxfifo; /* Receive DMA buffer */
#endif
@ -305,6 +312,10 @@ static int stm32l4serial_dmasetup(FAR struct uart_dev_s *dev);
static void stm32l4serial_dmashutdown(FAR struct uart_dev_s *dev);
static int stm32l4serial_dmareceive(FAR struct uart_dev_s *dev,
FAR unsigned int *status);
static void stm32l4serial_dmareenable(struct stm32l4_serial_s *priv);
#ifdef CONFIG_SERIAL_IFLOWCONTROL
static bool stm32l4serial_dmaiflowrestart(struct stm32l4_serial_s *priv);
#endif
static void stm32l4serial_dmarxint(FAR struct uart_dev_s *dev, bool enable);
static bool stm32l4serial_dmarxavailable(struct uart_dev_s *dev);
@ -749,6 +760,8 @@ static struct pm_callback_s g_serialcb =
};
#endif
static bool serial_suspended_for_stop = false;
/****************************************************************************
* Private Functions
****************************************************************************/
@ -1030,6 +1043,127 @@ static void stm32l4serial_setformat(FAR struct uart_dev_s *dev)
}
#endif /* CONFIG_SUPPRESS_UART_CONFIG */
/****************************************************************************
* Name: stm32l4serial_setsuspend
*
* Description:
* Suspend or resume serial peripheral.
*
****************************************************************************/
static void stm32l4serial_setsuspend(struct uart_dev_s *dev, bool suspend)
{
FAR struct stm32l4_serial_s *priv = (struct stm32l4_serial_s *)dev->priv;
#ifdef SERIAL_HAVE_DMA
bool dmarestored = false;
#endif
if (priv->suspended == suspend)
{
return;
}
priv->suspended = suspend;
if (suspend)
{
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow)
{
/* Force RTS high to prevent further Rx. */
stm32l4_configgpio((priv->rts_gpio & ~GPIO_MODE_MASK)
| (GPIO_OUTPUT | GPIO_OUTPUT_SET));
}
#endif
/* Disable interrupts to prevent Tx. */
stm32l4serial_disableusartint(priv, &priv->suspended_ie);
/* Wait last Tx to complete. */
while ((stm32l4serial_getreg(priv, STM32L4_USART_ISR_OFFSET) & USART_ISR_TC) == 0);
#ifdef SERIAL_HAVE_DMA
if (priv->dev.ops == &g_uart_dma_ops && !priv->rxdmasusp)
{
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow && priv->rxdmanext == RXDMA_BUFFER_SIZE)
{
/* Rx DMA in non-circular iflow mode and already stopped
* at end of DMA buffer. No need to suspend. */
}
else
#endif
{
/* Suspend Rx DMA. */
stm32l4_dmastop(priv->rxdma);
priv->rxdmasusp = true;
}
}
#endif
}
else
{
#ifdef SERIAL_HAVE_DMA
if (priv->dev.ops == &g_uart_dma_ops && priv->rxdmasusp)
{
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow)
{
(void)stm32l4serial_dmaiflowrestart(priv);
}
else
#endif
{
/* This USART does not have HW flow-control. Unconditionally
* re-enable DMA (might loss unprocessed bytes received
* to DMA buffer before suspending). */
stm32l4serial_dmareenable(priv);
priv->rxdmasusp = false;
}
dmarestored = true;
}
#endif
/* Re-enable interrupts to resume Tx. */
stm32l4serial_restoreusartint(priv, priv->suspended_ie);
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow)
{
/* Restore peripheral RTS control. */
stm32l4_configgpio(priv->rts_gpio);
}
#endif
}
#ifdef SERIAL_HAVE_DMA
if (dmarestored)
{
irqstate_t flags;
flags = enter_critical_section();
/* Perform initial Rx DMA buffer fetch to wake-up serial device
* activity.
*/
if (priv->rxdma != NULL)
{
stm32l4serial_dmarxcallback(priv->rxdma, 0, priv);
}
leave_critical_section(flags);
}
#endif
}
/****************************************************************************
* Name: stm32l4serial_setapbclock
*
@ -1945,6 +2079,22 @@ static bool stm32l4serial_rxflowcontrol(FAR struct uart_dev_s *dev,
/* Assert/de-assert nRTS set it high resume/stop sending */
stm32l4_gpiowrite(priv->rts_gpio, upper);
if (upper)
{
/* With heavy Rx traffic, RXNE might be set and data pending.
* Returning 'true' in such case would cause RXNE left unhandled
* and causing interrupt storm. Sending end might be also be slow
* to react on nRTS, and returning 'true' here would prevent
* processing that data.
*
* Therefore, return 'false' so input data is still being processed
* until sending end reacts on nRTS signal and stops sending more.
*/
return false;
}
return upper;
}
@ -2040,16 +2190,31 @@ static int stm32l4serial_dmareceive(FAR struct uart_dev_s *dev,
*
****************************************************************************/
#if defined(SERIAL_HAVE_DMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
#if defined(SERIAL_HAVE_DMA)
static void stm32l4serial_dmareenable(FAR struct stm32l4_serial_s *priv)
{
/* Configure for non-circular DMA reception into the RX fifo */
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow)
{
/* Configure for non-circular DMA reception into the RX FIFO */
stm32l4_dmasetup(priv->rxdma,
priv->usartbase + STM32L4_USART_RDR_OFFSET,
(uint32_t)priv->rxfifo,
RXDMA_BUFFER_SIZE,
SERIAL_DMA_IFLOW_CONTROL_WORD);
stm32l4_dmasetup(priv->rxdma,
priv->usartbase + STM32L4_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 */
stm32l4_dmasetup(priv->rxdma,
priv->usartbase + STM32L4_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.
@ -2057,16 +2222,79 @@ static void stm32l4serial_dmareenable(FAR struct stm32l4_serial_s *priv)
priv->rxdmanext = 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.
*/
#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.
*/
stm32l4_dmastart(priv->rxdma, stm32l4serial_dmarxcallback, (FAR void *)priv,
false);
stm32l4_dmastart(priv->rxdma, stm32l4serial_dmarxcallback, (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.
*/
stm32l4_dmastart(priv->rxdma, stm32l4serial_dmarxcallback, (void *)priv, true);
}
/* Clear DMA suspended flag. */
priv->rxdmasusp = false;
}
#endif
/****************************************************************************
* Name: stm32l4serial_dmaiflowrestart
*
* Description:
* Call to restart RX DMA for input flow-controlled USART
*
****************************************************************************/
#if defined(SERIAL_HAVE_DMA) && defined(CONFIG_SERIAL_IFLOWCONTROL)
static bool stm32l4serial_dmaiflowrestart(struct stm32l4_serial_s *priv)
{
if (!priv->rxenable)
{
/* Rx not enabled by upper layer. */
return false;
}
if (priv->rxdmanext != RXDMA_BUFFER_SIZE)
{
if (priv->rxdmasusp)
{
/* Rx DMA in suspended state. */
if (stm32l4serial_dmarxavailable(&priv->dev))
{
/* DMA buffer has unprocessed data, do not re-enable yet. */
return false;
}
}
else
{
return false;
}
}
/* DMA is stopped or suspended and DMA buffer does not have pending data,
* re-enabling without data loss is now safe. */
stm32l4serial_dmareenable(priv);
return true;
}
#endif
/****************************************************************************
* Name: stm32l4serial_dmarxint
*
@ -2091,11 +2319,11 @@ static void stm32l4serial_dmarxint(FAR struct uart_dev_s *dev, bool enable)
priv->rxenable = enable;
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow && priv->rxenable && (priv->rxdmanext == RXDMA_BUFFER_SIZE))
if (priv->iflow)
{
/* Re-enable RX DMA. */
stm32l4serial_dmareenable(priv);
(void)stm32l4serial_dmaiflowrestart(priv);
}
#endif
}
@ -2245,15 +2473,32 @@ static void stm32l4serial_dmarxcallback(DMA_HANDLE handle, uint8_t status,
uart_recvchars(&priv->dev);
#ifdef CONFIG_SERIAL_IFLOWCONTROL
if (priv->iflow && priv->rxenable &&
(priv->rxdmanext == RXDMA_BUFFER_SIZE))
if (priv->iflow)
{
/* Re-enable RX DMA. */
stm32l4serial_dmareenable(priv);
(void)stm32l4serial_dmaiflowrestart(priv);
}
#endif
}
/* Get the masked USART status word to check and clear error flags.
*
* When wake-up from low power mode was not fast enough, UART is resumed too
* late and sometimes exactly when character was coming over UART, resulting
* to frame error.
* If error flag is not cleared, Rx DMA will be stuck. Clearing errors
* will release Rx DMA.
*/
priv->sr = stm32l4serial_getreg(priv, STM32L4_USART_ISR_OFFSET);
if ((priv->sr & (USART_ISR_ORE | USART_ISR_NF | USART_ISR_FE)) != 0)
{
stm32l4serial_putreg(priv, STM32L4_USART_ICR_OFFSET,
(USART_ICR_NCF | USART_ICR_ORECF | USART_ICR_FECF));
}
}
#endif
@ -2287,29 +2532,27 @@ static void stm32l4serial_pmnotify(FAR struct pm_callback_s *cb, int domain,
{
case(PM_NORMAL):
{
/* Logic for PM_NORMAL goes here */
stm32l4_serial_set_suspend(false);
}
break;
case(PM_IDLE):
{
/* Logic for PM_IDLE goes here */
stm32l4_serial_set_suspend(false);
}
break;
case(PM_STANDBY):
{
/* Logic for PM_STANDBY goes here */
/* TODO: logic for enabling serial in Stop 1 mode with HSI16 missing */
stm32l4_serial_set_suspend(true);
}
break;
case(PM_SLEEP):
{
/* Logic for PM_SLEEP goes here */
stm32l4_serial_set_suspend(true);
}
break;
@ -2371,6 +2614,49 @@ static int stm32l4serial_pmprepare(FAR struct pm_callback_s *cb, int domain,
#ifdef USE_SERIALDRIVER
/****************************************************************************
* Name: stm32l4_is_serial_suspended
*
* Description:
* Check if serial peripherals have been suspended for deep-sleep/stop modes.
*
****************************************************************************/
bool stm32l4_is_serial_suspended(void)
{
return (serial_suspended_for_stop);
}
/****************************************************************************
* Name: stm32_serial_set_suspend
*
* Description:
* Suspend or resume serial peripherals for/from deep-sleep/stop modes.
*
****************************************************************************/
void stm32l4_serial_set_suspend(bool suspend)
{
int n;
/* Already in desired state? */
if (suspend == serial_suspended_for_stop)
return;
serial_suspended_for_stop = suspend;
for (n = 0; n < STM32L4_NUSART+STM32L4_NUART; n++)
{
struct stm32l4_serial_s *priv = uart_devs[n];
if (!priv || !priv->initialized)
continue;
stm32l4serial_setsuspend(&priv->dev, suspend);
}
}
/****************************************************************************
* Name: stm32l4_serial_get_uart
*

View File

@ -248,6 +248,26 @@ extern "C"
FAR uart_dev_t *stm32l4_serial_get_uart(int uart_num);
/****************************************************************************
* Name: stm32l4_is_serial_suspended
*
* Description:
* Check if serial peripherals have been suspended for deep-sleep/stop modes.
*
****************************************************************************/
bool stm32l4_is_serial_suspended(void);
/****************************************************************************
* Name: stm32_serial_set_suspend
*
* Description:
* Suspend or resume serial peripherals for/from deep-sleep/stop modes.
*
****************************************************************************/
void stm32l4_serial_set_suspend(bool suspend);
/************************************************************************************
* Name: stm32l4_serial_dma_poll
*