zephyr/drivers/serial/uart_nrfx_uarte.c
Krzysztof Chruściński c8d50e4ef1 drivers: serial: nrfx_uarte: Add support for non ISR PM mode
When fast UARTE instance is used (e.g. UARTE120 in nrf54h20), PM actions
are not ISR safe because they include communication over IPC so they can
only be called from the thread context. Extend driver to support both
PM modes. When non ISR mode is used then uart_rx_enable() and uart_tx()
will return error if they are called from ISR and resume operation
would need to be called because device is suspended. On completion,
driver is calling pm_device_runtime_put_async which can be called from
the ISR context.

Additionally, suspending in the TXSTOPPED and RXTO events has been
moved after user callback. It allows to support the case where
uart_rx_enable() or uart_tx() are called from that callback context.
Since suspending is called after returning from the callback it will
not trigger suspend action because API called in the callback context
will increment the usage counter (when pm_device_runtime_get() is
called).

Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
2024-12-09 22:05:03 +00:00

2523 lines
73 KiB
C

/*
* Copyright (c) 2018-2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Driver for Nordic Semiconductor nRF UARTE
*/
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <hal/nrf_uarte.h>
#include <nrfx_timer.h>
#include <zephyr/sys/util.h>
#include <zephyr/kernel.h>
#include <zephyr/cache.h>
#include <soc.h>
#include <dmm.h>
#include <helpers/nrfx_gppi.h>
#include <zephyr/linker/devicetree_regions.h>
#include <zephyr/irq.h>
#include <zephyr/logging/log.h>
#ifdef CONFIG_SOC_NRF54H20_GPD
#include <nrf/gpd.h>
#endif
LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL);
#define RX_FLUSH_WORKAROUND 1
#define UARTE(idx) DT_NODELABEL(uart##idx)
#define UARTE_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(UARTE(idx), prop)
#define UARTE_PROP(idx, prop) DT_PROP(UARTE(idx), prop)
#define UARTE_IS_CACHEABLE(idx) DMM_IS_REG_CACHEABLE(DT_PHANDLE(UARTE(idx), memory_regions))
/* Execute macro f(x) for all instances. */
#define UARTE_FOR_EACH_INSTANCE(f, sep, off_code, ...) \
NRFX_FOREACH_PRESENT(UARTE, f, sep, off_code, __VA_ARGS__)
/* Determine if any instance is using interrupt driven API. */
#define IS_INT_DRIVEN(unused, prefix, i, _) \
(IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i) && \
IS_ENABLED(CONFIG_UART_##prefix##i##_INTERRUPT_DRIVEN))
#if UARTE_FOR_EACH_INSTANCE(IS_INT_DRIVEN, (||), (0))
#define UARTE_INTERRUPT_DRIVEN 1
#endif
/* Determine if any instance is not using asynchronous API. */
#define IS_NOT_ASYNC(unused, prefix, i, _) \
(IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i) && \
!IS_ENABLED(CONFIG_UART_##prefix##i##_ASYNC))
#if UARTE_FOR_EACH_INSTANCE(IS_NOT_ASYNC, (||), (0))
#define UARTE_ANY_NONE_ASYNC 1
#endif
/* Determine if any instance is using asynchronous API. */
#define IS_ASYNC(unused, prefix, i, _) \
(IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i) && \
IS_ENABLED(CONFIG_UART_##prefix##i##_ASYNC))
#if UARTE_FOR_EACH_INSTANCE(IS_ASYNC, (||), (0))
#define UARTE_ANY_ASYNC 1
#endif
/* Determine if any instance is using asynchronous API with HW byte counting. */
#define IS_HW_ASYNC(unused, prefix, i, _) IS_ENABLED(CONFIG_UART_##prefix##i##_NRF_HW_ASYNC)
#if UARTE_FOR_EACH_INSTANCE(IS_HW_ASYNC, (||), (0))
#define UARTE_ANY_HW_ASYNC 1
#endif
/* Determine if any instance is using enhanced poll_out feature. */
#define IS_ENHANCED_POLL_OUT(unused, prefix, i, _) \
IS_ENABLED(CONFIG_UART_##prefix##i##_ENHANCED_POLL_OUT)
#if UARTE_FOR_EACH_INSTANCE(IS_ENHANCED_POLL_OUT, (||), (0))
#define UARTE_ENHANCED_POLL_OUT 1
#endif
#define INSTANCE_PROP(unused, prefix, i, prop) UARTE_PROP(prefix##i, prop)
#define INSTANCE_PRESENT(unused, prefix, i, prop) 1
/* Driver supports case when all or none instances support that HW feature. */
#if (UARTE_FOR_EACH_INSTANCE(INSTANCE_PROP, (+), (0), endtx_stoptx_supported)) == \
(UARTE_FOR_EACH_INSTANCE(INSTANCE_PRESENT, (+), (0), endtx_stoptx_supported))
#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))
#define UARTE_HAS_FRAME_TIMEOUT 1
#endif
#define INSTANCE_NEEDS_CACHE_MGMT(unused, prefix, i, prop) UARTE_IS_CACHEABLE(prefix##i)
#if UARTE_FOR_EACH_INSTANCE(INSTANCE_NEEDS_CACHE_MGMT, (+), (0), _)
#define UARTE_ANY_CACHE 1
#endif
#define IS_LOW_POWER(unused, prefix, i, _) IS_ENABLED(CONFIG_UART_##prefix##i##_NRF_ASYNC_LOW_POWER)
#if UARTE_FOR_EACH_INSTANCE(IS_LOW_POWER, (||), (0))
#define UARTE_ANY_LOW_POWER 1
#endif
BUILD_ASSERT(NRF_GPD_FAST_ACTIVE1 == 0);
/* Macro must resolve to literal 0 or 1 */
#define INSTANCE_IS_FAST(unused, prefix, idx, _) \
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(UARTE(idx)), \
(COND_CODE_1(UTIL_AND(IS_ENABLED(CONFIG_SOC_NRF54H20_GPD), \
DT_NODE_HAS_PROP(UARTE(idx), power_domains)), \
(COND_CODE_0(DT_PHA(UARTE(idx), power_domains, id), (1), (0))),\
(0))), (0))
#if UARTE_FOR_EACH_INSTANCE(INSTANCE_IS_FAST, (||), (0))
#define UARTE_ANY_FAST 1
#endif
#ifdef UARTE_ANY_CACHE
/* uart120 instance does not retain BAUDRATE register when ENABLE=0. When this instance
* is used then baudrate must be set after enabling the peripheral and not before.
* This approach works for all instances so can be generally applied when uart120 is used.
* It is not default for all because it costs some resources. Since currently only uart120
* needs cache, that is used to determine if workaround shall be applied.
*/
#define UARTE_BAUDRATE_RETENTION_WORKAROUND 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.
*/
#define RX_TIMEOUT_DIV 5
/* Size of hardware fifo in RX path. */
#define UARTE_HW_RX_FIFO_SIZE 5
#ifdef UARTE_ANY_ASYNC
struct uarte_async_tx {
struct k_timer timer;
const uint8_t *buf;
volatile size_t len;
const uint8_t *xfer_buf;
size_t xfer_len;
size_t cache_offset;
volatile int amount;
bool pending;
};
struct uarte_async_rx {
struct k_timer timer;
#ifdef CONFIG_HAS_NORDIC_DMM
uint8_t *usr_buf;
uint8_t *next_usr_buf;
#endif
uint8_t *buf;
size_t buf_len;
size_t offset;
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 */
uint32_t total_user_byte_cnt; /* Total number of bytes passed to user */
int32_t timeout_us; /* Timeout set by user */
int32_t timeout_slab; /* rx_timeout divided by RX_TIMEOUT_DIV */
int32_t timeout_left; /* Current time left until user callback */
union {
uint8_t ppi;
uint32_t cnt;
} cnt;
/* Flag to ensure that RX timeout won't be executed during ENDRX ISR */
volatile bool is_in_irq;
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */
uint8_t flush_cnt;
volatile bool enabled;
volatile bool discard_fifo;
};
struct uarte_async_cb {
uart_callback_t user_callback;
void *user_data;
struct uarte_async_rx rx;
struct uarte_async_tx tx;
};
#endif /* UARTE_ANY_ASYNC */
#ifdef UARTE_INTERRUPT_DRIVEN
struct uarte_nrfx_int_driven {
uart_irq_callback_user_data_t cb; /**< Callback function pointer */
void *cb_data; /**< Callback function arg */
uint8_t *tx_buffer;
uint16_t tx_buff_size;
volatile bool disable_tx_irq;
bool tx_irq_enabled;
#ifdef CONFIG_PM_DEVICE
bool rx_irq_enabled;
#endif
atomic_t fifo_fill_lock;
};
#endif
/* Device data structure */
struct uarte_nrfx_data {
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uart_config uart_config;
#ifdef UARTE_BAUDRATE_RETENTION_WORKAROUND
nrf_uarte_baudrate_t nrf_baudrate;
#endif
#endif
#ifdef UARTE_INTERRUPT_DRIVEN
struct uarte_nrfx_int_driven *int_driven;
#endif
#ifdef UARTE_ANY_ASYNC
struct uarte_async_cb *async;
#endif
atomic_val_t poll_out_lock;
atomic_t flags;
#ifdef UARTE_ENHANCED_POLL_OUT
uint8_t ppi_ch_endtx;
#endif
};
#define UARTE_FLAG_LOW_POWER_TX BIT(0)
#define UARTE_FLAG_LOW_POWER_RX BIT(1)
#define UARTE_FLAG_LOW_POWER (UARTE_FLAG_LOW_POWER_TX | UARTE_FLAG_LOW_POWER_RX)
#define UARTE_FLAG_TRIG_RXTO BIT(2)
#define UARTE_FLAG_POLL_OUT BIT(3)
/* If enabled then ENDTX is PPI'ed to TXSTOP */
#define UARTE_CFG_FLAG_PPI_ENDTX BIT(0)
/* If enabled then TIMER and PPI is used for byte counting. */
#define UARTE_CFG_FLAG_HW_BYTE_COUNTING BIT(1)
/* If enabled then UARTE peripheral is disabled when not used. This allows
* to achieve lowest power consumption in idle.
*/
#define UARTE_CFG_FLAG_LOW_POWER BIT(2)
/* If enabled then UARTE peripheral is using memory which is cacheable. */
#define UARTE_CFG_FLAG_CACHEABLE BIT(3)
/* Macro for converting numerical baudrate to register value. It is convenient
* to use this approach because for constant input it can calculate nrf setting
* at compile time.
*/
#define NRF_BAUDRATE(baudrate) ((baudrate) == 300 ? 0x00014000 :\
(baudrate) == 600 ? 0x00027000 : \
(baudrate) == 1200 ? NRF_UARTE_BAUDRATE_1200 : \
(baudrate) == 2400 ? NRF_UARTE_BAUDRATE_2400 : \
(baudrate) == 4800 ? NRF_UARTE_BAUDRATE_4800 : \
(baudrate) == 9600 ? NRF_UARTE_BAUDRATE_9600 : \
(baudrate) == 14400 ? NRF_UARTE_BAUDRATE_14400 : \
(baudrate) == 19200 ? NRF_UARTE_BAUDRATE_19200 : \
(baudrate) == 28800 ? NRF_UARTE_BAUDRATE_28800 : \
(baudrate) == 31250 ? NRF_UARTE_BAUDRATE_31250 : \
(baudrate) == 38400 ? NRF_UARTE_BAUDRATE_38400 : \
(baudrate) == 56000 ? NRF_UARTE_BAUDRATE_56000 : \
(baudrate) == 57600 ? NRF_UARTE_BAUDRATE_57600 : \
(baudrate) == 76800 ? NRF_UARTE_BAUDRATE_76800 : \
(baudrate) == 115200 ? NRF_UARTE_BAUDRATE_115200 : \
(baudrate) == 230400 ? NRF_UARTE_BAUDRATE_230400 : \
(baudrate) == 250000 ? NRF_UARTE_BAUDRATE_250000 : \
(baudrate) == 460800 ? NRF_UARTE_BAUDRATE_460800 : \
(baudrate) == 921600 ? NRF_UARTE_BAUDRATE_921600 : \
(baudrate) == 1000000 ? NRF_UARTE_BAUDRATE_1000000 : 0)
#define LOW_POWER_ENABLED(_config) \
(IS_ENABLED(UARTE_ANY_LOW_POWER) && \
!IS_ENABLED(CONFIG_PM_DEVICE) && \
(_config->flags & UARTE_CFG_FLAG_LOW_POWER))
/** @brief Check if device has PM that works in ISR safe mode.
*
* Only fast UARTE instance does not work in that mode so check PM configuration
* flags only if there is any fast instance present.
*
* @retval true if device PM is ISR safe.
* @retval false if device PM is not ISR safe.
*/
#define IS_PM_ISR_SAFE(dev) \
(!IS_ENABLED(UARTE_ANY_FAST) ||\
COND_CODE_1(CONFIG_PM_DEVICE,\
((dev->pm_base->flags & BIT(PM_DEVICE_FLAG_ISR_SAFE))), \
(0)))
/**
* @brief Structure for UARTE configuration.
*/
struct uarte_nrfx_config {
NRF_UARTE_Type *uarte_regs; /* Instance address */
uint32_t flags;
bool disable_rx;
const struct pinctrl_dev_config *pcfg;
#ifdef CONFIG_HAS_NORDIC_DMM
void *mem_reg;
#endif
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
/* 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 */
#ifdef UARTE_ANY_ASYNC
nrfx_timer_t timer;
uint8_t *tx_cache;
uint8_t *rx_flush_buf;
#endif
uint8_t *poll_out_byte;
uint8_t *poll_in_byte;
};
static inline NRF_UARTE_Type *get_uarte_instance(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
return config->uarte_regs;
}
static void endtx_isr(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
unsigned int key = irq_lock();
if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX);
}
irq_unlock(key);
}
#ifdef UARTE_ANY_NONE_ASYNC
/**
* @brief Interrupt service routine.
*
* This simply calls the callback function, if one exists.
*
* @param arg Argument to ISR.
*/
static void uarte_nrfx_isr_int(const void *arg)
{
const struct device *dev = arg;
const struct uarte_nrfx_config *config = dev->config;
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
/* If interrupt driven and asynchronous APIs are disabled then UART
* interrupt is still called to stop TX. Unless it is done using PPI.
*/
if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT) &&
nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDTX_MASK) &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) {
endtx_isr(dev);
}
bool txstopped = nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED);
if (txstopped && (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(config))) {
unsigned int key = irq_lock();
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
if (data->flags & UARTE_FLAG_POLL_OUT) {
data->flags &= ~UARTE_FLAG_POLL_OUT;
pm_device_runtime_put_async(dev, K_NO_WAIT);
}
} else {
nrf_uarte_disable(uarte);
}
#ifdef UARTE_INTERRUPT_DRIVEN
if (!data->int_driven)
#endif
{
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
}
irq_unlock(key);
}
#ifdef UARTE_INTERRUPT_DRIVEN
if (!data->int_driven) {
return;
}
if (txstopped) {
data->int_driven->fifo_fill_lock = 0;
if (!data->int_driven->tx_irq_enabled) {
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
}
if (data->int_driven->disable_tx_irq) {
data->int_driven->disable_tx_irq = false;
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_put_async(dev, K_NO_WAIT);
}
return;
}
}
if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ERROR)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ERROR);
}
if (data->int_driven->cb) {
data->int_driven->cb(dev, data->int_driven->cb_data);
}
#endif /* UARTE_INTERRUPT_DRIVEN */
}
#endif /* UARTE_ANY_NONE_ASYNC */
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
/**
* @brief Set the baud rate
*
* This routine set the given baud rate for the UARTE.
*
* @param dev UARTE device struct
* @param baudrate Baud rate
*
* @return 0 on success or error code
*/
static int baudrate_set(const struct device *dev, uint32_t baudrate)
{
const struct uarte_nrfx_config *config = dev->config;
/* calculated baudrate divisor */
nrf_uarte_baudrate_t nrf_baudrate = NRF_BAUDRATE(baudrate);
if (nrf_baudrate == 0) {
return -EINVAL;
}
/* scale baudrate setting */
if (config->clock_freq > 0U) {
nrf_baudrate /= config->clock_freq / NRF_UARTE_BASE_FREQUENCY_16MHZ;
}
#ifdef UARTE_BAUDRATE_RETENTION_WORKAROUND
struct uarte_nrfx_data *data = dev->data;
data->nrf_baudrate = nrf_baudrate;
#else
nrf_uarte_baudrate_set(get_uarte_instance(dev), nrf_baudrate);
#endif
return 0;
}
static int uarte_nrfx_configure(const struct device *dev,
const struct uart_config *cfg)
{
struct uarte_nrfx_data *data = dev->data;
nrf_uarte_config_t uarte_cfg;
#if defined(UARTE_CONFIG_STOP_Msk)
switch (cfg->stop_bits) {
case UART_CFG_STOP_BITS_1:
uarte_cfg.stop = NRF_UARTE_STOP_ONE;
break;
case UART_CFG_STOP_BITS_2:
uarte_cfg.stop = NRF_UARTE_STOP_TWO;
break;
default:
return -ENOTSUP;
}
#else
if (cfg->stop_bits != UART_CFG_STOP_BITS_1) {
return -ENOTSUP;
}
#endif
if (cfg->data_bits != UART_CFG_DATA_BITS_8) {
return -ENOTSUP;
}
switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE:
uarte_cfg.hwfc = NRF_UARTE_HWFC_DISABLED;
break;
case UART_CFG_FLOW_CTRL_RTS_CTS:
uarte_cfg.hwfc = NRF_UARTE_HWFC_ENABLED;
break;
default:
return -ENOTSUP;
}
#if defined(UARTE_CONFIG_PARITYTYPE_Msk)
uarte_cfg.paritytype = NRF_UARTE_PARITYTYPE_EVEN;
#endif
switch (cfg->parity) {
case UART_CFG_PARITY_NONE:
uarte_cfg.parity = NRF_UARTE_PARITY_EXCLUDED;
break;
case UART_CFG_PARITY_EVEN:
uarte_cfg.parity = NRF_UARTE_PARITY_INCLUDED;
break;
#if defined(UARTE_CONFIG_PARITYTYPE_Msk)
case UART_CFG_PARITY_ODD:
uarte_cfg.parity = NRF_UARTE_PARITY_INCLUDED;
uarte_cfg.paritytype = NRF_UARTE_PARITYTYPE_ODD;
break;
#endif
default:
return -ENOTSUP;
}
if (baudrate_set(dev, cfg->baudrate) != 0) {
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;
return 0;
}
static int uarte_nrfx_config_get(const struct device *dev,
struct uart_config *cfg)
{
struct uarte_nrfx_data *data = dev->data;
*cfg = data->uart_config;
return 0;
}
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
static int uarte_nrfx_err_check(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
/* register bitfields maps to the defines in uart.h */
return nrf_uarte_errorsrc_get_and_clear(uarte);
}
/* Function returns true if new transfer can be started. Since TXSTOPPED
* (and ENDTX) is cleared before triggering new transfer, TX is ready for new
* transfer if any event is set.
*/
static bool is_tx_ready(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
bool ppi_endtx = config->flags & UARTE_CFG_FLAG_PPI_ENDTX ||
IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT);
return nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED) ||
(!ppi_endtx ?
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX) : 0);
}
/* Wait until the transmitter is in the idle state. When this function returns,
* IRQ's are locked with the returned key.
*/
static int wait_tx_ready(const struct device *dev)
{
unsigned int key;
do {
/* wait arbitrary time before back off. */
bool res;
#if defined(CONFIG_ARCH_POSIX)
NRFX_WAIT_FOR(is_tx_ready(dev), 33, 3, res);
#else
NRFX_WAIT_FOR(is_tx_ready(dev), 100, 1, res);
#endif
if (res) {
key = irq_lock();
if (is_tx_ready(dev)) {
break;
}
irq_unlock(key);
}
if (IS_ENABLED(CONFIG_MULTITHREADING)) {
k_msleep(1);
}
} while (1);
return key;
}
/* Using Macro instead of static inline function to handle NO_OPTIMIZATIONS case
* where static inline fails on linking.
*/
#define HW_RX_COUNTING_ENABLED(config) \
(IS_ENABLED(UARTE_ANY_HW_ASYNC) ? \
(config->flags & UARTE_CFG_FLAG_HW_BYTE_COUNTING) : false)
static void uarte_periph_enable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *config = dev->config;
struct uarte_nrfx_data *data = dev->data;
(void)data;
nrf_uarte_enable(uarte);
#ifdef CONFIG_SOC_NRF54H20_GPD
nrf_gpd_retain_pins_set(config->pcfg, false);
#endif
#if UARTE_BAUDRATE_RETENTION_WORKAROUND
nrf_uarte_baudrate_set(uarte,
COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE,
(data->nrf_baudrate), (config->nrf_baudrate)));
#endif
#ifdef UARTE_ANY_ASYNC
if (data->async) {
if (HW_RX_COUNTING_ENABLED(config)) {
const nrfx_timer_t *timer = &config->timer;
nrfx_timer_enable(timer);
for (int i = 0; i < data->async->rx.flush_cnt; i++) {
nrfx_timer_increment(timer);
}
}
return;
}
#endif
if (IS_ENABLED(UARTE_ANY_NONE_ASYNC) && !config->disable_rx) {
nrf_uarte_rx_buffer_set(uarte, config->poll_in_byte, 1);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
#if defined(UARTE_INTERRUPT_DRIVEN) && defined(CONFIG_PM_DEVICE)
if (data->int_driven && data->int_driven->rx_irq_enabled) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDRX_MASK);
}
#endif
}
}
static void uarte_enable_locked(const struct device *dev, uint32_t act_mask)
{
struct uarte_nrfx_data *data = dev->data;
bool already_active = (data->flags & UARTE_FLAG_LOW_POWER) != 0;
data->flags |= act_mask;
if (already_active) {
/* Second direction already enabled so UARTE is enabled. */
return;
}
uarte_periph_enable(dev);
}
/* At this point we should have irq locked and any previous transfer completed.
* Transfer can be started, no need to wait for completion.
*/
static void tx_start(const struct device *dev, const uint8_t *buf, size_t len)
{
const struct uarte_nrfx_config *config = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
#if defined(CONFIG_PM_DEVICE) && !defined(CONFIG_PM_DEVICE_RUNTIME)
enum pm_device_state state;
(void)pm_device_state_get(dev, &state);
if (state != PM_DEVICE_STATE_ACTIVE) {
return;
}
#endif
if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_flush_range((void *)buf, len);
}
nrf_uarte_tx_buffer_set(uarte, buf, len);
if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX);
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_TXSTOPPED);
if (LOW_POWER_ENABLED(config)) {
uarte_enable_locked(dev, UARTE_FLAG_LOW_POWER_TX);
}
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTTX);
}
#if defined(UARTE_ANY_ASYNC)
/** @brief Disable UARTE peripheral is not used by RX or TX.
*
* It must be called with interrupts locked so that deciding if no direction is
* using the UARTE is atomically performed with UARTE peripheral disabling. Otherwise
* it would be possible that after clearing flags we get preempted and UARTE is
* enabled from the higher priority context and when we come back UARTE is disabled
* here.
* @param dev Device.
* @param dis_mask Mask of direction (RX or TX) which now longer uses the UARTE instance.
*/
static void uarte_disable_locked(const struct device *dev, uint32_t dis_mask)
{
struct uarte_nrfx_data *data = dev->data;
data->flags &= ~dis_mask;
if (data->flags & UARTE_FLAG_LOW_POWER) {
return;
}
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
const struct uarte_nrfx_config *config = dev->config;
if (data->async && HW_RX_COUNTING_ENABLED(config)) {
nrfx_timer_disable(&config->timer);
/* Timer/counter value is reset when disabled. */
data->async->rx.total_byte_cnt = 0;
data->async->rx.total_user_byte_cnt = 0;
}
#endif
#ifdef CONFIG_SOC_NRF54H20_GPD
const struct uarte_nrfx_config *cfg = dev->config;
nrf_gpd_retain_pins_set(cfg->pcfg, true);
#endif
nrf_uarte_disable(get_uarte_instance(dev));
}
static void rx_timeout(struct k_timer *timer);
static void tx_timeout(struct k_timer *timer);
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
static void timer_handler(nrf_timer_event_t event_type, void *p_context) { }
static int uarte_nrfx_rx_counting_init(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
const struct uarte_nrfx_config *cfg = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
int ret;
if (HW_RX_COUNTING_ENABLED(cfg)) {
nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG(
NRF_TIMER_BASE_FREQUENCY_GET(cfg->timer.p_reg));
uint32_t evt_addr = nrf_uarte_event_address_get(uarte, NRF_UARTE_EVENT_RXDRDY);
uint32_t tsk_addr = nrfx_timer_task_address_get(&cfg->timer, NRF_TIMER_TASK_COUNT);
tmr_config.mode = NRF_TIMER_MODE_COUNTER;
tmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
ret = nrfx_timer_init(&cfg->timer,
&tmr_config,
timer_handler);
if (ret != NRFX_SUCCESS) {
LOG_ERR("Timer already initialized");
return -EINVAL;
} else {
nrfx_timer_clear(&cfg->timer);
}
ret = nrfx_gppi_channel_alloc(&data->async->rx.cnt.ppi);
if (ret != NRFX_SUCCESS) {
LOG_ERR("Failed to allocate PPI Channel");
nrfx_timer_uninit(&cfg->timer);
return -EINVAL;
}
nrfx_gppi_channel_endpoints_setup(data->async->rx.cnt.ppi, evt_addr, tsk_addr);
nrfx_gppi_channels_enable(BIT(data->async->rx.cnt.ppi));
} else {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_RXDRDY_MASK);
}
return 0;
}
#endif /* !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) */
static int uarte_async_init(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
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);
if (ret != 0) {
return ret;
}
#endif
nrf_uarte_int_enable(uarte, rx_int_mask);
k_timer_init(&data->async->rx.timer, rx_timeout, NULL);
k_timer_user_data_set(&data->async->rx.timer, (void *)dev);
k_timer_init(&data->async->tx.timer, tx_timeout, NULL);
k_timer_user_data_set(&data->async->tx.timer, (void *)dev);
return 0;
}
/* Attempt to start TX (asynchronous transfer). If hardware is not ready, then pending
* flag is set. When current poll_out is completed, pending transfer is started.
* Function must be called with interrupts locked.
*/
static void start_tx_locked(const struct device *dev, struct uarte_nrfx_data *data)
{
nrf_uarte_int_enable(get_uarte_instance(dev), NRF_UARTE_INT_TXSTOPPED_MASK);
if (!is_tx_ready(dev)) {
/* Active poll out, postpone until it is completed. */
data->async->tx.pending = true;
} else {
data->async->tx.pending = false;
data->async->tx.amount = -1;
tx_start(dev, data->async->tx.xfer_buf, data->async->tx.xfer_len);
}
}
/* Setup cache buffer (used for sending data outside of RAM memory).
* During setup data is copied to cache buffer and transfer length is set.
*
* @return True if cache was set, false if no more data to put in cache.
*/
static bool setup_tx_cache(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
const struct uarte_nrfx_config *config = dev->config;
size_t remaining = data->async->tx.len - data->async->tx.cache_offset;
if (!remaining) {
return false;
}
size_t len = MIN(remaining, CONFIG_UART_ASYNC_TX_CACHE_SIZE);
data->async->tx.xfer_len = len;
data->async->tx.xfer_buf = config->tx_cache;
memcpy(config->tx_cache, &data->async->tx.buf[data->async->tx.cache_offset], len);
return true;
}
static bool has_hwfc(const struct device *dev)
{
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uarte_nrfx_data *data = dev->data;
return data->uart_config.flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS;
#else
const struct uarte_nrfx_config *config = dev->config;
return config->hw_config.hwfc == NRF_UARTE_HWFC_ENABLED;
#endif
}
static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf,
size_t len,
int32_t timeout)
{
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
unsigned int key = irq_lock();
if (data->async->tx.len) {
irq_unlock(key);
return -EBUSY;
}
data->async->tx.len = len;
data->async->tx.buf = buf;
if (nrf_dma_accessible_check(uarte, buf)) {
data->async->tx.xfer_buf = buf;
data->async->tx.xfer_len = len;
} else {
data->async->tx.cache_offset = 0;
(void)setup_tx_cache(dev);
}
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
/* If instance does not support PM from ISR device shall
* already be turned on.
*/
enum pm_device_state state;
int err;
err = pm_device_state_get(dev, &state);
(void)err;
__ASSERT_NO_MSG(err == 0);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -ENOTSUP;
}
}
pm_device_runtime_get(dev);
}
start_tx_locked(dev, data);
irq_unlock(key);
if (has_hwfc(dev) && timeout != SYS_FOREVER_US) {
k_timer_start(&data->async->tx.timer, K_USEC(timeout), K_NO_WAIT);
}
return 0;
}
static int uarte_nrfx_tx_abort(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
if (data->async->tx.buf == NULL) {
return -EFAULT;
}
data->async->tx.pending = false;
k_timer_stop(&data->async->tx.timer);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX);
return 0;
}
static void user_callback(const struct device *dev, struct uart_event *evt)
{
struct uarte_nrfx_data *data = dev->data;
if (data->async->user_callback) {
data->async->user_callback(dev, evt, data->async->user_data);
}
}
static void notify_uart_rx_rdy(const struct device *dev, size_t len)
{
struct uarte_nrfx_data *data = dev->data;
struct uart_event evt = {
.type = UART_RX_RDY,
.data.rx.buf = data->async->rx.buf,
.data.rx.len = len,
.data.rx.offset = data->async->rx.offset
};
user_callback(dev, &evt);
}
static void rx_buf_release(const struct device *dev, uint8_t *buf)
{
struct uart_event evt = {
.type = UART_RX_BUF_RELEASED,
.data.rx_buf.buf = buf,
};
user_callback(dev, &evt);
}
static void notify_rx_disable(const struct device *dev)
{
struct uart_event evt = {
.type = UART_RX_DISABLED,
};
user_callback(dev, (struct uart_event *)&evt);
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_put_async(dev, K_NO_WAIT);
}
}
#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)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
const struct uarte_nrfx_config *cfg = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
if (cfg->disable_rx) {
__ASSERT(false, "TX only UARTE instance");
return -ENOTSUP;
}
/* Signal error if RX is already enabled or if the driver is waiting
* for the RXTO event after a call to uart_rx_disable() to discard
* data from the UARTE internal RX FIFO.
*/
if (async_rx->enabled || async_rx->discard_fifo) {
return -EBUSY;
}
#ifdef CONFIG_HAS_NORDIC_DMM
uint8_t *dma_buf;
int ret = 0;
ret = dmm_buffer_in_prepare(cfg->mem_reg, buf, len, (void **)&dma_buf);
if (ret < 0) {
return ret;
}
async_rx->usr_buf = buf;
buf = dma_buf;
#endif
#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
#ifdef UARTE_HAS_FRAME_TIMEOUT
if (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 == SYS_FOREVER_US) ?
K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV);
async_rx->idle_cnt = 0;
#endif /* UARTE_HAS_FRAME_TIMEOUT */
#else
async_rx->timeout_us = timeout;
async_rx->timeout_slab = timeout / RX_TIMEOUT_DIV;
#endif
async_rx->buf = buf;
async_rx->buf_len = len;
async_rx->offset = 0;
async_rx->next_buf = NULL;
async_rx->next_buf_len = 0;
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
/* If instance does not support PM from ISR device shall
* already be turned on.
*/
enum pm_device_state state;
int err;
err = pm_device_state_get(dev, &state);
(void)err;
__ASSERT_NO_MSG(err == 0);
if (state != PM_DEVICE_STATE_ACTIVE) {
return -ENOTSUP;
}
}
pm_device_runtime_get(dev);
}
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(cfg)) {
if (async_rx->flush_cnt) {
int cpy_len = MIN(len, async_rx->flush_cnt);
if (IS_ENABLED(UARTE_ANY_CACHE) &&
(cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(cfg->rx_flush_buf, cpy_len);
}
memcpy(buf, cfg->rx_flush_buf, cpy_len);
if (IS_ENABLED(UARTE_ANY_CACHE) &&
(cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_flush_range(buf, cpy_len);
}
buf += cpy_len;
len -= cpy_len;
/* If flush content filled whole new buffer trigger interrupt
* to notify about received data and disabled RX from there.
*/
if (!len) {
async_rx->flush_cnt -= cpy_len;
memmove(cfg->rx_flush_buf, &cfg->rx_flush_buf[cpy_len],
async_rx->flush_cnt);
if (IS_ENABLED(UARTE_ANY_CACHE) &&
(cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_flush_range(cfg->rx_flush_buf,
async_rx->flush_cnt);
}
atomic_or(&data->flags, UARTE_FLAG_TRIG_RXTO);
NRFX_IRQ_PENDING_SET(nrfx_get_irq_number(uarte));
return 0;
} else {
#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
if (!K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
k_timer_start(&async_rx->timer, async_rx->timeout,
K_NO_WAIT);
}
#endif
}
}
}
nrf_uarte_rx_buffer_set(uarte, buf, len);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
async_rx->enabled = true;
if (LOW_POWER_ENABLED(cfg)) {
unsigned int key = irq_lock();
uarte_enable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
irq_unlock(key);
}
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
return 0;
}
static int uarte_nrfx_rx_buf_rsp(const struct device *dev, uint8_t *buf,
size_t len)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
int err;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
unsigned int key = irq_lock();
if (async_rx->buf == NULL) {
err = -EACCES;
} else if (async_rx->next_buf == NULL) {
#ifdef CONFIG_HAS_NORDIC_DMM
uint8_t *dma_buf;
const struct uarte_nrfx_config *config = dev->config;
err = dmm_buffer_in_prepare(config->mem_reg, buf, len, (void **)&dma_buf);
if (err < 0) {
return err;
}
async_rx->next_usr_buf = buf;
buf = dma_buf;
#endif
async_rx->next_buf = buf;
async_rx->next_buf_len = len;
nrf_uarte_rx_buffer_set(uarte, buf, len);
/* If buffer is shorter than RX FIFO then there is a risk that due
* to interrupt handling latency ENDRX event is not handled on time
* and due to ENDRX_STARTRX short data will start to be overwritten.
* In that case short is not enabled and ENDRX event handler will
* manually start RX for that buffer. Thanks to RX FIFO there is
* 5 byte time for doing that. If interrupt latency is higher and
* there is no HWFC in both cases data will be lost or corrupted.
*/
if (len >= UARTE_HW_RX_FIFO_SIZE) {
nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
err = 0;
} else {
err = -EBUSY;
}
irq_unlock(key);
return err;
}
static int uarte_nrfx_callback_set(const struct device *dev,
uart_callback_t callback,
void *user_data)
{
struct uarte_nrfx_data *data = dev->data;
if (!data->async) {
return -ENOTSUP;
}
data->async->user_callback = callback;
data->async->user_data = user_data;
return 0;
}
static int uarte_nrfx_rx_disable(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
int key;
if (async_rx->buf == NULL) {
return -EFAULT;
}
k_timer_stop(&async_rx->timer);
key = irq_lock();
if (async_rx->next_buf != NULL) {
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
}
async_rx->enabled = false;
async_rx->discard_fifo = true;
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
irq_unlock(key);
return 0;
}
static void tx_timeout(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(timer);
(void) uarte_nrfx_tx_abort(dev);
}
/**
* Whole timeout is divided by RX_TIMEOUT_DIV into smaller units, rx_timeout
* is executed periodically every rx_timeout_slab us. If between executions
* data was received, then we start counting down time from start, if not, then
* we subtract rx_timeout_slab from rx_timeout_left.
* If rx_timeout_left is less than rx_timeout_slab it means that receiving has
* timed out and we should tell user about that.
*/
static void rx_timeout(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(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;
if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
async_rx->idle_cnt = 0;
} else {
async_rx->idle_cnt++;
/* We compare against RX_TIMEOUT_DIV - 1 to get rather earlier timeout
* than late. idle_cnt is reset when last RX activity (RXDRDY event) is
* detected. It may happen that it happens when RX is inactive for whole
* RX timeout period (and it is the case when transmission is short compared
* to the timeout, for example timeout is 50 ms and transmission of few bytes
* takes less than 1ms). In that case if we compare against RX_TIMEOUT_DIV
* then RX notification would come after (RX_TIMEOUT_DIV + 1) * timeout.
*/
if (async_rx->idle_cnt == (RX_TIMEOUT_DIV - 1)) {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
return;
}
}
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;
struct uarte_async_rx *async_rx = &data->async->rx;
uint32_t read;
if (async_rx->is_in_irq) {
return;
}
/* Disable ENDRX ISR, in case ENDRX event is generated, it will be
* handled after rx_timeout routine is complete.
*/
nrf_uarte_int_disable(get_uarte_instance(dev),
NRF_UARTE_INT_ENDRX_MASK);
if (HW_RX_COUNTING_ENABLED(cfg)) {
read = nrfx_timer_capture(&cfg->timer, 0);
} else {
read = async_rx->cnt.cnt;
}
/* Check if data was received since last function call */
if (read != async_rx->total_byte_cnt) {
async_rx->total_byte_cnt = read;
async_rx->timeout_left = async_rx->timeout_us;
}
/* Check if there is data that was not sent to user yet
* Note though that 'len' is a count of data bytes received, but not
* necessarily the amount available in the current buffer
*/
int32_t len = async_rx->total_byte_cnt - async_rx->total_user_byte_cnt;
if (!HW_RX_COUNTING_ENABLED(cfg) &&
(len < 0)) {
/* Prevent too low value of rx_cnt.cnt which may occur due to
* latencies in handling of the RXRDY interrupt.
* At this point, the number of received bytes is at least
* equal to what was reported to the user.
*/
async_rx->cnt.cnt = async_rx->total_user_byte_cnt;
len = 0;
}
/* Check for current buffer being full.
* if the UART receives characters before the ENDRX is handled
* and the 'next' buffer is set up, then the SHORT between ENDRX and
* STARTRX will mean that data will be going into to the 'next' buffer
* until the ENDRX event gets a chance to be handled.
*/
bool clipped = false;
if (len + async_rx->offset > async_rx->buf_len) {
len = async_rx->buf_len - async_rx->offset;
clipped = true;
}
if (len > 0) {
if (clipped || (async_rx->timeout_left < async_rx->timeout_slab)) {
/* rx_timeout us elapsed since last receiving */
if (async_rx->buf != NULL) {
notify_uart_rx_rdy(dev, len);
async_rx->offset += len;
async_rx->total_user_byte_cnt += len;
}
} else {
async_rx->timeout_left -= async_rx->timeout_slab;
}
/* If there's nothing left to report until the buffers are
* switched then the timer can be stopped
*/
if (clipped) {
k_timer_stop(&async_rx->timer);
}
}
nrf_uarte_int_enable(get_uarte_instance(dev),
NRF_UARTE_INT_ENDRX_MASK);
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */
}
#define UARTE_ERROR_FROM_MASK(mask) \
((mask) & NRF_UARTE_ERROR_OVERRUN_MASK ? UART_ERROR_OVERRUN \
: (mask) & NRF_UARTE_ERROR_PARITY_MASK ? UART_ERROR_PARITY \
: (mask) & NRF_UARTE_ERROR_FRAMING_MASK ? UART_ERROR_FRAMING \
: (mask) & NRF_UARTE_ERROR_BREAK_MASK ? UART_BREAK \
: 0)
static void error_isr(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
uint32_t err = nrf_uarte_errorsrc_get(uarte);
struct uart_event evt = {
.type = UART_RX_STOPPED,
.data.rx_stop.reason = UARTE_ERROR_FROM_MASK(err),
};
/* For VPR cores read and write may be reordered - barrier needed. */
nrf_barrier_r();
nrf_uarte_errorsrc_clear(uarte, err);
user_callback(dev, &evt);
(void) uarte_nrfx_rx_disable(dev);
}
static void rxstarted_isr(const struct device *dev)
{
struct uart_event evt = {
.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;
#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
if (!K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_RXDRDY_MASK);
}
#else
if (async_rx->timeout_us != SYS_FOREVER_US) {
k_timeout_t timeout = K_USEC(async_rx->timeout_slab);
async_rx->timeout_left = async_rx->timeout_us;
k_timer_start(&async_rx->timer, timeout, timeout);
}
#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */
#endif /* !UARTE_HAS_FRAME_TIMEOUT */
user_callback(dev, &evt);
}
static void endrx_isr(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
async_rx->is_in_irq = true;
#endif
/* ensure rx timer is stopped - it will be restarted in RXSTARTED
* handler if needed
*/
k_timer_stop(&async_rx->timer);
/* this is the amount that the EasyDMA controller has copied into the
* buffer
*/
const int rx_amount = nrf_uarte_rx_amount_get(uarte) + async_rx->flush_cnt;
#ifdef CONFIG_HAS_NORDIC_DMM
const struct uarte_nrfx_config *config = dev->config;
int err =
dmm_buffer_in_release(config->mem_reg, async_rx->usr_buf, rx_amount, async_rx->buf);
(void)err;
__ASSERT_NO_MSG(err == 0);
async_rx->buf = async_rx->usr_buf;
#endif
async_rx->flush_cnt = 0;
/* The 'rx_offset' can be bigger than 'rx_amount', so it the length
* of data we report back the user may need to be clipped.
* This can happen because the 'rx_offset' count derives from RXRDY
* events, which can occur already for the next buffer before we are
* here to handle this buffer. (The next buffer is now already active
* because of the ENDRX_STARTRX shortcut)
*/
int rx_len = rx_amount - async_rx->offset;
if (rx_len < 0) {
rx_len = 0;
}
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
async_rx->total_user_byte_cnt += rx_len;
#endif
/* Only send the RX_RDY event if there is something to send */
if (rx_len > 0) {
notify_uart_rx_rdy(dev, rx_len);
}
rx_buf_release(dev, async_rx->buf);
async_rx->buf = async_rx->next_buf;
async_rx->buf_len = async_rx->next_buf_len;
#ifdef CONFIG_HAS_NORDIC_DMM
async_rx->usr_buf = async_rx->next_usr_buf;
#endif
async_rx->next_buf = NULL;
async_rx->next_buf_len = 0;
async_rx->offset = 0;
if (async_rx->enabled) {
/* If there is a next buffer, then STARTRX will have already been
* invoked by the short (the next buffer will be filling up already)
* and here we just do the swap of which buffer the driver is following,
* the next rx_timeout() will update the rx_offset.
*/
unsigned int key = irq_lock();
if (async_rx->buf) {
/* Check is based on assumption that ISR handler handles
* ENDRX before RXSTARTED so if short was set on time, RXSTARTED
* event will be set.
*/
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
}
/* Remove the short until the subsequent next buffer is setup */
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
} else {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
}
irq_unlock(key);
}
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
async_rx->is_in_irq = false;
#endif
}
/** @brief RX FIFO flushing
*
* Due to the HW bug which does not update RX.AMOUNT register when FIFO was empty
* a workaround is applied which checks RXSTARTED event. If that event is set it
* means that FIFO was not empty.
*
* @param dev Device.
*
* @return number of bytes flushed from the fifo.
*/
static uint8_t rx_flush(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *config = dev->config;
uint32_t rx_amount;
nrf_uarte_rx_buffer_set(uarte, config->rx_flush_buf, UARTE_HW_RX_FIFO_SIZE);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_FLUSHRX);
while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
/* empty */
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
if (!IS_ENABLED(RX_FLUSH_WORKAROUND)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
rx_amount = nrf_uarte_rx_amount_get(uarte);
} else if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
rx_amount = nrf_uarte_rx_amount_get(uarte);
} else {
rx_amount = 0;
}
if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE) &&
rx_amount) {
sys_cache_data_invd_range(config->rx_flush_buf, rx_amount);
}
return rx_amount;
}
/* This handler is called when the receiver is stopped. If rx was aborted
* data from fifo is flushed.
*/
static void rxto_isr(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
if (async_rx->buf) {
#ifdef CONFIG_HAS_NORDIC_DMM
(void)dmm_buffer_in_release(config->mem_reg, async_rx->usr_buf, 0, async_rx->buf);
async_rx->buf = async_rx->usr_buf;
#endif
rx_buf_release(dev, async_rx->buf);
async_rx->buf = NULL;
}
/* This point can be reached in two cases:
* 1. RX is disabled because all provided RX buffers have been filled.
* 2. RX was explicitly disabled by a call to uart_rx_disable().
* In both cases, the rx_enabled flag is cleared, so that RX can be
* enabled again.
* In the second case, additionally, data from the UARTE internal RX
* FIFO need to be discarded.
*/
async_rx->enabled = false;
if (async_rx->discard_fifo) {
async_rx->discard_fifo = false;
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
if (HW_RX_COUNTING_ENABLED(config)) {
/* It need to be included because TIMER+PPI got RXDRDY events
* and counted those flushed bytes.
*/
async_rx->total_user_byte_cnt += rx_flush(dev);
}
#endif
} else if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(config)) {
async_rx->flush_cnt = rx_flush(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
if (LOW_POWER_ENABLED(config)) {
uint32_t key = irq_lock();
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
irq_unlock(key);
}
notify_rx_disable(dev);
}
static void txstopped_isr(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
unsigned int key;
key = irq_lock();
size_t amount = (data->async->tx.amount >= 0) ?
data->async->tx.amount : nrf_uarte_tx_amount_get(uarte);
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
if (data->flags & UARTE_FLAG_POLL_OUT) {
pm_device_runtime_put_async(dev, K_NO_WAIT);
data->flags &= ~UARTE_FLAG_POLL_OUT;
}
} else if (LOW_POWER_ENABLED(config)) {
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX);
}
irq_unlock(key);
if (!data->async->tx.buf) {
return;
}
/* If there is a pending tx request, it means that uart_tx()
* was called when there was ongoing uart_poll_out. Handling
* TXSTOPPED interrupt means that uart_poll_out has completed.
*/
if (data->async->tx.pending) {
key = irq_lock();
start_tx_locked(dev, data);
irq_unlock(key);
return;
}
/* Cache buffer is used because tx_buf wasn't in RAM. */
if (data->async->tx.buf != data->async->tx.xfer_buf) {
/* In that case setup next chunk. If that was the last chunk
* fall back to reporting TX_DONE.
*/
if (amount == data->async->tx.xfer_len) {
data->async->tx.cache_offset += amount;
if (setup_tx_cache(dev)) {
key = irq_lock();
start_tx_locked(dev, data);
irq_unlock(key);
return;
}
/* Amount is already included in cache_offset. */
amount = data->async->tx.cache_offset;
} else {
/* TX was aborted, include cache_offset in amount. */
amount += data->async->tx.cache_offset;
}
}
k_timer_stop(&data->async->tx.timer);
struct uart_event evt = {
.data.tx.buf = data->async->tx.buf,
.data.tx.len = amount,
};
if (amount == data->async->tx.len) {
evt.type = UART_TX_DONE;
} else {
evt.type = UART_TX_ABORTED;
}
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
data->async->tx.buf = NULL;
data->async->tx.len = 0;
user_callback(dev, &evt);
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_put_async(dev, K_NO_WAIT);
}
}
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);
data->async->rx.idle_cnt = 0;
k_timer_start(&data->async->rx.timer, data->async->rx.timeout, K_NO_WAIT);
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_RXDRDY_MASK);
#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,
uint32_t int_mask, uint32_t int_en_mask)
{
if (nrf_uarte_event_check(uarte, event) && (int_mask & int_en_mask)) {
nrf_uarte_event_clear(uarte, event);
return true;
}
return false;
}
static void uarte_nrfx_isr_async(const void *arg)
{
const struct device *dev = arg;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *config = dev->config;
struct uarte_nrfx_data *data = dev->data;
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) || IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT))
&& event_check_clear(uarte, NRF_UARTE_EVENT_RXDRDY, NRF_UARTE_INT_RXDRDY_MASK, imask)) {
rxdrdy_isr(dev);
}
if (event_check_clear(uarte, NRF_UARTE_EVENT_ERROR, NRF_UARTE_INT_ERROR_MASK, imask)) {
error_isr(dev);
}
if (event_check_clear(uarte, NRF_UARTE_EVENT_ENDRX, NRF_UARTE_INT_ENDRX_MASK, imask)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
endrx_isr(dev);
}
/* RXSTARTED must be handled after ENDRX because it starts the RX timeout
* and if order is swapped then ENDRX will stop this timeout.
* Skip if ENDRX is set when RXSTARTED is set. It means that
* ENDRX occurred after check for ENDRX in isr which may happen when
* UARTE interrupt got preempted. Events are not cleared
* and isr will be called again. ENDRX will be handled first.
*/
if ((imask & NRF_UARTE_INT_RXSTARTED_MASK) &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED) &&
!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
rxstarted_isr(dev);
}
/* RXTO must be handled after ENDRX which should notify the buffer.
* Skip if ENDRX is set when RXTO is set. It means that
* ENDRX occurred after check for ENDRX in isr which may happen when
* UARTE interrupt got preempted. Events are not cleared
* and isr will be called again. ENDRX will be handled first.
*/
if ((imask & NRF_UARTE_INT_RXTO_MASK) &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO) &&
!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
rxto_isr(dev);
}
if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT) &&
(imask & NRF_UARTE_INT_ENDTX_MASK) &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) {
endtx_isr(dev);
}
if ((imask & NRF_UARTE_INT_TXSTOPPED_MASK) &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) {
txstopped_isr(dev);
}
if (atomic_and(&data->flags, ~UARTE_FLAG_TRIG_RXTO) & UARTE_FLAG_TRIG_RXTO) {
#ifdef CONFIG_HAS_NORDIC_DMM
int ret;
ret = dmm_buffer_in_release(config->mem_reg, async_rx->usr_buf, async_rx->buf_len,
async_rx->buf);
(void)ret;
__ASSERT_NO_MSG(ret == 0);
async_rx->buf = async_rx->usr_buf;
#endif
notify_uart_rx_rdy(dev, async_rx->buf_len);
rx_buf_release(dev, async_rx->buf);
async_rx->buf_len = 0;
async_rx->buf = NULL;
notify_rx_disable(dev);
}
}
#endif /* UARTE_ANY_ASYNC */
/**
* @brief Poll the device for input.
*
* @param dev UARTE device struct
* @param c Pointer to character
*
* @return 0 if a character arrived, -1 if the input buffer is empty.
*/
static int uarte_nrfx_poll_in(const struct device *dev, unsigned char *c)
{
const struct uarte_nrfx_config *config = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
#ifdef UARTE_ANY_ASYNC
struct uarte_nrfx_data *data = dev->data;
if (data->async) {
return -ENOTSUP;
}
#endif
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
return -1;
}
if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(config->poll_in_byte, 1);
}
*c = *config->poll_in_byte;
/* clear the interrupt */
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
return 0;
}
/**
* @brief Output a character in polled mode.
*
* @param dev UARTE device struct
* @param c Character to send
*/
static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c)
{
const struct uarte_nrfx_config *config = dev->config;
bool isr_mode = k_is_in_isr() || k_is_pre_kernel();
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
unsigned int key;
if (isr_mode) {
while (1) {
key = irq_lock();
if (is_tx_ready(dev)) {
#if UARTE_ANY_ASYNC
if (data->async && data->async->tx.len &&
data->async->tx.amount < 0) {
data->async->tx.amount = nrf_uarte_tx_amount_get(uarte);
}
#endif
break;
}
irq_unlock(key);
Z_SPIN_DELAY(3);
}
} else {
key = wait_tx_ready(dev);
}
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
/* If instance does not support PM from ISR device shall
* already be turned on.
*/
enum pm_device_state state;
int err;
err = pm_device_state_get(dev, &state);
(void)err;
__ASSERT_NO_MSG(err == 0);
if (state != PM_DEVICE_STATE_ACTIVE) {
irq_unlock(key);
return;
}
}
if (!(data->flags & UARTE_FLAG_POLL_OUT)) {
data->flags |= UARTE_FLAG_POLL_OUT;
pm_device_runtime_get(dev);
}
}
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(config)) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
}
*config->poll_out_byte = c;
tx_start(dev, config->poll_out_byte, 1);
irq_unlock(key);
}
#ifdef UARTE_INTERRUPT_DRIVEN
/** Interrupt driven FIFO fill function */
static int uarte_nrfx_fifo_fill(const struct device *dev,
const uint8_t *tx_data,
int len)
{
struct uarte_nrfx_data *data = dev->data;
len = MIN(len, data->int_driven->tx_buff_size);
if (!atomic_cas(&data->int_driven->fifo_fill_lock, 0, 1)) {
return 0;
}
/* Copy data to RAM buffer for EasyDMA transfer */
memcpy(data->int_driven->tx_buffer, tx_data, len);
unsigned int key = irq_lock();
if (!is_tx_ready(dev)) {
data->int_driven->fifo_fill_lock = 0;
len = 0;
} else {
tx_start(dev, data->int_driven->tx_buffer, len);
}
irq_unlock(key);
return len;
}
/** Interrupt driven FIFO read function */
static int uarte_nrfx_fifo_read(const struct device *dev,
uint8_t *rx_data,
const int size)
{
int num_rx = 0;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *config = dev->config;
if (size > 0 && nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
/* Clear the interrupt */
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(config->poll_in_byte, 1);
}
/* Receive a character */
rx_data[num_rx++] = *config->poll_in_byte;
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
}
return num_rx;
}
/** Interrupt driven transfer enabling function */
static void uarte_nrfx_irq_tx_enable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
struct uarte_nrfx_data *data = dev->data;
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_get(dev);
}
unsigned int key = irq_lock();
data->int_driven->disable_tx_irq = false;
data->int_driven->tx_irq_enabled = true;
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
irq_unlock(key);
}
/** Interrupt driven transfer disabling function */
static void uarte_nrfx_irq_tx_disable(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
/* TX IRQ will be disabled after current transmission is finished */
data->int_driven->disable_tx_irq = true;
data->int_driven->tx_irq_enabled = false;
}
/** Interrupt driven transfer ready function */
static int uarte_nrfx_irq_tx_ready_complete(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
struct uarte_nrfx_data *data = dev->data;
/* ENDTX flag is always on so that ISR is called when we enable TX IRQ.
* Because of that we have to explicitly check if ENDTX interrupt is
* enabled, otherwise this function would always return true no matter
* what would be the source of interrupt.
*/
bool ready = data->int_driven->tx_irq_enabled &&
nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED);
if (ready) {
data->int_driven->fifo_fill_lock = 0;
}
return ready ? data->int_driven->tx_buff_size : 0;
}
static int uarte_nrfx_irq_rx_ready(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
return nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX);
}
/** Interrupt driven receiver enabling function */
static void uarte_nrfx_irq_rx_enable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDRX_MASK);
}
/** Interrupt driven receiver disabling function */
static void uarte_nrfx_irq_rx_disable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDRX_MASK);
}
/** Interrupt driven error enabling function */
static void uarte_nrfx_irq_err_enable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ERROR_MASK);
}
/** Interrupt driven error disabling function */
static void uarte_nrfx_irq_err_disable(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ERROR_MASK);
}
/** Interrupt driven pending status function */
static int uarte_nrfx_irq_is_pending(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
return ((nrf_uarte_int_enable_check(uarte,
NRF_UARTE_INT_TXSTOPPED_MASK) &&
uarte_nrfx_irq_tx_ready_complete(dev))
||
(nrf_uarte_int_enable_check(uarte,
NRF_UARTE_INT_ENDRX_MASK) &&
uarte_nrfx_irq_rx_ready(dev)));
}
/** Interrupt driven interrupt update function */
static int uarte_nrfx_irq_update(const struct device *dev)
{
return 1;
}
/** Set the callback function */
static void uarte_nrfx_irq_callback_set(const struct device *dev,
uart_irq_callback_user_data_t cb,
void *cb_data)
{
struct uarte_nrfx_data *data = dev->data;
data->int_driven->cb = cb;
data->int_driven->cb_data = cb_data;
}
#endif /* UARTE_INTERRUPT_DRIVEN */
static DEVICE_API(uart, uart_nrfx_uarte_driver_api) = {
.poll_in = uarte_nrfx_poll_in,
.poll_out = uarte_nrfx_poll_out,
.err_check = uarte_nrfx_err_check,
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
.configure = uarte_nrfx_configure,
.config_get = uarte_nrfx_config_get,
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#ifdef UARTE_ANY_ASYNC
.callback_set = uarte_nrfx_callback_set,
.tx = uarte_nrfx_tx,
.tx_abort = uarte_nrfx_tx_abort,
.rx_enable = uarte_nrfx_rx_enable,
.rx_buf_rsp = uarte_nrfx_rx_buf_rsp,
.rx_disable = uarte_nrfx_rx_disable,
#endif /* UARTE_ANY_ASYNC */
#ifdef UARTE_INTERRUPT_DRIVEN
.fifo_fill = uarte_nrfx_fifo_fill,
.fifo_read = uarte_nrfx_fifo_read,
.irq_tx_enable = uarte_nrfx_irq_tx_enable,
.irq_tx_disable = uarte_nrfx_irq_tx_disable,
.irq_tx_ready = uarte_nrfx_irq_tx_ready_complete,
.irq_rx_enable = uarte_nrfx_irq_rx_enable,
.irq_rx_disable = uarte_nrfx_irq_rx_disable,
.irq_tx_complete = uarte_nrfx_irq_tx_ready_complete,
.irq_rx_ready = uarte_nrfx_irq_rx_ready,
.irq_err_enable = uarte_nrfx_irq_err_enable,
.irq_err_disable = uarte_nrfx_irq_err_disable,
.irq_is_pending = uarte_nrfx_irq_is_pending,
.irq_update = uarte_nrfx_irq_update,
.irq_callback_set = uarte_nrfx_irq_callback_set,
#endif /* UARTE_INTERRUPT_DRIVEN */
};
#ifdef UARTE_ENHANCED_POLL_OUT
static int endtx_stoptx_ppi_init(NRF_UARTE_Type *uarte,
struct uarte_nrfx_data *data)
{
nrfx_err_t ret;
ret = nrfx_gppi_channel_alloc(&data->ppi_ch_endtx);
if (ret != NRFX_SUCCESS) {
LOG_ERR("Failed to allocate PPI Channel");
return -EIO;
}
nrfx_gppi_channel_endpoints_setup(data->ppi_ch_endtx,
nrf_uarte_event_address_get(uarte, NRF_UARTE_EVENT_ENDTX),
nrf_uarte_task_address_get(uarte, NRF_UARTE_TASK_STOPTX));
nrfx_gppi_channels_enable(BIT(data->ppi_ch_endtx));
return 0;
}
#endif /* UARTE_ENHANCED_POLL_OUT */
/** @brief Pend until TX is stopped.
*
* There are 2 configurations that must be handled:
* - ENDTX->TXSTOPPED PPI enabled - just pend until TXSTOPPED event is set
* - disable ENDTX interrupt and manually trigger STOPTX, pend for TXSTOPPED
*/
static void wait_for_tx_stopped(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
bool ppi_endtx = (config->flags & UARTE_CFG_FLAG_PPI_ENDTX) ||
IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT);
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
bool res;
if (!ppi_endtx) {
/* We assume here that it can be called from any context,
* including the one that uarte interrupt will not preempt.
* Disable endtx interrupt to ensure that it will not be triggered
* (if in lower priority context) and stop TX if necessary.
*/
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDTX_MASK);
NRFX_WAIT_FOR(is_tx_ready(dev), 1000, 1, res);
if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) {
if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT)) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX);
}
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX);
}
}
NRFX_WAIT_FOR(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED),
1000, 1, res);
if (!ppi_endtx) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK);
}
}
static void uarte_pm_resume(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
(void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || !LOW_POWER_ENABLED(cfg)) {
uarte_periph_enable(dev);
}
}
static void uarte_pm_suspend(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
(void)data;
#ifdef UARTE_ANY_ASYNC
if (data->async) {
/* Entering inactive state requires device to be no
* active asynchronous calls.
*/
__ASSERT_NO_MSG(!data->async->rx.enabled);
__ASSERT_NO_MSG(!data->async->tx.len);
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
/* If runtime PM is enabled then reference counting ensures that
* suspend will not occur when TX is active.
*/
__ASSERT_NO_MSG(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED));
} else {
wait_for_tx_stopped(dev);
}
#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX)
if (data->async && HW_RX_COUNTING_ENABLED(cfg)) {
nrfx_timer_disable(&cfg->timer);
/* Timer/counter value is reset when disabled. */
data->async->rx.total_byte_cnt = 0;
data->async->rx.total_user_byte_cnt = 0;
}
#endif
} else if (IS_ENABLED(UARTE_ANY_NONE_ASYNC))
#endif
{
if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
#if defined(UARTE_INTERRUPT_DRIVEN) && defined(CONFIG_PM_DEVICE)
if (data->int_driven) {
data->int_driven->rx_irq_enabled =
nrf_uarte_int_enable_check(uarte,
NRF_UARTE_INT_ENDRX_MASK);
if (data->int_driven->rx_irq_enabled) {
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDRX_MASK);
}
}
#endif
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO)) {
/* Busy wait for event to register */
Z_SPIN_DELAY(2);
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ERROR);
}
wait_for_tx_stopped(dev);
}
#ifdef CONFIG_SOC_NRF54H20_GPD
nrf_gpd_retain_pins_set(cfg->pcfg, true);
#endif
nrf_uarte_disable(uarte);
(void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
}
static int uarte_nrfx_pm_action(const struct device *dev, enum pm_device_action action)
{
if (action == PM_DEVICE_ACTION_RESUME) {
uarte_pm_resume(dev);
} else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) {
uarte_pm_suspend(dev);
} else {
return -ENOTSUP;
}
return 0;
}
static int uarte_tx_path_init(const struct device *dev)
{
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *cfg = dev->config;
bool auto_endtx = false;
#ifdef UARTE_HAS_ENDTX_STOPTX_SHORT
nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_ENDTX_STOPTX);
auto_endtx = true;
#elif defined(UARTE_ENHANCED_POLL_OUT)
if (cfg->flags & UARTE_CFG_FLAG_PPI_ENDTX) {
struct uarte_nrfx_data *data = dev->data;
int err;
err = endtx_stoptx_ppi_init(uarte, data);
if (err < 0) {
return err;
}
auto_endtx = true;
}
#endif
/* Get to the point where TXSTOPPED event is set but TXSTOPPED interrupt is
* disabled. This trick is later on used to handle TX path and determine
* using HW if TX is active (TXSTOPPED event set means TX is inactive).
*
* Set TXSTOPPED event by requesting fake (zero-length) transfer.
* Pointer to RAM variable is set because otherwise such operation may
* result in HardFault or RAM corruption.
*/
nrf_uarte_enable(uarte);
nrf_uarte_tx_buffer_set(uarte, cfg->poll_out_byte, 0);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTTX);
if (!auto_endtx) {
while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) {
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX);
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK);
}
while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) {
}
nrf_uarte_disable(uarte);
return 0;
}
static int uarte_instance_init(const struct device *dev,
uint8_t interrupts_active)
{
int err;
const struct uarte_nrfx_config *cfg = dev->config;
if (IS_ENABLED(CONFIG_ARCH_POSIX)) {
/* For simulation the DT provided peripheral address needs to be corrected */
((struct pinctrl_dev_config *)cfg->pcfg)->reg = (uintptr_t)cfg->uarte_regs;
}
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
err = uarte_nrfx_configure(dev, &((struct uarte_nrfx_data *)dev->data)->uart_config);
if (err) {
return err;
}
#else
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
nrf_uarte_baudrate_set(uarte, cfg->nrf_baudrate);
nrf_uarte_configure(uarte, &cfg->hw_config);
#endif
#ifdef UARTE_ANY_ASYNC
struct uarte_nrfx_data *data = dev->data;
if (data->async) {
err = uarte_async_init(dev);
if (err < 0) {
return err;
}
}
#endif
err = uarte_tx_path_init(dev);
if (err) {
return err;
}
return pm_device_driver_init(dev, uarte_nrfx_pm_action);
}
#define UARTE_IRQ_CONFIGURE(idx, isr_handler) \
do { \
IRQ_CONNECT(DT_IRQN(UARTE(idx)), DT_IRQ(UARTE(idx), priority), \
isr_handler, DEVICE_DT_GET(UARTE(idx)), 0); \
irq_enable(DT_IRQN(UARTE(idx))); \
} while (false)
/* Low power mode is used when disable_rx is not defined or in async mode if
* kconfig option is enabled.
*/
#define USE_LOW_POWER(idx) \
COND_CODE_1(CONFIG_PM_DEVICE, (0), \
(((!UARTE_PROP(idx, disable_rx) && \
COND_CODE_1(CONFIG_UART_##idx##_ASYNC, \
(!IS_ENABLED(CONFIG_UART_##idx##_NRF_ASYNC_LOW_POWER)),\
(1))) ? 0 : UARTE_CFG_FLAG_LOW_POWER)))
#define UARTE_DISABLE_RX_INIT(node_id) \
.disable_rx = DT_PROP(node_id, disable_rx)
#define UARTE_GET_FREQ(idx) DT_PROP(DT_CLOCKS_CTLR(UARTE(idx)), clock_frequency)
#define UARTE_GET_BAUDRATE_DIV(idx) \
COND_CODE_1(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \
((UARTE_GET_FREQ(idx) / NRF_UARTE_BASE_FREQUENCY_16MHZ)), (1))
/* When calculating baudrate we need to take into account that high speed instances
* must have baudrate adjust to the ratio between UARTE clocking frequency and 16 MHz.
*/
#define UARTE_GET_BAUDRATE(idx) \
(NRF_BAUDRATE(UARTE_PROP(idx, current_speed)) / UARTE_GET_BAUDRATE_DIV(idx))
/* Macro for setting nRF specific configuration structures. */
#define UARTE_NRF_CONFIG(idx) { \
.hwfc = (UARTE_PROP(idx, hw_flow_control) == \
UART_CFG_FLOW_CTRL_RTS_CTS) ? \
NRF_UARTE_HWFC_ENABLED : NRF_UARTE_HWFC_DISABLED, \
.parity = IS_ENABLED(CONFIG_UART_##idx##_NRF_PARITY_BIT) ? \
NRF_UARTE_PARITY_INCLUDED : NRF_UARTE_PARITY_EXCLUDED, \
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. */
#define UARTE_CONFIG(idx) { \
.baudrate = UARTE_PROP(idx, current_speed), \
.data_bits = UART_CFG_DATA_BITS_8, \
.stop_bits = UART_CFG_STOP_BITS_1, \
.parity = IS_ENABLED(CONFIG_UART_##idx##_NRF_PARITY_BIT) \
? UART_CFG_PARITY_EVEN \
: UART_CFG_PARITY_NONE, \
.flow_ctrl = UARTE_PROP(idx, hw_flow_control) \
? UART_CFG_FLOW_CTRL_RTS_CTS \
: UART_CFG_FLOW_CTRL_NONE, \
}
#define UART_NRF_UARTE_DEVICE(idx) \
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(UARTE(idx)); \
UARTE_INT_DRIVEN(idx); \
PINCTRL_DT_DEFINE(UARTE(idx)); \
IF_ENABLED(CONFIG_UART_##idx##_ASYNC, ( \
static uint8_t \
uarte##idx##_tx_cache[CONFIG_UART_ASYNC_TX_CACHE_SIZE] \
DMM_MEMORY_SECTION(UARTE(idx)); \
static uint8_t uarte##idx##_flush_buf[UARTE_HW_RX_FIFO_SIZE] \
DMM_MEMORY_SECTION(UARTE(idx)); \
struct uarte_async_cb uarte##idx##_async;)) \
static uint8_t uarte##idx##_poll_out_byte DMM_MEMORY_SECTION(UARTE(idx));\
static uint8_t uarte##idx##_poll_in_byte DMM_MEMORY_SECTION(UARTE(idx)); \
static struct uarte_nrfx_data uarte_##idx##_data = { \
IF_ENABLED(CONFIG_UART_USE_RUNTIME_CONFIGURE, \
(.uart_config = UARTE_CONFIG(idx),)) \
IF_ENABLED(CONFIG_UART_##idx##_ASYNC, \
(.async = &uarte##idx##_async,)) \
IF_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN, \
(.int_driven = &uarte##idx##_int_driven,)) \
}; \
COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, (), \
(BUILD_ASSERT(NRF_BAUDRATE(UARTE_PROP(idx, current_speed)) > 0,\
"Unsupported baudrate");)) \
static const struct uarte_nrfx_config uarte_##idx##z_config = { \
COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, \
(IF_ENABLED(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \
(.clock_freq = UARTE_GET_FREQ(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), \
IF_ENABLED(CONFIG_HAS_NORDIC_DMM, \
(.mem_reg = DMM_DEV_TO_REG(UARTE(idx)),)) \
.flags = \
(IS_ENABLED(CONFIG_UART_##idx##_ENHANCED_POLL_OUT) ? \
UARTE_CFG_FLAG_PPI_ENDTX : 0) | \
(IS_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC) ? \
UARTE_CFG_FLAG_HW_BYTE_COUNTING : 0) | \
(!IS_ENABLED(CONFIG_HAS_NORDIC_DMM) ? 0 : \
(UARTE_IS_CACHEABLE(idx) ? \
UARTE_CFG_FLAG_CACHEABLE : 0)) | \
USE_LOW_POWER(idx), \
UARTE_DISABLE_RX_INIT(UARTE(idx)), \
.poll_out_byte = &uarte##idx##_poll_out_byte, \
.poll_in_byte = &uarte##idx##_poll_in_byte, \
IF_ENABLED(CONFIG_UART_##idx##_ASYNC, \
(.tx_cache = uarte##idx##_tx_cache, \
.rx_flush_buf = uarte##idx##_flush_buf,)) \
IF_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC, \
(.timer = NRFX_TIMER_INSTANCE( \
CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER),)) \
}; \
static int uarte_##idx##_init(const struct device *dev) \
{ \
COND_CODE_1(CONFIG_UART_##idx##_ASYNC, \
(UARTE_IRQ_CONFIGURE(idx, uarte_nrfx_isr_async);), \
(UARTE_IRQ_CONFIGURE(idx, uarte_nrfx_isr_int);)) \
return uarte_instance_init( \
dev, \
IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN)); \
} \
\
PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action, \
COND_CODE_1(INSTANCE_IS_FAST(_, /*empty*/, idx, _),\
(0), (PM_DEVICE_ISR_SAFE))); \
\
DEVICE_DT_DEFINE(UARTE(idx), \
uarte_##idx##_init, \
PM_DEVICE_DT_GET(UARTE(idx)), \
&uarte_##idx##_data, \
&uarte_##idx##z_config, \
PRE_KERNEL_1, \
CONFIG_SERIAL_INIT_PRIORITY, \
&uart_nrfx_uarte_driver_api)
#define UARTE_INT_DRIVEN(idx) \
IF_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN, \
(static uint8_t uarte##idx##_tx_buffer \
[MIN(CONFIG_UART_##idx##_NRF_TX_BUFFER_SIZE, \
BIT_MASK(UARTE##idx##_EASYDMA_MAXCNT_SIZE))] \
DMM_MEMORY_SECTION(UARTE(idx)); \
static struct uarte_nrfx_int_driven \
uarte##idx##_int_driven = { \
.tx_buffer = uarte##idx##_tx_buffer, \
.tx_buff_size = sizeof(uarte##idx##_tx_buffer),\
};))
#define COND_UART_NRF_UARTE_DEVICE(unused, prefix, i, _) \
IF_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i, (UART_NRF_UARTE_DEVICE(prefix##i);))
UARTE_FOR_EACH_INSTANCE(COND_UART_NRF_UARTE_DEVICE, (), ())