Skip to content

Commit 54e409e

Browse files
nordic-krchrlubos
authored andcommitted
[nrf fromtree] drivers: serial: nrfx_uarte: Add workaround for FRAMETIMEOUT corner case
When reception is restarted (STARTRX after ENDRX but no STOPRX) it is possible that FRAMETIMEOUT countdown counter will not be started by the first received byte if byte was already being transmitted when STARTRX was called. If that is the only byte then it is expected that timeout will be triggered but since FRAMETIMEOUT counter is not started there is no FRAMETIMEOUT event which has short to STOPRX. This situation will happen in case short buffers are used (< 5 bytes) because then short ENDRX_STARTRX is not used then. Signed-off-by: Krzysztof Chruściński <[email protected]> (cherry picked from commit 94d355a)
1 parent 21e025e commit 54e409e

File tree

1 file changed

+45
-2
lines changed

1 file changed

+45
-2
lines changed

drivers/serial/uart_nrfx_uarte.c

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL);
9595
#define UARTE_HAS_FRAME_TIMEOUT 1
9696
#endif
9797

98+
/* Frame timeout has a bug that countdown counter may not be triggered in some
99+
* specific condition. It may happen if RX is manually started after ENDRX (STOPRX
100+
* task was not triggered) and there is ongoing reception of a byte. RXDRDY event
101+
* triggered by the reception of that byte may not trigger frame timeout counter.
102+
* If this is the last byte of a transfer then without the workaround there will
103+
* be no expected RX timeout.
104+
*/
105+
#ifdef UARTE_HAS_FRAME_TIMEOUT
106+
#define RX_FRAMETIMEOUT_WORKAROUND 1
107+
#endif
108+
98109
#define INSTANCE_NEEDS_CACHE_MGMT(unused, prefix, i, prop) UARTE_IS_CACHEABLE(prefix##i)
99110

100111
#if UARTE_FOR_EACH_INSTANCE(INSTANCE_NEEDS_CACHE_MGMT, (+), (0), _)
@@ -251,6 +262,8 @@ struct uarte_nrfx_data {
251262
#define UARTE_FLAG_LOW_POWER (UARTE_FLAG_LOW_POWER_TX | UARTE_FLAG_LOW_POWER_RX)
252263
#define UARTE_FLAG_TRIG_RXTO BIT(2)
253264
#define UARTE_FLAG_POLL_OUT BIT(3)
265+
/* Flag indicating that a workaround for not working frame timeout is active. */
266+
#define UARTE_FLAG_FTIMEOUT_WATCH BIT(4)
254267

255268
/* If enabled then ENDTX is PPI'ed to TXSTOP */
256269
#define UARTE_CFG_FLAG_PPI_ENDTX BIT(0)
@@ -1317,9 +1330,22 @@ static void rx_timeout(struct k_timer *timer)
13171330
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
13181331

13191332
#ifdef UARTE_HAS_FRAME_TIMEOUT
1320-
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) {
1321-
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
1333+
struct uarte_nrfx_data *data = dev->data;
1334+
struct uarte_async_rx *async_rx = &data->async->rx;
1335+
bool rxdrdy = nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY);
1336+
1337+
if (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND) &&
1338+
(atomic_and(&data->flags, ~UARTE_FLAG_FTIMEOUT_WATCH) & UARTE_FLAG_FTIMEOUT_WATCH)) {
1339+
if (rxdrdy) {
1340+
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
1341+
k_timer_start(&async_rx->timer, async_rx->timeout, K_NO_WAIT);
1342+
}
1343+
} else {
1344+
if (!rxdrdy) {
1345+
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
1346+
}
13221347
}
1348+
13231349
return;
13241350
#else /* UARTE_HAS_FRAME_TIMEOUT */
13251351
struct uarte_nrfx_data *data = dev->data;
@@ -1545,6 +1571,7 @@ static void endrx_isr(const struct device *dev)
15451571
async_rx->offset = 0;
15461572

15471573
if (async_rx->enabled) {
1574+
bool start_timeout = false;
15481575
/* If there is a next buffer, then STARTRX will have already been
15491576
* invoked by the short (the next buffer will be filling up already)
15501577
* and here we just do the swap of which buffer the driver is following,
@@ -1570,6 +1597,11 @@ static void endrx_isr(const struct device *dev)
15701597
*/
15711598
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
15721599
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
1600+
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
1601+
if (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND)) {
1602+
data->flags |= UARTE_FLAG_FTIMEOUT_WATCH;
1603+
start_timeout = true;
1604+
}
15731605
}
15741606
/* Remove the short until the subsequent next buffer is setup */
15751607
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
@@ -1578,6 +1610,11 @@ static void endrx_isr(const struct device *dev)
15781610
}
15791611

15801612
irq_unlock(key);
1613+
if (IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) {
1614+
if (start_timeout && !K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
1615+
k_timer_start(&async_rx->timer, async_rx->timeout, K_NO_WAIT);
1616+
}
1617+
}
15811618
}
15821619

15831620
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
@@ -1635,6 +1672,12 @@ static void rxto_isr(const struct device *dev)
16351672
struct uarte_nrfx_data *data = dev->data;
16361673
struct uarte_async_rx *async_rx = &data->async->rx;
16371674

1675+
if (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND)) {
1676+
if (atomic_test_and_clear_bit(&data->flags, UARTE_FLAG_FTIMEOUT_WATCH)) {
1677+
k_timer_stop(&async_rx->timer);
1678+
}
1679+
}
1680+
16381681
if (async_rx->buf) {
16391682
#ifdef CONFIG_HAS_NORDIC_DMM
16401683
(void)dmm_buffer_in_release(config->mem_reg, async_rx->usr_buf, 0, async_rx->buf);

0 commit comments

Comments
 (0)