Skip to content

Commit bd344be

Browse files
kamnxthenrikbrixandersen
authored andcommitted
drivers: serial: stm32: use hardware rx timeout interrupt when available
Use the RTO interrupt for RX timeouts when using the async API. The workqueue based approach is sensitive to load and has a higher latency than using hardware timeouts. Signed-off-by: Kamil Krzyżanowski <[email protected]>
1 parent 50d6eac commit bd344be

File tree

1 file changed

+83
-5
lines changed

1 file changed

+83
-5
lines changed

drivers/serial/uart_stm32.c

Lines changed: 83 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,15 @@ LOG_MODULE_REGISTER(uart_stm32, CONFIG_UART_LOG_LEVEL);
6666
#define HAS_DRIVER_ENABLE 0
6767
#endif
6868

69+
/* Helper for checking if we can use hardware receive timeouts
70+
* instead of the work queue on a given UART
71+
*/
72+
#ifdef USART_RTOR_RTO
73+
#define HAS_RTO 1
74+
#else
75+
#define HAS_RTO 0
76+
#endif
77+
6978
#ifdef CONFIG_PM
7079
/* Placeholder value when wakeup-line DT property is not defined */
7180
#define STM32_WAKEUP_LINE_NONE 0xFFFFFFFF
@@ -1407,6 +1416,14 @@ static void uart_stm32_isr(const struct device *dev)
14071416
/* clear the RXNE by flushing the fifo, because Rx data was not read */
14081417
LL_USART_RequestRxDataFlush(usart);
14091418
#endif /* USART_SR_RXNE */
1419+
#if HAS_RTO
1420+
} else if (LL_USART_IsEnabledIT_RTO(usart) && LL_USART_IsActiveFlag_RTO(usart)) {
1421+
1422+
LOG_DBG("rx timeout interrupt occurred");
1423+
1424+
LL_USART_ClearFlag_RTO(usart);
1425+
uart_stm32_dma_rx_flush(dev, STM32_ASYNC_STATUS_TIMEOUT);
1426+
#endif /* HAS_RTO */
14101427
}
14111428

14121429
/* Clear errors */
@@ -1490,7 +1507,15 @@ static int uart_stm32_async_rx_disable(const struct device *dev)
14901507
return -EFAULT;
14911508
}
14921509

1510+
#if HAS_RTO
1511+
if (LL_USART_IsEnabledIT_RTO(usart)) {
1512+
LL_USART_DisableIT_RTO(usart);
1513+
} else {
1514+
LL_USART_DisableIT_IDLE(usart);
1515+
}
1516+
#else /* HAS_RTO */
14931517
LL_USART_DisableIT_IDLE(usart);
1518+
#endif /* HAS_RTO */
14941519

14951520
uart_stm32_dma_rx_flush(dev, STM32_ASYNC_STATUS_TIMEOUT);
14961521

@@ -1736,6 +1761,63 @@ static int uart_stm32_async_tx(const struct device *dev,
17361761
return 0;
17371762
}
17381763

1764+
static void set_timeout_itr(USART_TypeDef *usart, uint32_t baudrate, int32_t timeout)
1765+
{
1766+
#if HAS_RTO
1767+
#if HAS_LPUART
1768+
if (IS_LPUART_INSTANCE(usart)) {
1769+
goto enable_idle_itr;
1770+
}
1771+
#endif
1772+
1773+
if (LL_USART_IsEnabledIT_RTO(usart)) {
1774+
LL_USART_DisableRxTimeout(usart);
1775+
LL_USART_DisableIT_RTO(usart);
1776+
LL_USART_ClearFlag_RTO(usart);
1777+
} else {
1778+
LL_USART_DisableIT_IDLE(usart);
1779+
LL_USART_ClearFlag_IDLE(usart);
1780+
}
1781+
1782+
if (timeout == SYS_FOREVER_US) {
1783+
goto enable_idle_itr;
1784+
}
1785+
1786+
/* Cast to uint64_t to avoid overflowing before the division */
1787+
uint64_t timeout_in_bits = ((uint64_t)timeout * baudrate) / 1000000;
1788+
1789+
if (timeout_in_bits > UINT32_MAX) {
1790+
goto enable_idle_itr;
1791+
}
1792+
1793+
/* Not all UARTs support hardware RX timeouts.
1794+
* Try to set the timeout anyway, and see if it gets set.
1795+
* Ref RM, "USART receiver timeout register (USART_RTOR):
1796+
* "This register is reserved and forced by hardware to "0x00000000"
1797+
* when the Receiver timeout feature is not supported."
1798+
* This also falls back to using the IDLE interrupt
1799+
* if the timeout requested is too short or 0.
1800+
*/
1801+
LL_USART_SetRxTimeout(usart, (uint32_t)timeout_in_bits);
1802+
if (LL_USART_GetRxTimeout(usart) == 0) {
1803+
goto enable_idle_itr;
1804+
}
1805+
1806+
/* Use hardware RTO interrupt */
1807+
LOG_DBG("Set RTO timeout: %d us / %d bits", timeout, (uint32_t)timeout_in_bits);
1808+
1809+
LL_USART_ClearFlag_RTO(usart);
1810+
LL_USART_EnableIT_RTO(usart);
1811+
LL_USART_EnableRxTimeout(usart);
1812+
return;
1813+
1814+
enable_idle_itr:
1815+
#endif /* HAS_RTO */
1816+
1817+
LL_USART_ClearFlag_IDLE(usart);
1818+
LL_USART_EnableIT_IDLE(usart);
1819+
}
1820+
17391821
static int uart_stm32_async_rx_enable(const struct device *dev,
17401822
uint8_t *rx_buf, size_t buf_size, int32_t timeout)
17411823
{
@@ -1792,11 +1874,7 @@ static int uart_stm32_async_rx_enable(const struct device *dev,
17921874
/* Enable RX DMA requests */
17931875
uart_stm32_dma_rx_enable(dev);
17941876

1795-
/* Enable IRQ IDLE to define the end of a
1796-
* RX DMA transaction.
1797-
*/
1798-
LL_USART_ClearFlag_IDLE(usart);
1799-
LL_USART_EnableIT_IDLE(usart);
1877+
set_timeout_itr(usart, data->uart_cfg->baudrate, timeout);
18001878

18011879
LL_USART_EnableIT_ERROR(usart);
18021880

0 commit comments

Comments
 (0)