blob: 41e8d8c6d7fa970268483642697d17cacc4d037d [file]
/*
* Copyright (c) 2018-2021 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Driver for Nordic Semiconductor nRF UARTE
*/
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#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 <hal/nrf_timer.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/minmax.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>
LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL);
#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
#define MAYBE_CONST
#else
#define MAYBE_CONST const
#endif
#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
/* Set a flag that is used for code that is shared between bytes counting methods
* on legacy and new platforms.
*/
#if defined(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER) || \
defined(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
#define COUNT_BYTES_WITH_TIMER_COMMON 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
#if defined(CONFIG_UARTE_NRFX_UARTE_HAS_DMAEND)
BUILD_ASSERT(IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT), "DMAEND support requires frame timeout support");
BUILD_ASSERT(NRF_UARTE_HAS_DMAEND_TASK, "Devicetree enables an unsupported DMAEND task");
BUILD_ASSERT(NRF_UARTE_HAS_FRAMETIMEOUT_DMAEND_SHORT,
"Devicetree enables an unsupported FRAMETIMEOUT_DMAEND short");
#endif
#if defined(CONFIG_UARTE_NRFX_UARTE_HAS_DMAEND) && NRF_UARTE_HAS_DMAEND_TASK && \
NRF_UARTE_HAS_FRAMETIMEOUT_DMAEND_SHORT
#define UARTE_HAS_DMAEND 1
#endif
/* Frame timeout has a bug that countdown counter may not be triggered in some
* specific condition. It may happen if RX is manually started after ENDRX (STOPRX
* task was not triggered) and there is ongoing reception of a byte. RXDRDY event
* triggered by the reception of that byte may not trigger frame timeout counter.
* If this is the last byte of a transfer then without the workaround there will
* be no expected RX timeout.
*/
#ifdef UARTE_HAS_FRAME_TIMEOUT
#define RX_FRAMETIMEOUT_WORKAROUND 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
#define INSTANCE_IS_HIGH_SPEED(unused, prefix, idx, _) \
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(UARTE(prefix##idx)), \
((NRF_PERIPH_GET_FREQUENCY(UARTE(prefix##idx)) > NRF_UARTE_BASE_FREQUENCY_16MHZ)), \
(0))
/* Macro determines if there is any high speed instance (instance that is driven using
* clock that is faster than 16 MHz).
*/
#define UARTE_ANY_HIGH_SPEED (UARTE_FOR_EACH_INSTANCE(INSTANCE_IS_HIGH_SPEED, (||), (0)))
#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
/* TIMER CC channels for counting bytes with TIMER. */
/* Channel used for capturing current counter value. */
#define UARTE_TIMER_CAPTURE_CH 0
/* Channel used to get compare event when number of received bytes reaches user buffer size. */
#define UARTE_TIMER_USR_CNT_CH 1
/* Channel used to get compare event when bounce buffer need to be switched. */
#define UARTE_TIMER_BUF_SWITCH_CH 2
/* Magic byte that is used to fill the buffer. */
#define UARTE_MAGIC_BYTE 0xAA
#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;
};
/* Structure with data for Count Bytes With Timer receiver mode (cbwt). */
struct uarte_async_rx_cbwt {
uint8_t *curr_bounce_buf;
uint8_t *anomaly_byte_addr;
uint32_t usr_rd_off;
uint32_t usr_wr_off;
uint32_t bounce_off;
uint32_t bounce_limit;
uint32_t last_cnt;
uint32_t cc_usr;
uint32_t cc_swap;
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
size_t bounce_buf_swap_len;
#endif
#ifdef UARTE_ANY_CACHE
uint8_t *anomaly_byte_dst;
uint8_t anomaly_byte;
#endif
uint8_t bounce_idx;
bool discard_fifo;
};
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;
k_timeout_t timeout;
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY
uint32_t total_user_byte_cnt; /* Total number of bytes passed to user */
#endif /* CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY */
#ifdef COUNT_BYTES_WITH_TIMER_COMMON
nrfx_gppi_handle_t ppi_h;
/* Flag to ensure that RX timeout won't be executed during ENDRX ISR */
volatile bool is_in_irq;
#endif
uint8_t idle_cnt;
uint8_t flush_cnt;
/* Flag indicating that STOPRX is triggered and RXTO is expected. */
bool stopped;
/* STOPRX must wait until ISR accounts for a pending ENDRX rollover. */
bool stoprx_deferred;
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
#ifdef CONFIG_UART_NRFX_UARTE_HFXO_ON_ACTIVE
struct onoff_client hfxo_client;
struct k_sem hfxo_ready;
#endif
atomic_val_t poll_out_lock;
atomic_t flags;
#ifdef UARTE_ENHANCED_POLL_OUT
nrfx_gppi_handle_t ppi_h_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)
/* Flag indicating that a workaround for not working frame timeout is active. */
#define UARTE_FLAG_FTIMEOUT_WATCH BIT(4)
/* Flag indicating that UART_RX_BUF_REQUEST event need to be called from the interrupt context. */
#define UARTE_FLAG_RX_BUF_REQ BIT(5)
/* Flag indicating that CC value in TIMER was set too late. */
#define UARTE_FLAG_LATE_CC BIT(6)
/* If enabled then ENDTX is PPI'ed to TXSTOP */
#define UARTE_CFG_FLAG_PPI_ENDTX BIT(0)
/* 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(1)
/* If enabled then UARTE peripheral is using memory which is cacheable. */
#define UARTE_CFG_FLAG_CACHEABLE BIT(2)
/* Indicates that UARTE/TIMER interrupt priority differs from system clock (GRTC/RTC). */
#define UARTE_CFG_FLAG_VAR_IRQ BIT(4)
/* Indicates that instance needs special handling of BAUDRATE register. */
#define UARTE_CFG_FLAG_VOLATILE_BAUDRATE BIT(5)
/* Formula for getting the baudrate settings is following:
* 2^12 * floor(2^20 / round(f_PCLK / desired_baudrate)) where f_PCLK is a frequency that
* drives the UARTE.
*
* @param f_pclk Frequency of the clock that drives the peripheral.
* @param baudrate Desired baudrate.
*
* @return Baudrate setting to be written to the BAUDRATE register
*/
#define UARTE_GET_CUSTOM_BAUDRATE(f_pclk, baudrate) \
((BIT(20) / DIV_ROUND_CLOSEST(f_pclk, baudrate)) << 12)
/* 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)
/* Convert Zephyr data bits enum to HAL enum. Use 8 bit data size first as it is the most common. */
#define DATABITS_TO_NRF_FRAMESIZE(data_bits) \
((data_bits) == UART_CFG_DATA_BITS_8 ? NRF_UARTE_FRAME_SIZE_8_BIT : \
(data_bits) == UART_CFG_DATA_BITS_5 ? NRF_UARTE_FRAME_SIZE_5_BIT : \
(data_bits) == UART_CFG_DATA_BITS_6 ? NRF_UARTE_FRAME_SIZE_6_BIT : \
(data_bits) == UART_CFG_DATA_BITS_7 ? NRF_UARTE_FRAME_SIZE_7_BIT : \
NRF_UARTE_FRAME_SIZE_9_BIT)
#define UARTE_MIN_BUF_SWAP_LEN 10
#define UARTE_US_TO_BYTES(baudrate) \
DIV_ROUND_UP((CONFIG_UART_NRFX_UARTE_BOUNCE_BUF_SWAP_LATENCY * baudrate), 10000000)
#define UARTE_BUF_SWAP_LEN(bounce_buf_len, baudrate) \
((bounce_buf_len) - MAX(UARTE_MIN_BUF_SWAP_LEN, UARTE_US_TO_BYTES(baudrate)))
#define LOW_POWER_ENABLED(_config) \
(IS_ENABLED(UARTE_ANY_LOW_POWER) && \
!IS_ENABLED(CONFIG_PM_DEVICE) && \
(_config->flags & UARTE_CFG_FLAG_LOW_POWER))
/**
* @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
#ifdef COUNT_BYTES_WITH_TIMER_COMMON
NRF_TIMER_Type * timer_regs;
IRQn_Type uarte_irqn;
#endif
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
IRQn_Type timer_irqn;
uint8_t *bounce_buf[2];
size_t bounce_buf_len;
size_t bounce_buf_swap_len;
struct uarte_async_rx_cbwt *cbwt_data;
#endif
uint8_t *tx_cache;
uint8_t *rx_flush_buf;
#endif /* UARTE_ANY_ASYNC */
uint8_t *poll_out_byte;
uint8_t *poll_in_byte;
};
/* Determine if instance is using an approach with counting bytes with TIMER (legacy). */
#define IS_CBWT_LEGACY(dev) \
COND_CODE_1(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY, \
((((const struct uarte_nrfx_config *)dev->config)->timer_regs != NULL)),\
(false))
/* Determine if instance is using an approach with counting bytes with TIMER (cbwt). */
#define IS_CBWT(dev) \
COND_CODE_1(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER, \
((((const struct uarte_nrfx_config *)dev->config)->timer_regs != NULL)),\
(false))
static inline NRF_UARTE_Type *get_uarte_instance(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
return config->uarte_regs;
}
#if !defined(CONFIG_UART_NRFX_UARTE_NO_IRQ)
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);
}
#endif
/** @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;
const struct uarte_nrfx_config *config = dev->config;
data->flags &= ~dis_mask;
if (data->flags & UARTE_FLAG_LOW_POWER) {
return;
}
#if defined(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
if (data->async && IS_CBWT_LEGACY(dev)) {
#if NRF_TIMER_HAS_SHUTDOWN
nrf_timer_task_trigger(config->timer_regs, NRF_TIMER_TASK_SHUTDOWN);
#else
nrf_timer_task_trigger(config->timer_regs, NRF_TIMER_TASK_STOP);
nrf_timer_task_trigger(config->timer_regs, NRF_TIMER_TASK_CLEAR);
#endif
/* Timer/counter value is reset when disabled. */
data->async->rx.total_user_byte_cnt = 0;
}
#endif
nrf_uarte_disable(get_uarte_instance(dev));
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
}
#if defined(UARTE_ANY_NONE_ASYNC) && !defined(CONFIG_UART_NRFX_UARTE_NO_IRQ)
/**
* @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 {
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX);
}
#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 && !CONFIG_UART_NRFX_UARTE_NO_IRQ */
#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;
nrf_uarte_baudrate_t nrf_baudrate;
/* calculated baudrate divisor */
if (UARTE_ANY_HIGH_SPEED && (config->clock_freq > NRF_UARTE_BASE_FREQUENCY_16MHZ)) {
nrf_baudrate = UARTE_GET_CUSTOM_BAUDRATE(config->clock_freq, baudrate);
} else {
nrf_baudrate = NRF_BAUDRATE(baudrate);
}
if (nrf_baudrate == 0) {
return -EINVAL;
}
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
if (IS_CBWT(dev)) {
struct uarte_async_rx_cbwt *cbwt_data = config->cbwt_data;
cbwt_data->bounce_buf_swap_len = UARTE_BUF_SWAP_LEN(config->bounce_buf_len,
baudrate);
}
#endif
#ifdef UARTE_BAUDRATE_RETENTION_WORKAROUND
if (config->flags & UARTE_CFG_FLAG_VOLATILE_BAUDRATE) {
struct uarte_nrfx_data *data = dev->data;
data->nrf_baudrate = nrf_baudrate;
} else {
nrf_uarte_baudrate_set(get_uarte_instance(dev), 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 NRF_UARTE_HAS_STOP_MODES
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
#ifndef NRF_UARTE_HAS_FRAME_SIZE
if (cfg->data_bits != UART_CFG_DATA_BITS_8) {
return -ENOTSUP;
}
#endif
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 NRF_UARTE_HAS_PARITY_TYPES
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 NRF_UARTE_HAS_PARITY_TYPES
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
#if NRF_UARTE_HAS_FRAME_SIZE
uarte_cfg.frame_size = DATABITS_TO_NRF_FRAMESIZE(cfg->data_bits);
uarte_cfg.endian = NRF_UARTE_ENDIAN_MSB;
#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;
}
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;
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
nrf_uarte_enable(uarte);
#if UARTE_BAUDRATE_RETENTION_WORKAROUND
if (config->flags & UARTE_CFG_FLAG_VOLATILE_BAUDRATE) {
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) {
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY
if (IS_CBWT_LEGACY(dev)) {
nrf_timer_task_trigger(config->timer_regs, NRF_TIMER_TASK_START);
for (int i = 0; i < data->async->rx.flush_cnt; i++) {
nrf_timer_task_trigger(config->timer_regs, NRF_TIMER_TASK_COUNT);
}
}
#endif
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)
static void rx_timeout(struct k_timer *timer);
static void tx_timeout(struct k_timer *timer);
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 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);
}
#ifdef UARTE_HAS_FRAME_TIMEOUT
#ifdef UARTE_HAS_DMAEND
#define FRAME_TIMEOUT_SHORT_ACTIVE NRF_UARTE_SHORT_FRAMETIMEOUT_DMAEND
#define FRAME_TIMEOUT_SHORTS_ALL \
(NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX | NRF_UARTE_SHORT_FRAMETIMEOUT_DMAEND)
#else
#define FRAME_TIMEOUT_SHORT_ACTIVE NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX
#define FRAME_TIMEOUT_SHORTS_ALL NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX
#endif
static void frame_timeout_shorts_disable(NRF_UARTE_Type *uarte)
{
nrf_uarte_shorts_disable(uarte, FRAME_TIMEOUT_SHORTS_ALL);
}
static void frame_timeout_shorts_enable(NRF_UARTE_Type *uarte)
{
nrf_uarte_shorts_enable(uarte, FRAME_TIMEOUT_SHORT_ACTIVE);
}
#endif
static void rx_disable_finalize(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uart_event evt = {
.type = UART_RX_DISABLED,
};
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 |
NRF_UARTE_INT_RXDRDY_MASK;
async_rx->enabled = false;
nrf_uarte_int_disable(get_uarte_instance(dev), rx_int_mask);
if (LOW_POWER_ENABLED(cfg)) {
uint32_t key = irq_lock();
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
irq_unlock(key);
}
user_callback(dev, (struct uart_event *)&evt);
/* runtime PM is put after the callback. In case uart is re-enabled from that
* callback we avoid suspending/resuming the device.
*/
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_put(dev);
}
}
/** @brief Trigger RX stop.
*
* Function triggers RX stop and sets the flag if it is expected that RXTO will be generated.
*
* @param dev Device.
* @param force If true then RXTO is expected, if false then it depends on presence of the next
* buffer.
*/
static ALWAYS_INLINE void trigger_stoprx(const struct device *dev, bool force)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
/* RXTO is not expected after triggering STOPRX if there is ENDRX_STARTRX short.
* It is enabled if there is a second buffer.
*/
async_rx->stopped = force ? true : (async_rx->next_buf == NULL);
nrf_uarte_task_trigger(get_uarte_instance(dev), NRF_UARTE_TASK_STOPRX);
}
#ifdef UARTE_HAS_FRAME_TIMEOUT
static ALWAYS_INLINE void trigger_rx_end(const struct device *dev)
{
#ifdef UARTE_HAS_DMAEND
nrf_uarte_task_trigger(get_uarte_instance(dev), NRF_UARTE_TASK_DMAEND);
#else
trigger_stoprx(dev, false);
#endif
}
#endif
static int rx_disable(const struct device *dev, bool api)
{
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;
k_timer_stop(&async_rx->timer);
key = irq_lock();
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
if (cbwt_data) {
nrf_timer_event_clear(cfg->timer_regs,
nrf_timer_compare_event_get(UARTE_TIMER_BUF_SWITCH_CH));
nrf_timer_event_clear(cfg->timer_regs,
nrf_timer_compare_event_get(UARTE_TIMER_USR_CNT_CH));
nrf_timer_int_disable(cfg->timer_regs,
nrf_timer_compare_int_get(UARTE_TIMER_BUF_SWITCH_CH) |
nrf_timer_compare_int_get(UARTE_TIMER_USR_CNT_CH));
nrf_uarte_shorts_disable(cfg->uarte_regs, NRF_UARTE_SHORT_ENDRX_STARTRX);
}
#endif
async_rx->stoprx_deferred = false;
if (async_rx->next_buf != NULL) {
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
if (!IS_CBWT(dev) && nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) {
/* The short may have already started the next buffer. Stopping it now
* would overwrite RX.AMOUNT before the pending ENDRX is processed.
* RX remains active until that ISR runs, so ENDRX must be serviced
* before the next buffer fills.
*/
async_rx->stoprx_deferred = true;
} else {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
}
}
async_rx->enabled = false;
if (api) {
async_rx->discard_fifo = true;
}
#ifdef UARTE_HAS_FRAME_TIMEOUT
frame_timeout_shorts_disable(uarte);
#endif
if (!async_rx->stoprx_deferred) {
trigger_stoprx(dev, true);
}
irq_unlock(key);
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;
if (async_rx->buf == NULL) {
return -EFAULT;
}
return rx_disable(dev, true);
}
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
static uint32_t get_byte_cnt(NRF_TIMER_Type *timer)
{
nrf_timer_task_trigger(timer, nrf_timer_capture_task_get(UARTE_TIMER_CAPTURE_CH));
nrf_barrier_w();
return nrf_timer_cc_get(timer, UARTE_TIMER_CAPTURE_CH);
}
static void rx_buf_req(const struct device *dev)
{
struct uart_event evt = {
.type = UART_RX_BUF_REQUEST,
};
user_callback(dev, &evt);
}
static bool notify_rx_rdy(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
size_t len = cbwt_data->usr_wr_off - cbwt_data->usr_rd_off;
if (len == 0) {
return async_rx->buf != NULL;
}
struct uart_event evt = {
.type = UART_RX_RDY,
.data.rx.buf = async_rx->buf,
.data.rx.len = len,
.data.rx.offset = cbwt_data->usr_rd_off
};
user_callback(dev, &evt);
cbwt_data->usr_rd_off += len;
if (cbwt_data->usr_rd_off == async_rx->buf_len) {
rx_buf_release(dev, async_rx->buf);
async_rx->buf = async_rx->next_buf;
async_rx->buf_len = async_rx->next_buf_len;
async_rx->next_buf_len = 0;
async_rx->next_buf = 0;
cbwt_data->usr_rd_off = 0;
cbwt_data->usr_wr_off = 0;
if (async_rx->buf_len == 0) {
return false;
}
/* Set past value to ensure that event will not expire after clearing but
* before setting the new value.
*/
nrf_timer_cc_set(cfg->timer_regs, UARTE_TIMER_USR_CNT_CH, cbwt_data->cc_usr - 1);
nrf_timer_event_clear(cfg->timer_regs,
nrf_timer_compare_event_get(UARTE_TIMER_USR_CNT_CH));
cbwt_data->cc_usr += async_rx->buf_len;
nrf_timer_cc_set(cfg->timer_regs, UARTE_TIMER_USR_CNT_CH, cbwt_data->cc_usr);
/* Check if CC is already in the past. In that case trigger CC handling.*/
if (cbwt_data->cc_usr <= get_byte_cnt(cfg->timer_regs)) {
atomic_or(&data->flags, UARTE_FLAG_LATE_CC);
NRFX_IRQ_PENDING_SET(cfg->timer_irqn);
} else {
atomic_and(&data->flags, ~UARTE_FLAG_LATE_CC);
}
}
return true;
}
static void anomaly_byte_handle(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint8_t curr_byte, anomaly_byte;
uint32_t diff;
if (cbwt_data->anomaly_byte_addr == NULL) {
return;
}
diff = cfg->uarte_regs->DMA.RX.PTR - (uint32_t)cbwt_data->curr_bounce_buf;
/* Anomaly can be checked only if more than 1 byte is received to the current buffer. */
if (diff < 2) {
return;
}
if (IS_ENABLED(UARTE_ANY_CACHE) && (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(cbwt_data->curr_bounce_buf, 1);
sys_cache_data_invd_range(cbwt_data->anomaly_byte_addr, 1);
}
curr_byte = cbwt_data->curr_bounce_buf[0];
anomaly_byte = *cbwt_data->anomaly_byte_addr;
if ((curr_byte == UARTE_MAGIC_BYTE) && (anomaly_byte != UARTE_MAGIC_BYTE)) {
#ifdef UARTE_ANY_CACHE
if (cfg->flags & UARTE_CFG_FLAG_CACHEABLE) {
/* We cannot write directly to curr_bounce_buf as it is written by
* DMA and with cache operations data may be overwritten. Copying
* need to be postponed to the moment when user buffer is filled.
*/
cbwt_data->anomaly_byte = anomaly_byte;
cbwt_data->anomaly_byte_dst = &cbwt_data->curr_bounce_buf[0];
} else {
cbwt_data->curr_bounce_buf[0] = anomaly_byte;
}
#else
cbwt_data->curr_bounce_buf[0] = anomaly_byte;
#endif
}
cbwt_data->anomaly_byte_addr = NULL;
}
static uint32_t fill_usr_buf(const struct device *dev, uint32_t len)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint8_t *buf = cfg->bounce_buf[cbwt_data->bounce_idx];
uint32_t usr_rem = async_rx->buf_len - cbwt_data->usr_wr_off;
uint32_t bounce_rem = cbwt_data->bounce_limit - cbwt_data->bounce_off;
uint32_t cpy_len = min3(bounce_rem, usr_rem, len);
__ASSERT(cpy_len + cbwt_data->bounce_off <= cfg->bounce_buf_len,
"Exceeding the buffer cpy_len:%d off:%d limit:%d",
cpy_len, cbwt_data->bounce_off, cbwt_data->bounce_limit);
if (IS_ENABLED(UARTE_ANY_CACHE) && (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(&buf[cbwt_data->bounce_off], cpy_len);
}
memcpy(&async_rx->buf[cbwt_data->usr_wr_off], &buf[cbwt_data->bounce_off], cpy_len);
#ifdef UARTE_ANY_CACHE
if ((buf == cbwt_data->anomaly_byte_dst) && (cbwt_data->bounce_off == 0)) {
async_rx->buf[cbwt_data->usr_wr_off] = cbwt_data->anomaly_byte;
cbwt_data->anomaly_byte_dst = NULL;
}
#endif
cbwt_data->bounce_off += cpy_len;
cbwt_data->usr_wr_off += cpy_len;
cbwt_data->last_cnt += cpy_len;
if (cbwt_data->bounce_off == cbwt_data->bounce_limit) {
/* Bounce buffer drained */
cbwt_data->bounce_idx = cbwt_data->bounce_idx == 0 ? 1 : 0;
cbwt_data->bounce_off = 0;
cbwt_data->bounce_limit = cfg->bounce_buf_len;
}
return cpy_len;
}
static bool update_usr_buf(const struct device *dev, uint32_t len, bool notify_any, bool buf_req)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
anomaly_byte_handle(dev);
do {
uint32_t cpy_len = len ? fill_usr_buf(dev, len) : 0;
bool usr_buf_full = cbwt_data->usr_wr_off == async_rx->buf_len;
len -= cpy_len;
if (((len == 0) && notify_any) || usr_buf_full) {
if (!notify_rx_rdy(dev)) {
return false;
}
if (usr_buf_full && buf_req) {
rx_buf_req(dev);
}
}
} while (len > 0);
return true;
}
static void prepare_bounce_buf(const struct device *dev, uint8_t *buf,
size_t swap_len, size_t len)
{
const struct uarte_nrfx_config *cfg = dev->config;
buf[0] = UARTE_MAGIC_BYTE;
for (size_t i = swap_len; i < len; i++) {
buf[i] = UARTE_MAGIC_BYTE;
}
if (IS_ENABLED(UARTE_ANY_CACHE) && (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_flush_range(buf, 1);
sys_cache_data_flush_range(&buf[swap_len], len);
}
}
/* This function is responsible for swapping the bounce buffer and it is the most
* tricky part of the solution. Receiver is continuously working and we want to
* change DMA pointer on the fly. DMA is also incrementing that pointer so there are
* moments in the reception when updating the pointer will result in different behavior.
*
* There are two main cases that need to be handled:
* 1. PTR is updated and there was no byte boundary (in the middle of a byte or there is
* no byte on the line). It is a safe spot.
*
* The most common and simplest case. PTR is update but since
* DMA already started the reception of the previous byte it means that next byte will
* be stored in the previous PTR and bytes following that byte will be stored to the
* new bounce buffer
*
* 2. Updating the pointer collided with byte boundary.
*
* RXDRDY and RXSTARTED events are used to detect if collision occurred.
* There are few scenarios that may happen and the driver must detect which one occurred.
* Detection is done by reading back the PTR register. Following cases are considered:
*
* - PTR did not change. It means that it was written after byte boundary. It is the same
* case as if PTR was updated in the safe spot.
*
* - PTR is updated by 1. There is an anomaly and it is unclear where next byte will be
* copied. PTR state indicates that it should be copied to the beginning of the new
* bounce buffer but it might be copied to the previous bounce buffer. Both locations
* are written with a magic byte (0xAA) and later on it is checked which location has
* changed and if byte was written to the previous bounce buffer it is copied to the
* start of the new bounce buffer.
*
* - PTR is not updated with the new bounce buffer location. DMA is incrementing PTR content
* and it is possible that SW writes new value between read and modify and DMA may
* overwrite value written by the driver. In that case reception continuous to the
* previous bounce buffer and swap procedure need to be repeated.
*/
static int bounce_buf_swap(const struct device *dev, uint8_t *prev_bounce_buf)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint32_t prev_buf_cnt, new_cnt, cnt, ptr;
uint32_t prev_buf_inc = 1;
int key;
key = irq_lock();
/* Clear events that indicates byte boundary and set PTR. If events are set
* after PTR is set then we know that setting PTR collided with byte boundary.
*/
nrf_uarte_event_clear(cfg->uarte_regs, NRF_UARTE_EVENT_RXSTARTED);
nrf_uarte_event_clear(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY);
cfg->uarte_regs->DMA.RX.PTR = (uint32_t)cbwt_data->curr_bounce_buf;
cnt = get_byte_cnt(cfg->timer_regs);
if (!nrf_uarte_event_check(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY) &&
!nrf_uarte_event_check(cfg->uarte_regs, NRF_UARTE_EVENT_RXSTARTED)) {
/* RXDRDY did not happen when PTR was set. Safest case. PTR was updated
* correctly. Last byte will be received to the previous buffer.
*/
new_cnt = 0;
prev_buf_cnt = cnt - cbwt_data->last_cnt;
goto no_collision;
}
/* Setting PTR collided with byte boundary we need to detect what happened. */
while (!nrf_uarte_event_check(cfg->uarte_regs, NRF_UARTE_EVENT_RXSTARTED)) {
}
/* Read pointer when there is no new byte coming. */
do {
cnt = get_byte_cnt(cfg->timer_regs);
ptr = cfg->uarte_regs->DMA.RX.PTR;
} while (cnt != get_byte_cnt(cfg->timer_regs));
new_cnt = ptr - (uint32_t)cbwt_data->curr_bounce_buf;
prev_buf_cnt = cnt - cbwt_data->last_cnt;
if (new_cnt == 0) {
/* New PTR is not incremented. It was written after LIST post ENDRX
* incrementation.
*/
} else if (new_cnt == 1) {
/* new_cnt == 1. New PTR incremented. It's possible that data is already
* copied to that new location or it is written to the tail of the previous
* bounce buffer. We try to detect what happens.
*/
prev_buf_inc = 0;
cbwt_data->anomaly_byte_addr =
&prev_bounce_buf[cbwt_data->bounce_off + prev_buf_cnt];
} else if (new_cnt <= cfg->bounce_buf_len) {
prev_buf_inc = 0;
prev_buf_cnt = cnt - cbwt_data->last_cnt - (new_cnt - 1);
} else {
/* New PTR value is not set. Re-set PTR is needed. Transfer continues to
* previous buffer whole buffer swapping need to be repeat.
*/
irq_unlock(key);
return -EAGAIN;
}
no_collision:
cbwt_data->bounce_limit = cbwt_data->bounce_off + prev_buf_cnt + prev_buf_inc;
__ASSERT(cbwt_data->bounce_limit < cfg->bounce_buf_len,
"Too high limit (%d, max:%d), increase latency",
cbwt_data->bounce_limit, cfg->bounce_buf_len);
irq_unlock(key);
return prev_buf_cnt;
}
static size_t get_swap_len(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
return cbwt_data->bounce_buf_swap_len;
#else
return cfg->bounce_buf_swap_len;
#endif
}
static void bounce_buf_switch(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
int new_data = cbwt_data->cc_swap - cbwt_data->last_cnt;
uint8_t *prev_bounce_buf = cbwt_data->curr_bounce_buf;
int prev_cnt;
/* Fill user buffer with all pending data. */
if (!update_usr_buf(dev, new_data < 0 ? 0 : new_data, false, true)) {
rx_disable(dev, false);
return;
}
cbwt_data->curr_bounce_buf = (cbwt_data->curr_bounce_buf == cfg->bounce_buf[0]) ?
cfg->bounce_buf[1] : cfg->bounce_buf[0];
prepare_bounce_buf(dev, cbwt_data->curr_bounce_buf, get_swap_len(dev),
cfg->bounce_buf_len);
/* Swapping may need retry. */
while ((prev_cnt = bounce_buf_swap(dev, prev_bounce_buf)) < 0) {
}
/* Update user buffer with data that was received during swapping. */
if (update_usr_buf(dev, prev_cnt, false, true)) {
/* Set compare event for next moment when bounce buffers need to be swapped. */
cbwt_data->cc_swap += get_swap_len(dev);
__ASSERT(cbwt_data->cc_swap > get_byte_cnt(cfg->timer_regs),
"Setting CC too late next:%d cnt:%d",
cbwt_data->cc_swap, get_byte_cnt(cfg->timer_regs));
nrf_timer_cc_set(cfg->timer_regs, UARTE_TIMER_BUF_SWITCH_CH, cbwt_data->cc_swap);
} else {
/* Stop RX. */
rx_disable(dev, false);
}
}
static void usr_buf_complete(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint32_t rem = async_rx->buf_len - cbwt_data->usr_wr_off;
__ASSERT_NO_MSG(rem <= (get_byte_cnt(cfg->timer_regs) - cbwt_data->last_cnt));
if (!update_usr_buf(dev, rem, true, true)) {
/* Stop RX if there is no next buffer. */
rx_disable(dev, false);
}
}
static void notify_new_data(const struct device *dev, bool buf_req)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint32_t cnt = get_byte_cnt(cfg->timer_regs);
uint32_t new_data = cnt - cbwt_data->last_cnt;
(void)update_usr_buf(dev, new_data, true, buf_req);
}
static void cbwt_rx_timeout(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(timer);
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
if (nrf_uarte_event_check(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY)) {
nrf_uarte_event_clear(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY);
async_rx->idle_cnt = 0;
} else {
async_rx->idle_cnt++;
if (async_rx->idle_cnt == (RX_TIMEOUT_DIV - 1)) {
if (cfg->flags & UARTE_CFG_FLAG_VAR_IRQ) {
if (async_rx->is_in_irq) {
/* TIMER or UARTE interrupt preempted. Lets try again
* later.
*/
return;
}
irq_disable(cfg->uarte_irqn);
irq_disable(cfg->timer_irqn);
}
nrf_uarte_int_enable(cfg->uarte_regs, NRF_UARTE_INT_RXDRDY_MASK);
notify_new_data(dev, true);
k_timer_stop(timer);
if (cfg->flags & UARTE_CFG_FLAG_VAR_IRQ) {
irq_enable(cfg->uarte_irqn);
irq_enable(cfg->timer_irqn);
}
return;
}
}
}
static void cbwt_rx_flush_handle(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint32_t rem_data = get_byte_cnt(cfg->timer_regs) - cbwt_data->last_cnt;
uint32_t bbuf_rem_data = cbwt_data->bounce_limit - cbwt_data->bounce_off;
uint32_t amount;
uint8_t *dst;
nrf_uarte_rx_buffer_set(uarte, cfg->rx_flush_buf, UARTE_HW_RX_FIFO_SIZE);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
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 (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) {
/* FIFO is empty. */
return;
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED);
amount = nrf_uarte_rx_amount_get(uarte);
if (rem_data <= bbuf_rem_data) {
/* instead of -1 it should be -amount but RXDRDY event is not generated
* for bytes following first that goes to FIFO they are generated during flushing.
*/
dst = &cfg->bounce_buf[cbwt_data->bounce_idx][cbwt_data->bounce_off + rem_data - 1];
} else {
/* See comment in if clause. */
dst = &cbwt_data->curr_bounce_buf[rem_data - bbuf_rem_data - 1];
}
if (IS_ENABLED(UARTE_ANY_CACHE) && (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
sys_cache_data_invd_range(cfg->rx_flush_buf, amount);
sys_cache_data_invd_range(dst, amount);
}
memcpy(dst, cfg->rx_flush_buf, amount);
}
static void cbwt_rxto_isr(const struct device *dev, bool do_flush)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
if (async_rx->buf) {
notify_new_data(dev, false);
}
if (async_rx->buf) {
rx_buf_release(dev, async_rx->buf);
async_rx->buf = NULL;
}
if (async_rx->next_buf) {
rx_buf_release(dev, async_rx->next_buf);
async_rx->next_buf = NULL;
}
if (do_flush) {
cbwt_rx_flush_handle(dev);
}
if (async_rx->discard_fifo) {
cbwt_data->discard_fifo = async_rx->discard_fifo;
async_rx->discard_fifo = false;
}
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_STOP);
rx_disable_finalize(dev);
}
static bool timer_ch_evt_check_clear(NRF_TIMER_Type *timer, uint32_t ch)
{
nrf_timer_event_t evt = nrf_timer_compare_event_get(ch);
if (nrf_timer_event_check(timer, evt)) {
nrf_timer_event_clear(timer, evt);
return true;
}
return false;
}
static void timer_isr(const void *arg)
{
const struct device *dev = arg;
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
static const uint32_t flags_to_check = UARTE_FLAG_RX_BUF_REQ |
UARTE_FLAG_TRIG_RXTO |
UARTE_FLAG_LATE_CC;
uint32_t flags = atomic_and(&data->flags, ~flags_to_check);
async_rx->is_in_irq = true;
if (timer_ch_evt_check_clear(cfg->timer_regs, UARTE_TIMER_USR_CNT_CH) ||
(flags & UARTE_FLAG_LATE_CC)) {
usr_buf_complete(dev);
}
/* Must be after user buf complete CC handling. */
if (timer_ch_evt_check_clear(cfg->timer_regs, UARTE_TIMER_BUF_SWITCH_CH)) {
bounce_buf_switch(dev);
}
if (flags & UARTE_FLAG_RX_BUF_REQ) {
rx_buf_req(dev);
}
if (flags & UARTE_FLAG_TRIG_RXTO) {
cbwt_rxto_isr(dev, false);
}
async_rx->is_in_irq = false;
}
static void cbwt_rx_enable(const struct device *dev, bool with_timeout)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
uint32_t rem_data;
uint32_t len = async_rx->buf_len;
uint32_t rx_int_mask = NRF_UARTE_INT_RXTO_MASK |
(with_timeout ? NRF_UARTE_INT_RXDRDY_MASK : 0);
if (cbwt_data->discard_fifo) {
rem_data = 0;
cbwt_data->discard_fifo = false;
} else {
rem_data = get_byte_cnt(cfg->timer_regs) - cbwt_data->last_cnt;
}
cbwt_data->usr_rd_off = 0;
cbwt_data->usr_wr_off = 0;
if (rem_data >= len) {
atomic_or(&data->flags, UARTE_FLAG_TRIG_RXTO);
NRFX_IRQ_PENDING_SET(cfg->timer_irqn);
return;
} else if (rem_data) {
(void)update_usr_buf(dev, rem_data, false, true);
len -= rem_data;
}
prepare_bounce_buf(dev, cfg->bounce_buf[0], get_swap_len(dev), cfg->bounce_buf_len);
cbwt_data->last_cnt = 0;
cbwt_data->bounce_off = 0;
cbwt_data->bounce_idx = 0;
cbwt_data->curr_bounce_buf = cfg->bounce_buf[0];
cbwt_data->bounce_limit = cfg->bounce_buf_len;
/* Enable ArrayList. */
nrf_uarte_shorts_enable(cfg->uarte_regs, NRF_UARTE_SHORT_ENDRX_STARTRX);
nrf_uarte_event_clear(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY);
nrf_uarte_int_enable(cfg->uarte_regs, rx_int_mask);
nrf_uarte_rx_buffer_set(cfg->uarte_regs, cbwt_data->curr_bounce_buf, 1);
nrf_timer_event_clear(cfg->timer_regs,
nrf_timer_compare_event_get(UARTE_TIMER_BUF_SWITCH_CH));
nrf_timer_event_clear(cfg->timer_regs,
nrf_timer_compare_event_get(UARTE_TIMER_USR_CNT_CH));
nrf_timer_int_enable(cfg->timer_regs,
nrf_timer_compare_int_get(UARTE_TIMER_BUF_SWITCH_CH) |
nrf_timer_compare_int_get(UARTE_TIMER_USR_CNT_CH));
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_CLEAR);
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_START);
cbwt_data->cc_usr = len;
cbwt_data->cc_swap = get_swap_len(dev);
nrf_timer_cc_set(cfg->timer_regs, UARTE_TIMER_BUF_SWITCH_CH, get_swap_len(dev));
nrf_timer_cc_set(cfg->timer_regs, UARTE_TIMER_USR_CNT_CH, len);
atomic_or(&data->flags, UARTE_FLAG_RX_BUF_REQ);
nrf_uarte_task_trigger(cfg->uarte_regs, NRF_UARTE_TASK_STARTRX);
async_rx->enabled = true;
NRFX_IRQ_PENDING_SET(cfg->timer_irqn);
}
static int cbwt_uarte_async_init(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
static const uint32_t rx_int_mask = NRF_UARTE_INT_ERROR_MASK |
NRF_UARTE_INT_RXTO_MASK;
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uarte_async_rx_cbwt *cbwt_data = cfg->cbwt_data;
cbwt_data->bounce_buf_swap_len = cfg->bounce_buf_swap_len;
#endif
/* Enable EasyDMA LIST feature (it is exposed in SPIM but not in UARTE). */
*(volatile uint32_t *)((uint32_t)cfg->uarte_regs + 0x714) = 1;
nrf_uarte_int_enable(cfg->uarte_regs, rx_int_mask);
return 0;
}
#endif /* CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER */
#ifdef COUNT_BYTES_WITH_TIMER_COMMON
static int count_byte_with_timer_common_init(const struct device *dev)
{
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 evt = nrf_uarte_event_address_get(cfg->uarte_regs, NRF_UARTE_EVENT_RXDRDY);
uint32_t tsk = nrf_timer_task_address_get(cfg->timer_regs, NRF_TIMER_TASK_COUNT);
int ret;
nrf_timer_mode_set(cfg->timer_regs, NRF_TIMER_MODE_COUNTER);
nrf_timer_bit_width_set(cfg->timer_regs, NRF_TIMER_BIT_WIDTH_32);
ret = nrfx_gppi_conn_alloc(evt, tsk, &async_rx->ppi_h);
if (ret < 0) {
LOG_ERR("GPPI allocation failed: %d", ret);
return ret;
}
nrfx_gppi_conn_enable(async_rx->ppi_h);
return 0;
}
#endif
static int uarte_async_init(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
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);
#ifdef COUNT_BYTES_WITH_TIMER_COMMON
if (IS_CBWT(dev) || IS_CBWT_LEGACY(dev)) {
int ret = count_byte_with_timer_common_init(dev);
if (ret < 0) {
return ret;
}
}
#endif
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
if (IS_CBWT(dev)) {
return cbwt_uarte_async_init(dev);
}
#endif
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)
{
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);
}
nrf_uarte_int_enable(get_uarte_instance(dev), NRF_UARTE_INT_TXSTOPPED_MASK);
}
/* 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)) {
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 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);
}
#ifdef UARTE_HAS_FRAME_TIMEOUT
static uint32_t us_to_bauds(uint32_t baudrate, int32_t timeout)
{
/* Divide baudrate by 1000 to avoid 64 bit division. This approach is not 100% accurate
* but error is insignificant (within few bauds). Precise timeout my be more important
* for higher baudrates but inaccuracy diminishes with higher baudrate.
*/
uint32_t bauds = ((baudrate / 1000) * timeout) / 1000;
return MIN(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);
bool with_timeout = timeout != SYS_FOREVER_US;
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
if (!IS_CBWT(dev)) {
void *dma_buf;
int ret = 0;
ret = dmm_buffer_in_prepare(cfg->mem_reg, buf, len, &dma_buf);
if (ret < 0) {
return ret;
}
async_rx->usr_buf = buf;
buf = dma_buf;
}
#endif
#ifdef UARTE_HAS_FRAME_TIMEOUT
if (!IS_CBWT(dev) && with_timeout) {
uint32_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE,
(data->uart_config.baudrate), (cfg->baudrate));
nrf_uarte_frame_timeout_set(uarte, us_to_bauds(baudrate, timeout));
}
#endif
if (with_timeout) {
if (!IS_CBWT(dev) && IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) {
async_rx->timeout = K_USEC(timeout);
} else {
async_rx->timeout = with_timeout ?
K_USEC(timeout / RX_TIMEOUT_DIV) : K_NO_WAIT;
}
} else {
async_rx->timeout = K_NO_WAIT;
}
async_rx->buf = buf;
async_rx->buf_len = len;
async_rx->offset = 0;
async_rx->next_buf = NULL;
async_rx->next_buf_len = 0;
async_rx->stoprx_deferred = false;
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
pm_device_runtime_get(dev);
} else if (LOW_POWER_ENABLED(cfg)) {
unsigned int key = irq_lock();
uarte_enable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
irq_unlock(key);
}
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
if (IS_CBWT(dev)) {
cbwt_rx_enable(dev, with_timeout);
return 0;
}
#endif
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 {
if (with_timeout) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
k_timer_start(&async_rx->timer, async_rx->timeout,
async_rx->timeout);
}
}
}
}
nrf_uarte_rx_buffer_set(uarte, buf, len);
if (IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE120) && (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) {
/* Spurious RXTO event was seen on fast instance (UARTE120) thus
* RXTO interrupt is kept enabled only when RX is active.
*/
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
}
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;
#ifdef UARTE_HAS_FRAME_TIMEOUT
if (with_timeout) {
/* Arm the short only after all paths that can return without starting RX. */
frame_timeout_shorts_enable(uarte);
}
#endif
nrf_uarte_int_enable(uarte, rx_int_mask);
async_rx->enabled = true;
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->enabled || async_rx->buf == NULL) {
err = -EACCES;
} else if (async_rx->next_buf == NULL) {
#ifdef CONFIG_HAS_NORDIC_DMM
if (!IS_CBWT(dev)) {
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) {
irq_unlock(key);
return err;
}
async_rx->next_usr_buf = buf;
buf = dma_buf;
}
#endif
async_rx->next_buf = buf;
async_rx->next_buf_len = len;
if (!IS_CBWT(dev)) {
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 void tx_timeout(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(timer);
(void) uarte_nrfx_tx_abort(dev);
}
#ifndef UARTE_HAS_FRAME_TIMEOUT
/** Function is called when idle state is detected on the line. Notify user
* all pending data.
*/
static void rx_idle_line_handle(const struct device *dev)
{
if (!IS_CBWT_LEGACY(dev)) {
trigger_stoprx(dev, false);
return;
}
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
uint32_t len;
if (async_rx->is_in_irq == true) {
return;
}
irq_disable(cfg->uarte_irqn);
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_CAPTURE0);
nrf_barrier_rw();
len = nrf_timer_cc_get(cfg->timer_regs, 0) - async_rx->total_user_byte_cnt;
if ((len > 0) && ((len + async_rx->offset) < async_rx->buf_len)) {
notify_uart_rx_rdy(dev, len);
async_rx->offset += len;
async_rx->total_user_byte_cnt += len;
}
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_RXDRDY_MASK);
irq_enable(cfg->uarte_irqn);
#endif
}
#endif
/**
* 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);
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
if (!async_rx->enabled) {
/* RX is already disabled and timer expired while RX was being disabled. */
return;
}
#ifdef UARTE_HAS_FRAME_TIMEOUT
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
if (IS_CBWT(dev)) {
cbwt_rx_timeout(timer);
return;
}
#endif
bool rxdrdy = nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY);
if (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND) &&
(atomic_and(&data->flags, ~UARTE_FLAG_FTIMEOUT_WATCH) & UARTE_FLAG_FTIMEOUT_WATCH)) {
if (rxdrdy) {
k_timer_start(timer, async_rx->timeout, K_NO_WAIT);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
}
} else {
if (!rxdrdy) {
k_timer_stop(timer);
trigger_rx_end(dev);
}
}
return;
#else /* UARTE_HAS_FRAME_TIMEOUT */
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)) {
k_timer_stop(timer);
rx_idle_line_handle(dev);
return;
}
}
#endif /* UARTE_HAS_FRAME_TIMEOUT */
}
#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)rx_disable(dev, false);
}
static void rxstarted_isr(const struct device *dev)
{
struct uart_event evt = {
.type = UART_RX_BUF_REQUEST,
};
#if !defined(UARTE_HAS_FRAME_TIMEOUT)
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_rx *async_rx = &data->async->rx;
if (!K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
nrf_uarte_int_enable(get_uarte_instance(dev), NRF_UARTE_INT_RXDRDY_MASK);
}
#endif
user_callback(dev, &evt);
}
static void endrx_isr(const struct device *dev, bool rxstarted, bool rxto)
{
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_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
async_rx->is_in_irq = true;
#endif
/* 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_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
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) {
bool start_timeout = false;
/* 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) {
/* Remove the short until the subsequent next buffer is setup */
nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX);
/* If RXSTARTED is not set then it means that second buffer was
* provided not on time. RXSTARTED means that ENDRX_STARTRX
* synchronously started the next DMA. The frame timeout workaround
* is needed only for the delayed software STARTRX below.
*/
if (!rxstarted && !rxto) {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO);
async_rx->stopped = false;
if (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND)) {
data->flags |= UARTE_FLAG_FTIMEOUT_WATCH;
start_timeout = true;
}
}
} else {
if (!K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
k_timer_stop(&async_rx->timer);
}
trigger_stoprx(dev, true);
}
irq_unlock(key);
#ifdef UARTE_HAS_FRAME_TIMEOUT
if (start_timeout && !K_TIMEOUT_EQ(async_rx->timeout, K_NO_WAIT)) {
k_timer_start(&async_rx->timer, async_rx->timeout, K_NO_WAIT);
}
#endif
}
#if defined(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
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 (IS_ENABLED(RX_FRAMETIMEOUT_WORKAROUND)) {
if (atomic_test_and_clear_bit(&data->flags, UARTE_FLAG_FTIMEOUT_WATCH)) {
k_timer_stop(&async_rx->timer);
}
}
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.
*/
if (async_rx->discard_fifo) {
async_rx->discard_fifo = false;
#if defined(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
if (IS_CBWT_LEGACY(dev)) {
/* 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);
}
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
#ifdef UARTE_HAS_FRAME_TIMEOUT
frame_timeout_shorts_disable(uarte);
#endif
if (IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE120) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) {
/* Spurious RXTO event was seen on fast instance (UARTE120) thus
* RXTO interrupt is kept enabled only when RX is active.
*/
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_RXTO_MASK);
}
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
rx_disable_finalize(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(dev);
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(dev);
}
}
static void rxdrdy_isr(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
struct uarte_async_rx *async_rx = &data->async->rx;
if (!async_rx->enabled) {
/* RX is already being disabled, ignore the interrupt. */
return;
}
data->async->rx.idle_cnt = 0;
k_timer_start(&data->async->rx.timer, data->async->rx.timeout, data->async->rx.timeout);
nrf_uarte_int_disable(uarte, NRF_UARTE_INT_RXDRDY_MASK);
}
static bool event_check_clear(NRF_UARTE_Type *uarte, nrf_uarte_event_t event,
uint32_t int_mask, uint32_t int_en_mask)
{
if ((int_mask & int_en_mask) && nrf_uarte_event_check(uarte, event)) {
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);
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);
bool rxto, endrx, rxstarted, rxdrdy, error;
bool endrx_processed = false;
#if defined(UARTE_HAS_FRAME_TIMEOUT) && !defined(UARTE_HAS_DMAEND)
/* The frame timeout short triggers STOPRX. Detect that case to set the flag
* that RXTO is expected.
*/
if (!IS_CBWT(dev) &&
event_check_clear(uarte, NRF_UARTE_EVENT_FRAME_TIMEOUT,
NRF_UARTE_INT_RXTO_MASK, imask) && (async_rx->next_buf == NULL)) {
async_rx->stopped = true;
}
#endif
/* Order of reading those events is important as it must be ensured that processing
* order is maintained.
*/
rxto = event_check_clear(uarte, NRF_UARTE_EVENT_RXTO, NRF_UARTE_INT_RXTO_MASK, imask) &&
(async_rx->stopped == true);
endrx = event_check_clear(uarte, NRF_UARTE_EVENT_ENDRX, NRF_UARTE_INT_ENDRX_MASK, imask);
rxdrdy = event_check_clear(uarte, NRF_UARTE_EVENT_RXDRDY, NRF_UARTE_INT_RXDRDY_MASK, imask);
rxstarted = event_check_clear(uarte, NRF_UARTE_EVENT_RXSTARTED,
NRF_UARTE_INT_RXSTARTED_MASK, imask);
error = event_check_clear(uarte, NRF_UARTE_EVENT_ERROR, NRF_UARTE_INT_ERROR_MASK, imask);
if (error) {
error_isr(dev);
}
if (rxdrdy) {
rxdrdy_isr(dev);
}
/* If next buffer is already provided then handle ENDRX before RXSTARTED. */
if ((endrx || rxstarted) && (async_rx->next_buf != NULL)) {
if (!endrx) {
__ASSERT_NO_MSG(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX));
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
}
endrx_isr(dev, rxstarted, rxto);
endrx_processed = true;
endrx = false;
}
if (rxstarted && async_rx->enabled) {
rxstarted_isr(dev);
}
if (endrx) {
endrx_isr(dev, false, false);
endrx_processed = true;
}
if (async_rx->stoprx_deferred && endrx_processed) {
/* RX.AMOUNT for the previous buffer has now been consumed. It is safe to
* stop the buffer started by ENDRX_STARTRX.
*/
async_rx->stoprx_deferred = false;
trigger_stoprx(dev, true);
}
/* If RXTO is set, check also if STOPRX was triggered as there are cases where RXTO
* is unexpectedly generated with ENDRX and such events shall be discarded.
*/
if (rxto) {
async_rx->stopped = false;
#ifdef CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER
if (IS_CBWT(dev)) {
cbwt_rxto_isr(dev, true);
} else {
rxto_isr(dev);
}
#else
rxto_isr(dev);
#endif
}
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 (!IS_CBWT(dev) &&
(atomic_and(&data->flags, ~UARTE_FLAG_TRIG_RXTO) & UARTE_FLAG_TRIG_RXTO)) {
#ifdef CONFIG_HAS_NORDIC_DMM
const struct uarte_nrfx_config *config = dev->config;
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;
rx_disable_finalize(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 (!(data->flags & UARTE_FLAG_POLL_OUT)) {
data->flags |= UARTE_FLAG_POLL_OUT;
pm_device_runtime_get(dev);
}
}
*config->poll_out_byte = c;
tx_start(dev, config->poll_out_byte, 1);
if (!IS_ENABLED(CONFIG_UART_NRFX_UARTE_NO_IRQ) &&
(IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(config))) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
}
irq_unlock(key);
if (IS_ENABLED(CONFIG_UART_NRFX_UARTE_NO_IRQ)) {
key = wait_tx_ready(dev);
if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT) &&
!(config->flags & UARTE_CFG_FLAG_PPI_ENDTX)) {
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX);
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_TXSTOPPED);
while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) {
}
}
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
if (!(data->flags & UARTE_FLAG_POLL_OUT)) {
data->flags &= ~UARTE_FLAG_POLL_OUT;
pm_device_runtime_put(dev);
}
} else if (LOW_POWER_ENABLED(config)) {
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX);
}
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;
bool already_enabled;
pm_device_runtime_get(dev);
unsigned int key = irq_lock();
already_enabled = data->int_driven->tx_irq_enabled;
if (!already_enabled) {
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);
if (already_enabled) {
pm_device_runtime_put(dev);
}
}
/** Interrupt driven transfer disabling function */
static void uarte_nrfx_irq_tx_disable(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
if (data->int_driven->tx_irq_enabled) {
/* 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);
if (!nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDRX_MASK)) {
pm_device_runtime_get(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);
if (nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDRX_MASK)) {
pm_device_runtime_put_async(dev, K_NO_WAIT);
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)));
}
/** 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_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)
{
int ret;
#ifdef DPPIC_PRESENT
nrf_uarte_publish_clear(uarte, NRF_UARTE_EVENT_ENDTX);
nrf_uarte_subscribe_clear(uarte, NRF_UARTE_TASK_STOPTX);
#endif
ret = nrfx_gppi_conn_alloc(
nrf_uarte_event_address_get(uarte, NRF_UARTE_EVENT_ENDTX),
nrf_uarte_task_address_get(uarte, NRF_UARTE_TASK_STOPTX), &data->ppi_h_endtx);
if (ret < 0) {
LOG_ERR("Failed to allocate PPI Channel");
return ret;
}
nrfx_gppi_conn_enable(data->ppi_h_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 && !IS_ENABLED(CONFIG_UART_NRFX_UARTE_NO_IRQ)) {
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK);
}
}
#ifdef CONFIG_UART_NRFX_UARTE_HFXO_ON_ACTIVE
static void uarte_hfxo_active(struct onoff_manager *mgr,
struct onoff_client *cli,
uint32_t state,
int res)
{
struct uarte_nrfx_data *data = CONTAINER_OF(cli, struct uarte_nrfx_data, hfxo_client);
k_sem_give(&data->hfxo_ready);
}
#endif
static void uarte_pm_resume(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
#ifdef CONFIG_UART_NRFX_UARTE_HFXO_ON_ACTIVE
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
struct uarte_nrfx_data *data = dev->data;
bool isr_mode = k_is_in_isr() || k_is_pre_kernel();
int err;
k_sem_reset(&data->hfxo_ready);
sys_notify_init_callback(&data->hfxo_client.notify, uarte_hfxo_active);
err = onoff_request(mgr, &data->hfxo_client);
__ASSERT_NO_MSG(err >= 0);
/* Don't wait for the HFXO if in an ISR or pre-kernel context */
if (!isr_mode) {
err = k_sem_take(&data->hfxo_ready, K_FOREVER);
__ASSERT_NO_MSG(err == 0);
}
#endif
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || !LOW_POWER_ENABLED(cfg)) {
uarte_periph_enable(dev);
}
}
static int 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;
int err;
(void)data;
#ifdef UARTE_ANY_ASYNC
if (data->async) {
/* Entering inactive state requires device to have no active asynchronous calls. */
if (data->async->rx.enabled || (data->async->tx.len > 0)) {
return -EAGAIN;
}
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_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY)
if (data->async && IS_CBWT_LEGACY(dev)) {
#if NRF_TIMER_HAS_SHUTDOWN
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_SHUTDOWN);
#else
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_STOP);
nrf_timer_task_trigger(cfg->timer_regs, NRF_TIMER_TASK_CLEAR);
#endif
/* Timer/counter value is reset when disabled. */
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);
}
nrf_uarte_disable(uarte);
err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
#ifdef CONFIG_UART_NRFX_UARTE_HFXO_ON_ACTIVE
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_HF);
int onoff_err;
sys_notify_init_callback(&data->hfxo_client.notify, uarte_hfxo_active);
onoff_err = onoff_cancel_or_release(mgr, &data->hfxo_client);
__ASSERT_NO_MSG(onoff_err >= 0);
#endif
return err;
}
static int uarte_nrfx_pm_action(const struct device *dev, enum pm_device_action action)
{
int err = 0;
if (action == PM_DEVICE_ACTION_RESUME) {
uarte_pm_resume(dev);
} else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) {
err = uarte_pm_suspend(dev);
} else {
err = -ENOTSUP;
}
return err;
}
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);
if (!IS_ENABLED(CONFIG_UART_NRFX_UARTE_NO_IRQ)) {
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)
{
__maybe_unused struct uarte_nrfx_data *data = dev->data;
const struct uarte_nrfx_config *cfg = dev->config;
int err;
#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
/* For simulation the DT provided peripheral address needs to be corrected */
((struct pinctrl_dev_config *)cfg->pcfg)->reg = (uintptr_t)cfg->uarte_regs;
#if defined(COUNT_BYTES_WITH_TIMER_COMMON)
if (cfg->timer_regs != NULL) {
((struct uarte_nrfx_config *)dev->config)->timer_regs =
nhw_convert_periph_base_addr(cfg->timer_regs);
}
#endif
#endif
/* Apply sleep state by default.
* If PM is disabled, the default state will be applied in pm_device_driver_init.
*/
(void)pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
#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 CONFIG_UART_NRFX_UARTE_HFXO_ON_ACTIVE
k_sem_init(&data->hfxo_ready, 0, 1);
#endif
#ifdef UARTE_ANY_ASYNC
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);
}
#ifdef CONFIG_DEVICE_DEINIT_SUPPORT
static int uarte_instance_deinit(const struct device *dev)
{
return pm_device_driver_deinit(dev, uarte_nrfx_pm_action);
}
#endif
#define UARTE_TIMER_REG(node) (NRF_TIMER_Type *)DT_REG_ADDR(node)
#define UARTE_TIMER_NODE(idx) \
DT_NODELABEL(NRFX_CONCAT(timer, CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER))
#define UARTE_TIMER_IRQN(idx) DT_IRQN(DT_PHANDLE(UARTE(idx), timer))
#define UARTE_TIMER_IRQ_PRIO(idx) DT_IRQ(DT_PHANDLE(UARTE(idx), timer), priority)
#define UARTE_IS_CBWT(idx) \
UTIL_AND(IS_ENABLED(CONFIG_UART_##idx##_COUNT_BYTES_WITH_TIMER), \
IS_ENABLED(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER))
#define UARTE_IS_CBWT_LEGACY(idx) \
UTIL_AND(IS_ENABLED(CONFIG_UART_##idx##_COUNT_BYTES_WITH_TIMER_LEGACY), \
IS_ENABLED(CONFIG_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER_LEGACY))
#define UARTE_USE_TIMER_PROP(idx) \
COND_CODE_1(UTIL_OR(UARTE_IS_CBWT(idx), UARTE_IS_CBWT_LEGACY(idx)), \
(UARTE_HAS_PROP(idx, timer)), (0))
#define UARTE_COUNT_BYTES_WITH_TIMER_COMMON_CONFIG(idx) \
.timer_regs = COND_CODE_1(UARTE_USE_TIMER_PROP(idx), \
(UARTE_TIMER_REG(DT_PHANDLE(UARTE(idx), timer))), \
(COND_CODE_1(CONFIG_UART_##idx##_NRF_HW_ASYNC, \
(UARTE_TIMER_REG(UARTE_TIMER_NODE(idx))), \
(NULL)))), \
.uarte_irqn = DT_IRQN(UARTE(idx)),
#define UARTE_COUNT_BYTES_WITH_TIMER_CONFIG(idx) \
IF_ENABLED(UARTE_IS_CBWT(idx), \
(.timer_irqn = UARTE_TIMER_IRQN(idx), \
.bounce_buf = { \
uart##idx##_bounce_buf, \
&uart##idx##_bounce_buf[sizeof(uart##idx##_bounce_buf) / 2] \
}, \
.bounce_buf_len = sizeof(uart##idx##_bounce_buf) / 2, \
.bounce_buf_swap_len = UARTE_BUF_SWAP_LEN(sizeof(uart##idx##_bounce_buf) / 2,\
UARTE_PROP(idx, current_speed)), \
.cbwt_data = &uart##idx##_bounce_data,))
#define UARTE_COUNT_BYTES_WITH_TIMER_VALIDATE_CONFIG(idx) \
__ASSERT_NO_MSG(UARTE_TIMER_IRQ_PRIO(idx) == DT_IRQ(UARTE(idx), priority))
#define UARTE_TIMER_IRQ_CONNECT(idx, func) \
IF_ENABLED(UARTE_IS_CBWT(idx), \
(UARTE_COUNT_BYTES_WITH_TIMER_VALIDATE_CONFIG(idx); \
IRQ_CONNECT(UARTE_TIMER_IRQN(idx), UARTE_TIMER_IRQ_PRIO(idx), func, \
DEVICE_DT_GET(UARTE(idx)), 0); \
irq_enable(UARTE_TIMER_IRQN(idx));))
/* Macro sets flag to indicate that uart use different interrupt priority than the system clock. */
#define UARTE_HAS_VAR_PRIO(idx) \
COND_CODE_1(UARTE_IS_CBWT(idx), \
(((DT_IRQ(UARTE(idx), priority) != DT_IRQ(DT_NODELABEL(grtc), priority)) ? \
UARTE_CFG_FLAG_VAR_IRQ : 0)), (0))
#define UARTE_GET_ISR(idx) \
COND_CODE_1(CONFIG_UART_##idx##_ASYNC, (uarte_nrfx_isr_async), (uarte_nrfx_isr_int))
/* Declare interrupt handler for direct ISR. */
#define UARTE_DIRECT_ISR_DECLARE(idx) \
IF_ENABLED(CONFIG_UART_NRFX_UARTE_DIRECT_ISR, ( \
ISR_DIRECT_DECLARE(uarte_##idx##_direct_isr) \
{ \
ISR_DIRECT_PM(); \
UARTE_GET_ISR(idx)(DEVICE_DT_GET(UARTE(idx))); \
return 1; \
} \
))
/* Depending on configuration standard or direct IRQ is connected. */
#define UARTE_IRQ_CONNECT(idx, irqn, prio) \
COND_CODE_1(CONFIG_UART_NRFX_UARTE_NO_IRQ, (), \
(COND_CODE_1(CONFIG_UART_NRFX_UARTE_DIRECT_ISR, \
(IRQ_DIRECT_CONNECT(irqn, prio, uarte_##idx##_direct_isr, 0)), \
(IRQ_CONNECT(irqn, prio, UARTE_GET_ISR(idx), \
DEVICE_DT_GET(UARTE(idx)), 0)))))
#define UARTE_IRQ_CONFIGURE(idx) \
do { \
UARTE_IRQ_CONNECT(idx, DT_IRQN(UARTE(idx)), DT_IRQ(UARTE(idx), priority)); \
irq_enable(DT_IRQN(UARTE(idx))); \
UARTE_TIMER_IRQ_CONNECT(idx, timer_isr) \
} 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 _NRF_DT_FRAMESIZE(data_bits) ((data_bits) == 5 ? NRF_UARTE_FRAME_SIZE_5_BIT : \
(data_bits) == 6 ? NRF_UARTE_FRAME_SIZE_6_BIT : \
(data_bits) == 7 ? NRF_UARTE_FRAME_SIZE_7_BIT : \
(data_bits) == 8 ? NRF_UARTE_FRAME_SIZE_8_BIT : NRF_UARTE_FRAME_SIZE_9_BIT)
/* Convert DT numeric value to HAL enum. */
#define NRF_DT_FRAMESIZE(idx) \
COND_CODE_1(UARTE_HAS_PROP(idx, data_bits), \
(_NRF_DT_FRAMESIZE(UARTE_PROP(idx, data_bits))), \
(NRF_UARTE_FRAME_SIZE_8_BIT))
#define _CFG_DATA_BITS(data_bits) ((data_bits) == 5 ? UART_CFG_DATA_BITS_5 : \
(data_bits) == 6 ? UART_CFG_DATA_BITS_6 : \
(data_bits) == 7 ? UART_CFG_DATA_BITS_7 : \
(data_bits) == 8 ? UART_CFG_DATA_BITS_8 : UART_CFG_DATA_BITS_9)
/* Convert DT numeric value used for data bits to enum specified in the API. */
#define CFG_DATA_BITS(idx) \
COND_CODE_1(UTIL_AND(NRF_UARTE_HAS_FRAME_SIZE, UARTE_HAS_PROP(idx, data_bits)), \
(_CFG_DATA_BITS(UARTE_PROP(idx, data_bits))), \
(UART_CFG_DATA_BITS_8))
/* Get frequency divider that is used to adjust the BAUDRATE value. */
#define UARTE_GET_BAUDRATE_DIV(f_pclk) (f_pclk / NRF_UARTE_BASE_FREQUENCY_16MHZ)
/* 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.
* Additionally, >1Mbaud speeds are calculated using a formula.
*/
#define UARTE_GET_BAUDRATE2(f_pclk, current_speed) \
((f_pclk > NRF_UARTE_BASE_FREQUENCY_16MHZ) && (current_speed > 1000000)) ? \
UARTE_GET_CUSTOM_BAUDRATE(f_pclk, current_speed) : \
(NRF_BAUDRATE(current_speed) / UARTE_GET_BAUDRATE_DIV(f_pclk))
/* Convert DT current-speed to a value that is written to the BAUDRATE register. */
#define UARTE_GET_BAUDRATE(idx) \
UARTE_GET_BAUDRATE2(NRF_PERIPH_GET_FREQUENCY(UARTE(idx)), UARTE_PROP(idx, current_speed))
/* 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,)) \
IF_ENABLED(NRF_UARTE_HAS_FRAME_SIZE, \
(.frame_size = NRF_DT_FRAMESIZE(idx),)) \
}
/* Macro for setting zephyr specific configuration structures. */
#define UARTE_CONFIG(idx) { \
.baudrate = UARTE_PROP(idx, current_speed), \
.data_bits = CFG_DATA_BITS(idx), \
.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, \
}
/* Macro determines if PM actions are interrupt safe.
*
* Non-asynchronous API if RX is disabled is not ISR safe.
*
* Macro must resolve to a literal 1 or 0.
*/
#define UARTE_PM_ISR_SAFE(idx) \
COND_CODE_1( \
CONFIG_UART_##idx##_ASYNC, \
(PM_DEVICE_ISR_SAFE), \
( \
COND_CODE_1( \
UARTE_PROP(idx, disable_rx), \
(PM_DEVICE_ISR_SAFE), \
(0) \
) \
) \
)
/* If UARTE instance is using GPPI during the initialization and GPPI is using
* Ironside then that instance needs to be initialized once Ironside and GPPI is
* ready. GPPI is initialized with Ironside priority +1 so UARTE instance
* is initialized just after that.
*
* Macro determines if delayed initialization needs to be applied.
*/
#define UARTE_INIT_AFTER_GPPI(idx) \
COND_CODE_1(UARTE_IS_CBWT(idx), \
(UTIL_OR(IS_ENABLED(CONFIG_GPPI_EXT_ALLOCATOR_CLI), \
UTIL_AND(IS_ENABLED(CONFIG_NRFX_GPPI_SD2PPI_GLOBAL), \
IS_ENABLED(CONFIG_IRONSIDE_SE_CALL)))), (0))
/* Init phase is delayed to POST_KERNEL if it relies on Ironside+GPPI being ready. */
#define UARTE_INIT_PHASE(idx) \
COND_CODE_1(UARTE_INIT_AFTER_GPPI(idx), (POST_KERNEL), (PRE_KERNEL_1))
#define UARTE_INIT_BASE_PRIO COND_CODE_1(CONFIG_GPPI_EXT_ALLOCATOR_CLI, \
(UTIL_INC(UTIL_INC(CONFIG_IPC_SERVICE_REG_BACKEND_PRIORITY))), \
(UTIL_INC(UTIL_INC(CONFIG_IRONSIDE_SE_CALL_INIT_PRIORITY))))
/* If delayed initialization is used then init priority is derived from Ironside
* communication initialization priority or GPPI external allocator backend priority.
*/
#define UARTE_INIT_PRIO(idx) \
COND_CODE_1(UARTE_INIT_AFTER_GPPI(idx), \
(UARTE_INIT_BASE_PRIO), \
(CONFIG_SERIAL_INIT_PRIORITY))
#define UART_NRF_UARTE_DEVICE(idx) \
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(UARTE(idx)); \
NRF_DT_CHECK_NODE_HAS_REQUIRED_MEMORY_REGIONS(UARTE(idx)); \
UARTE_INT_DRIVEN(idx); \
PINCTRL_DT_DEFINE(UARTE(idx)); \
IF_ENABLED(CONFIG_UART_##idx##_ASYNC, ( \
IF_ENABLED(UARTE_IS_CBWT(idx), \
(static uint8_t uart##idx##_bounce_buf[CONFIG_UART_NRFX_UARTE_BOUNCE_BUF_LEN] \
DMM_MEMORY_SECTION(UARTE(idx)); \
static struct uarte_async_rx_cbwt uart##idx##_bounce_data; \
)) \
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(UARTE_GET_BAUDRATE(idx) > 0, \
"Unsupported baudrate");)) \
static MAYBE_CONST struct uarte_nrfx_config uarte_##idx##z_config = { \
COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, \
(.clock_freq = NRF_PERIPH_GET_FREQUENCY(UARTE(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_HAS_NORDIC_DMM) ? 0 : \
(UARTE_IS_CACHEABLE(idx) ? \
UARTE_CFG_FLAG_CACHEABLE : 0)) | \
((IS_ENABLED(UARTE_BAUDRATE_RETENTION_WORKAROUND) && \
UARTE_IS_CACHEABLE(idx)) ? \
UARTE_CFG_FLAG_VOLATILE_BAUDRATE : 0) | \
UARTE_HAS_VAR_PRIO(idx) | \
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_UARTE_NRFX_UARTE_COUNT_BYTES_WITH_TIMER, \
(UARTE_COUNT_BYTES_WITH_TIMER_CONFIG(idx))) \
IF_ENABLED(COUNT_BYTES_WITH_TIMER_COMMON, \
(UARTE_COUNT_BYTES_WITH_TIMER_COMMON_CONFIG(idx))) \
}; \
UARTE_DIRECT_ISR_DECLARE(idx) \
static int uarte_##idx##_init(const struct device *dev) \
{ \
UARTE_IRQ_CONFIGURE(idx); \
return uarte_instance_init( \
dev, \
IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN)); \
} \
\
PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action, \
UARTE_PM_ISR_SAFE(idx)); \
\
DEVICE_DT_DEINIT_DEFINE(UARTE(idx), \
uarte_##idx##_init, \
uarte_instance_deinit, \
PM_DEVICE_DT_GET(UARTE(idx)), \
&uarte_##idx##_data, \
&uarte_##idx##z_config, \
UARTE_INIT_PHASE(idx), \
UARTE_INIT_PRIO(idx), \
&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, (), ())