Skip to content

Commit

Permalink
drivers: serial: nrfx_uarte: Add support for frame timeout
Browse files Browse the repository at this point in the history
Frame timeout is a hardware feature present in newer versions
of UARTE (e.g. in NRF54X platforms) for detecting idle state
on RX line and ending RX after configurable timeout.

Signed-off-by: Krzysztof Chruściński <[email protected]>
  • Loading branch information
nordic-krch committed Oct 15, 2024
1 parent 3366b30 commit 64e292f
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 10 deletions.
2 changes: 0 additions & 2 deletions drivers/serial/Kconfig.nrfx
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,8 @@ config UART_NRFX_UARTE
config UART_NRFX_UARTE_LEGACY_SHIM
bool "Legacy UARTE shim"
depends on UART_NRFX_UARTE
depends on !SOC_SERIES_NRF54LX
depends on !SOC_SERIES_NRF54HX || RISCV
depends on !SOC_SERIES_NRF92X || RISCV
# New shim takes more ROM. Until it is fixed use legacy shim.
default y

config UART_NRFX_UARTE_ENHANCED_RX
Expand Down
67 changes: 59 additions & 8 deletions drivers/serial/uart_nrfx_uarte.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, 0);
#define UARTE_HAS_ENDTX_STOPTX_SHORT 1
#endif

#if (UARTE_FOR_EACH_INSTANCE(INSTANCE_PROP, (+), (0), frame_timeout_supported)) == \
(UARTE_FOR_EACH_INSTANCE(INSTANCE_PRESENT, (+), (0), frame_timeout_supported))

Check notice on line 90 in drivers/serial/uart_nrfx_uarte.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/serial/uart_nrfx_uarte.c:90 -#if (UARTE_FOR_EACH_INSTANCE(INSTANCE_PROP, (+), (0), frame_timeout_supported)) == \ +#if (UARTE_FOR_EACH_INSTANCE(INSTANCE_PROP, (+), (0), frame_timeout_supported)) == \
#define UARTE_HAS_FRAME_TIMEOUT 1
#endif

/*
* RX timeout is divided into time slabs, this define tells how many divisions
* should be made. More divisions - higher timeout accuracy and processor usage.
Expand Down Expand Up @@ -116,7 +121,9 @@ struct uarte_async_rx {
uint8_t *next_buf;
size_t next_buf_len;
#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
#if !defined(UARTE_HAS_FRAME_TIMEOUT)
uint32_t idle_cnt;
#endif
k_timeout_t timeout;
#else
uint32_t total_byte_cnt; /* Total number of bytes received */
Expand Down Expand Up @@ -230,6 +237,9 @@ struct uarte_nrfx_config {
/* None-zero in case of high speed instances. Baudrate is adjusted by that ratio. */
uint32_t clock_freq;
#else
#ifdef UARTE_HAS_FRAME_TIMEOUT
uint32_t baudrate;
#endif
nrf_uarte_baudrate_t nrf_baudrate;
nrf_uarte_config_t hw_config;
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
Expand Down Expand Up @@ -376,10 +386,6 @@ static int uarte_nrfx_configure(const struct device *dev,
struct uarte_nrfx_data *data = dev->data;
nrf_uarte_config_t uarte_cfg;

#if NRF_UARTE_HAS_FRAME_TIMEOUT
uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_DIS;
#endif

#if defined(UARTE_CONFIG_STOP_Msk)
switch (cfg->stop_bits) {
case UART_CFG_STOP_BITS_1:
Expand Down Expand Up @@ -436,6 +442,9 @@ static int uarte_nrfx_configure(const struct device *dev,
return -ENOTSUP;
}

#ifdef UARTE_HAS_FRAME_TIMEOUT
uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_EN;
#endif
nrf_uarte_configure(get_uarte_instance(dev), &uarte_cfg);

data->uart_config = *cfg;
Expand Down Expand Up @@ -672,7 +681,8 @@ static int uarte_nrfx_init(const struct device *dev)
NRF_UARTE_INT_RXSTARTED_MASK |
NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK |
(IS_ENABLED(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) ? NRF_UARTE_INT_RXDRDY_MASK : 0);
((IS_ENABLED(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) &&
!IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) ? NRF_UARTE_INT_RXDRDY_MASK : 0);

Check notice on line 686 in drivers/serial/uart_nrfx_uarte.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/serial/uart_nrfx_uarte.c:686 - static const uint32_t rx_int_mask = - NRF_UARTE_INT_ENDRX_MASK | - NRF_UARTE_INT_RXSTARTED_MASK | - NRF_UARTE_INT_ERROR_MASK | - NRF_UARTE_INT_RXTO_MASK | - ((IS_ENABLED(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) && - !IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) ? NRF_UARTE_INT_RXDRDY_MASK : 0); + static const uint32_t rx_int_mask = NRF_UARTE_INT_ENDRX_MASK | + NRF_UARTE_INT_RXSTARTED_MASK | + NRF_UARTE_INT_ERROR_MASK | NRF_UARTE_INT_RXTO_MASK | + ((IS_ENABLED(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) && + !IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) + ? NRF_UARTE_INT_RXDRDY_MASK + : 0);
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
int ret = uarte_nrfx_rx_counting_init(dev);
Expand Down Expand Up @@ -839,6 +849,15 @@ static void notify_rx_disable(const struct device *dev)
user_callback(dev, (struct uart_event *)&evt);
}

#ifdef UARTE_HAS_FRAME_TIMEOUT
static uint32_t us_to_bauds(uint32_t baudrate, int32_t timeout)
{
uint64_t bauds = (uint64_t)baudrate * timeout / 1000000;

return MIN((uint32_t)bauds, UARTE_FRAMETIMEOUT_COUNTERTOP_Msk);
}
#endif

static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf,
size_t len,
int32_t timeout)
Expand All @@ -862,9 +881,22 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf,
}

#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
#ifdef UARTE_HAS_FRAME_TIMEOUT
if (timeout && (timeout != SYS_FOREVER_US)) {
uint32_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE,
(data->uart_config.baudrate), (cfg->baudrate));

async_rx->timeout = K_USEC(timeout);
nrf_uarte_frame_timeout_set(uarte, us_to_bauds(baudrate, timeout));
nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX);
} else {
async_rx->timeout = K_NO_WAIT;
}
#else
async_rx->timeout = ((timeout == 0) || timeout == SYS_FOREVER_US) ?
K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV);
async_rx->idle_cnt = 0;

Check notice on line 898 in drivers/serial/uart_nrfx_uarte.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/serial/uart_nrfx_uarte.c:898 - async_rx->timeout = ((timeout == 0) || timeout == SYS_FOREVER_US) ? - K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV); + async_rx->timeout = ((timeout == 0) || timeout == SYS_FOREVER_US) + ? K_NO_WAIT + : K_USEC(timeout / RX_TIMEOUT_DIV);
#endif
#else
async_rx->timeout_us = timeout;
async_rx->timeout_slab = timeout / RX_TIMEOUT_DIV;
Expand Down Expand Up @@ -1021,6 +1053,12 @@ static void rx_timeout(struct k_timer *timer)
#if CONFIG_UART_NRFX_UARTE_ENHANCED_RX
NRF_UARTE_Type *uarte = get_uarte_instance(dev);

#ifdef UARTE_HAS_FRAME_TIMEOUT
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
}
return;
#else /* UARTE_HAS_FRAME_TIMEOUT */
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;

Expand All @@ -1036,6 +1074,7 @@ static void rx_timeout(struct k_timer *timer)
}

k_timer_start(&async_rx->timer, async_rx->timeout, K_NO_WAIT);
#endif /* UARTE_HAS_FRAME_TIMEOUT */
#else /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
Expand Down Expand Up @@ -1149,6 +1188,7 @@ static void rxstarted_isr(const struct device *dev)
.type = UART_RX_BUF_REQUEST,
};

#ifndef UARTE_HAS_FRAME_TIMEOUT
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;

Expand All @@ -1166,6 +1206,7 @@ static void rxstarted_isr(const struct device *dev)
k_timer_start(&async_rx->timer, timeout, timeout);
}
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */
#endif /* !UARTE_HAS_FRAME_TIMEOUT */
user_callback(dev, &evt);
}

Expand Down Expand Up @@ -1365,7 +1406,9 @@ static void rxto_isr(const struct device *dev)

#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
NRF_UARTE_Type *uarte = get_uarte_instance(dev);

#ifdef UARTE_HAS_FRAME_TIMEOUT
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX);
#endif
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
#endif

Expand Down Expand Up @@ -1461,6 +1504,9 @@ static void txstopped_isr(const struct device *dev)

static void rxdrdy_isr(const struct device *dev)
{
#if !defined(UARTE_HAS_FRAME_TIMEOUT)
struct uarte_nrfx_data *data = dev->data;

#if defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
NRF_UARTE_Type *uarte = get_uarte_instance(dev);

Expand All @@ -1470,6 +1516,7 @@ static void rxdrdy_isr(const struct device *dev)
#else
data->async->rx.cnt.cnt++;
#endif
#endif /* !UARTE_HAS_FRAME_TIMEOUT */
}

static bool event_check_clear(NRF_UARTE_Type *uarte, nrf_uarte_event_t event,
Expand All @@ -1492,7 +1539,7 @@ static void uarte_nrfx_isr_async(const void *arg)
struct uarte_async_rx *async_rx = &data->async->rx;
uint32_t imask = nrf_uarte_int_enable_check(uarte, UINT32_MAX);

if (!HW_RX_COUNTING_ENABLED(config)
if (!(HW_RX_COUNTING_ENABLED(config) || IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT))
&& event_check_clear(uarte, NRF_UARTE_EVENT_RXDRDY, NRF_UARTE_INT_RXDRDY_MASK, imask)) {
rxdrdy_isr(dev);

Expand Down Expand Up @@ -2134,6 +2181,8 @@ static int uarte_nrfx_pm_action(const struct device *dev,
IF_ENABLED(UARTE_HAS_STOP_CONFIG, (.stop = NRF_UARTE_STOP_ONE,))\
IF_ENABLED(UARTE_ODD_PARITY_ALLOWED, \
(.paritytype = NRF_UARTE_PARITYTYPE_EVEN,)) \
IF_ENABLED(UARTE_HAS_FRAME_TIMEOUT, \
(.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_EN,)) \
}

/* Macro for setting zephyr specific configuration structures. */
Expand Down Expand Up @@ -2177,7 +2226,9 @@ static int uarte_nrfx_pm_action(const struct device *dev,
COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, \
(IF_ENABLED(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \
(.clock_freq = UARTE_GET_FREQ(idx),))), \
(.nrf_baudrate = UARTE_GET_BAUDRATE(idx), \
(IF_ENABLED(UARTE_HAS_FRAME_TIMEOUT, \
(.baudrate = UARTE_PROP(idx, current_speed),)) \
.nrf_baudrate = UARTE_GET_BAUDRATE(idx), \
.hw_config = UARTE_NRF_CONFIG(idx),)) \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx)), \
.uarte_regs = _CONCAT(NRF_UARTE, idx), \
Expand Down

0 comments on commit 64e292f

Please sign in to comment.