blob: 750c211efb067a6654abc8cc550f81a4881082b1 [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @brief Driver for Nordic Semiconductor nRF UARTE
*/
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/irq.h>
#include <zephyr/drivers/uart.h>
#include <zephyr/drivers/serial/uart_async_to_irq.h>
#include <zephyr/pm/device.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/linker/devicetree_regions.h>
#include <zephyr/logging/log.h>
#include <nrfx_uarte.h>
#include <helpers/nrfx_gppi.h>
#include <haly/nrfy_uarte.h>
#define LOG_MODULE_NAME uarte
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_UART_LOG_LEVEL);
#define INSTANCE_INT_DRIVEN(periph, prefix, i, _) \
IS_ENABLED(CONFIG_UART_##prefix##i##_INTERRUPT_DRIVEN)
#define INSTANCE_ASYNC(periph, prefix, i, _) \
IS_ENABLED(CONFIG_UART_##prefix##i##_ASYNC)
#define INSTANCE_POLLING(periph, prefix, id, _) \
UTIL_AND(CONFIG_HAS_HW_NRF_UARTE##prefix##id, \
UTIL_AND(COND_CODE_1(CONFIG_UART_##prefix##id##_INTERRUPT_DRIVEN, (0), (1)), \
COND_CODE_1(CONFIG_UART_##prefix##id##_ASYNC, (0), (1))))
#define INSTANCE_ENHANCED_POLL_OUT(periph, prefix, i, _) \
IS_ENABLED(CONFIG_UART_##prefix##i##_ENHANCED_POLL_OUT)
/* Macro determining if any instance is using interrupt driven API. */
#if (NRFX_FOREACH_ENABLED(UARTE, INSTANCE_INT_DRIVEN, (+), (0), _))
#define UARTE_ANY_INTERRUPT_DRIVEN 1
#else
#define UARTE_ANY_INTERRUPT_DRIVEN 0
#endif
/* Macro determining if any instance is enabled and using ASYNC API. */
#if (NRFX_FOREACH_ENABLED(UARTE, INSTANCE_ASYNC, (+), (0), _))
#define UARTE_ANY_ASYNC 1
#else
#define UARTE_ANY_ASYNC 0
#endif
/* Macro determining if any instance is using only polling API. */
#if (NRFX_FOREACH_ENABLED(UARTE, INSTANCE_POLLING, (+), (0), _))
#define UARTE_ANY_POLLING 1
#else
#define UARTE_ANY_POLLING 0
#endif
/* Macro determining if any instance is using interrupt driven API. */
#if (NRFX_FOREACH_ENABLED(UARTE, INSTANCE_ENHANCED_POLL_OUT, (+), (0), _))
#define UARTE_ENHANCED_POLL_OUT 1
#else
#define UARTE_ENHANCED_POLL_OUT 0
#endif
#if UARTE_ANY_INTERRUPT_DRIVEN || UARTE_ANY_ASYNC
#define UARTE_INT_ASYNC 1
#else
#define UARTE_INT_ASYNC 0
#endif
#if defined(UARTE_CONFIG_PARITYTYPE_Msk)
#define UARTE_ODD_PARITY_ALLOWED 1
#else
#define UARTE_ODD_PARITY_ALLOWED 0
#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
/* Macro for converting numerical baudrate to register value. It is convenient
* to use this approach because for constant input it can calculate nrf setting
* at compile time.
*/
#define NRF_BAUDRATE(baudrate) ((baudrate) == 300 ? 0x00014000 :\
(baudrate) == 600 ? 0x00027000 : \
(baudrate) == 1200 ? NRF_UARTE_BAUDRATE_1200 : \
(baudrate) == 2400 ? NRF_UARTE_BAUDRATE_2400 : \
(baudrate) == 4800 ? NRF_UARTE_BAUDRATE_4800 : \
(baudrate) == 9600 ? NRF_UARTE_BAUDRATE_9600 : \
(baudrate) == 14400 ? NRF_UARTE_BAUDRATE_14400 : \
(baudrate) == 19200 ? NRF_UARTE_BAUDRATE_19200 : \
(baudrate) == 28800 ? NRF_UARTE_BAUDRATE_28800 : \
(baudrate) == 31250 ? NRF_UARTE_BAUDRATE_31250 : \
(baudrate) == 38400 ? NRF_UARTE_BAUDRATE_38400 : \
(baudrate) == 56000 ? NRF_UARTE_BAUDRATE_56000 : \
(baudrate) == 57600 ? NRF_UARTE_BAUDRATE_57600 : \
(baudrate) == 76800 ? NRF_UARTE_BAUDRATE_76800 : \
(baudrate) == 115200 ? NRF_UARTE_BAUDRATE_115200 : \
(baudrate) == 230400 ? NRF_UARTE_BAUDRATE_230400 : \
(baudrate) == 250000 ? NRF_UARTE_BAUDRATE_250000 : \
(baudrate) == 460800 ? NRF_UARTE_BAUDRATE_460800 : \
(baudrate) == 921600 ? NRF_UARTE_BAUDRATE_921600 : \
(baudrate) == 1000000 ? NRF_UARTE_BAUDRATE_1000000 : 0)
#define UARTE_DATA_FLAG_TRAMPOLINE BIT(0)
#define UARTE_DATA_FLAG_RX_ENABLED BIT(1)
struct uarte_async_data {
uart_callback_t user_callback;
void *user_data;
struct k_timer tx_timer;
struct k_timer rx_timer;
k_timeout_t rx_timeout;
/* Keeps the most recent error mask. */
uint32_t err;
uint8_t idle_cnt;
};
/* Device data structure */
struct uarte_nrfx_data {
struct uart_async_to_irq_data *a2i_data;
#if CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uart_config uart_config;
#endif
struct uarte_async_data *async;
atomic_t flags;
uint8_t rx_byte;
};
BUILD_ASSERT(offsetof(struct uarte_nrfx_data, a2i_data) == 0);
/* If set then pins are managed when going to low power mode. */
#define UARTE_CFG_FLAG_GPIO_MGMT BIT(0)
/* If set then receiver is not used. */
#define UARTE_CFG_FLAG_NO_RX BIT(1)
/* If set then instance is using interrupt driven API. */
#define UARTE_CFG_FLAG_INTERRUPT_DRIVEN_API BIT(2)
/**
* @brief Structure for UARTE configuration.
*/
struct uarte_nrfx_config {
const struct uart_async_to_irq_config *a2i_config;
nrfx_uarte_t nrfx_dev;
nrfx_uarte_config_t nrfx_config;
const struct pinctrl_dev_config *pcfg;
uint32_t flags;
LOG_INSTANCE_PTR_DECLARE(log);
};
BUILD_ASSERT(offsetof(struct uarte_nrfx_config, a2i_config) == 0);
#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)
/* Determine if the device has interrupt driven API enabled. */
#define IS_INT_DRIVEN_API(dev) \
(UARTE_ANY_INTERRUPT_DRIVEN && \
(((const struct uarte_nrfx_config *)dev->config)->flags & \
UARTE_CFG_FLAG_INTERRUPT_DRIVEN_API))
/* Determine if the device supports only polling API. */
#define IS_POLLING_API(dev) \
(!UARTE_INT_ASYNC || (((struct uarte_nrfx_data *)dev->data)->async == NULL))
/* Determine if the device supports asynchronous API. */
#define IS_ASYNC_API(dev) (!IS_INT_DRIVEN_API(dev) && !IS_POLLING_API(dev))
static inline const nrfx_uarte_t *get_nrfx_dev(const struct device *dev)
{
const struct uarte_nrfx_config *config = dev->config;
return &config->nrfx_dev;
}
static int callback_set(const struct device *dev, uart_callback_t callback, void *user_data)
{
struct uarte_nrfx_data *data = dev->data;
data->async->user_callback = callback;
data->async->user_data = user_data;
return 0;
}
#if UARTE_ANY_ASYNC
static int api_callback_set(const struct device *dev, uart_callback_t callback, void *user_data)
{
if (!IS_ASYNC_API(dev)) {
return -ENOTSUP;
}
return callback_set(dev, callback, user_data);
}
#endif
static void on_tx_done(const struct device *dev, const nrfx_uarte_event_t *event)
{
struct uarte_nrfx_data *data = dev->data;
struct uart_event evt = {
.type = (event->data.tx.flags & NRFX_UARTE_TX_DONE_ABORTED) ?
UART_TX_ABORTED : UART_TX_DONE,
.data.tx.buf = event->data.tx.p_buffer,
.data.tx.len = event->data.tx.length
};
bool hwfc;
#if CONFIG_UART_USE_RUNTIME_CONFIGURE
hwfc = data->uart_config.flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS;
#else
const struct uarte_nrfx_config *config = dev->config;
hwfc = config->nrfx_config.config.hwfc == NRF_UARTE_HWFC_ENABLED;
#endif
if (hwfc) {
k_timer_stop(&data->async->tx_timer);
}
data->async->user_callback(dev, &evt, data->async->user_data);
}
static void on_rx_done(const struct device *dev, const nrfx_uarte_event_t *event)
{
struct uarte_nrfx_data *data = dev->data;
struct uart_event evt;
if (event->data.rx.length) {
if (data->async->err) {
evt.type = UART_RX_STOPPED;
evt.data.rx_stop.reason = UARTE_ERROR_FROM_MASK(data->async->err);
evt.data.rx_stop.data.buf = event->data.rx.p_buffer;
evt.data.rx_stop.data.len = event->data.rx.length;
/* Keep error code for uart_err_check(). */
if (!IS_INT_DRIVEN_API(dev)) {
data->async->err = 0;
}
} else {
evt.type = UART_RX_RDY,
evt.data.rx.buf = event->data.rx.p_buffer,
evt.data.rx.len = event->data.rx.length,
evt.data.rx.offset = 0;
}
data->async->user_callback(dev, &evt, data->async->user_data);
}
evt.type = UART_RX_BUF_RELEASED;
evt.data.rx_buf.buf = event->data.rx.p_buffer;
data->async->user_callback(dev, &evt, data->async->user_data);
}
static void start_rx_timer(struct uarte_nrfx_data *data)
{
struct uarte_async_data *adata = data->async;
k_timer_start(&adata->rx_timer, adata->rx_timeout, K_NO_WAIT);
}
static void on_rx_byte(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_data *adata = data->async;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
nrfx_uarte_rxdrdy_disable(nrfx_dev);
adata->idle_cnt = RX_TIMEOUT_DIV;
start_rx_timer(data);
}
static void on_rx_buf_req(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_data *adata = data->async;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
struct uart_event evt = {
.type = UART_RX_BUF_REQUEST
};
/* If counter reached zero that indicates that timeout was reached and
* reception of one buffer was terminated to restart another transfer.
*/
if (!K_TIMEOUT_EQ(adata->rx_timeout, K_NO_WAIT)) {
/* Read and clear any pending new data information. */
nrfx_uarte_rx_new_data_check(nrfx_dev);
nrfx_uarte_rxdrdy_enable(nrfx_dev);
}
data->async->user_callback(dev, &evt, data->async->user_data);
}
static void on_rx_disabled(const struct device *dev, struct uarte_nrfx_data *data)
{
struct uart_event evt = {
.type = UART_RX_DISABLED
};
atomic_and(&data->flags, ~UARTE_DATA_FLAG_RX_ENABLED);
k_timer_stop(&data->async->rx_timer);
data->async->user_callback(dev, &evt, data->async->user_data);
}
static void trigger_handler(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
if (UARTE_ANY_INTERRUPT_DRIVEN &&
atomic_and(&data->flags, ~UARTE_DATA_FLAG_TRAMPOLINE) &
UARTE_DATA_FLAG_TRAMPOLINE) {
uart_async_to_irq_trampoline_cb(dev);
}
}
static void evt_handler(nrfx_uarte_event_t const *event, void *context)
{
const struct device *dev = context;
struct uarte_nrfx_data *data = dev->data;
switch (event->type) {
case NRFX_UARTE_EVT_TX_DONE:
on_tx_done(dev, event);
break;
case NRFX_UARTE_EVT_RX_DONE:
on_rx_done(dev, event);
break;
case NRFX_UARTE_EVT_RX_BYTE:
on_rx_byte(dev);
break;
case NRFX_UARTE_EVT_ERROR:
data->async->err = event->data.error.error_mask;
break;
case NRFX_UARTE_EVT_RX_BUF_REQUEST:
on_rx_buf_req(dev);
break;
case NRFX_UARTE_EVT_RX_DISABLED:
on_rx_disabled(dev, data);
break;
case NRFX_UARTE_EVT_RX_BUF_TOO_LATE:
/* No support */
break;
case NRFX_UARTE_EVT_TRIGGER:
trigger_handler(dev);
break;
default:
__ASSERT_NO_MSG(0);
}
}
static int api_tx(const struct device *dev, const uint8_t *buf, size_t len, int32_t timeout)
{
struct uarte_nrfx_data *data = dev->data;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
nrfx_err_t err;
bool hwfc;
#if CONFIG_UART_USE_RUNTIME_CONFIGURE
hwfc = data->uart_config.flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS;
#else
const struct uarte_nrfx_config *config = dev->config;
hwfc = config->nrfx_config.config.hwfc == NRF_UARTE_HWFC_ENABLED;
#endif
err = nrfx_uarte_tx(nrfx_dev, buf, len, 0);
if (err != NRFX_SUCCESS) {
return (err == NRFX_ERROR_BUSY) ? -EBUSY : -EIO;
}
if (hwfc && timeout != SYS_FOREVER_US) {
k_timer_start(&data->async->tx_timer, K_USEC(timeout), K_NO_WAIT);
}
return 0;
}
static int api_tx_abort(const struct device *dev)
{
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
nrfx_err_t err;
err = nrfx_uarte_tx_abort(nrfx_dev, false);
return (err == NRFX_SUCCESS) ? 0 : -EFAULT;
}
static void tx_timeout_handler(struct k_timer *timer)
{
const struct device *dev = k_timer_user_data_get(timer);
(void)api_tx_abort(dev);
}
static void rx_timeout_handler(struct k_timer *timer)
{
const struct device *dev = (const struct device *)k_timer_user_data_get(timer);
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_data *adata = data->async;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
if (nrfx_uarte_rx_new_data_check(nrfx_dev)) {
adata->idle_cnt = RX_TIMEOUT_DIV - 1;
} else {
adata->idle_cnt--;
if (adata->idle_cnt == 0) {
(void)nrfx_uarte_rx_abort(nrfx_dev, false, false);
return;
}
}
start_rx_timer(data);
}
/* Determine if RX FIFO content shall be kept when device is being disabled.
* When flow-control is used then we expect to keep RX FIFO content since HWFC
* enforces lossless communication. However, when HWFC is not used (by any instance
* then RX FIFO handling feature is disabled in the nrfx_uarte to save space.
* It is based on assumption that without HWFC it is expected that some data may
* be lost and there are means to prevent that (keeping receiver always opened by
* provided reception buffers on time).
*/
static inline uint32_t get_keep_fifo_content_flag(const struct device *dev)
{
#if CONFIG_UART_USE_RUNTIME_CONFIGURE
struct uarte_nrfx_data *data = dev->data;
if (data->uart_config.flow_ctrl == UART_CFG_FLOW_CTRL_RTS_CTS) {
return NRFX_UARTE_RX_ENABLE_KEEP_FIFO_CONTENT;
}
#else
const struct uarte_nrfx_config *config = dev->config;
if (config->nrfx_config.config.hwfc == NRF_UARTE_HWFC_ENABLED) {
return NRFX_UARTE_RX_ENABLE_KEEP_FIFO_CONTENT;
}
#endif
return 0;
}
static int api_rx_enable(const struct device *dev, uint8_t *buf, size_t len, int32_t timeout)
{
nrfx_err_t err;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
struct uarte_async_data *adata = data->async;
uint32_t flags = NRFX_UARTE_RX_ENABLE_CONT |
get_keep_fifo_content_flag(dev) |
(IS_ASYNC_API(dev) ? NRFX_UARTE_RX_ENABLE_STOP_ON_END : 0);
if (cfg->flags & UARTE_CFG_FLAG_NO_RX) {
return -ENOTSUP;
}
if (timeout != SYS_FOREVER_US) {
adata->idle_cnt = RX_TIMEOUT_DIV + 1;
adata->rx_timeout = K_USEC(timeout / RX_TIMEOUT_DIV);
} else {
adata->rx_timeout = K_NO_WAIT;
}
err = nrfx_uarte_rx_buffer_set(nrfx_dev, buf, len);
if (err != NRFX_SUCCESS) {
return -EIO;
}
err = nrfx_uarte_rx_enable(nrfx_dev, flags);
if (err != NRFX_SUCCESS) {
return (err == NRFX_ERROR_BUSY) ? -EBUSY : -EIO;
}
atomic_or(&data->flags, UARTE_DATA_FLAG_RX_ENABLED);
return 0;
}
static int api_rx_buf_rsp(const struct device *dev, uint8_t *buf, size_t len)
{
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
struct uarte_nrfx_data *data = dev->data;
nrfx_err_t err;
if (!(data->flags & UARTE_DATA_FLAG_RX_ENABLED)) {
return -EACCES;
}
err = nrfx_uarte_rx_buffer_set(nrfx_dev, buf, len);
switch (err) {
case NRFX_SUCCESS:
return 0;
case NRFX_ERROR_BUSY:
return -EBUSY;
default:
return -EIO;
}
}
static int api_rx_disable(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
k_timer_stop(&data->async->rx_timer);
return (nrfx_uarte_rx_abort(get_nrfx_dev(dev), true, false) == NRFX_SUCCESS) ? 0 : -EFAULT;
}
static int api_poll_in(const struct device *dev, unsigned char *c)
{
const struct uarte_nrfx_config *cfg = dev->config;
const nrfx_uarte_t *instance = &cfg->nrfx_dev;
nrfx_err_t err;
if (IS_INT_DRIVEN_API(dev)) {
return uart_fifo_read(dev, c, 1) == 0 ? -1 : 0;
}
if (IS_ASYNC_API(dev)) {
return -EBUSY;
}
err = nrfx_uarte_rx_ready(instance, NULL);
if (err == NRFX_SUCCESS) {
uint8_t *rx_byte = cfg->nrfx_config.rx_cache.p_buffer;
*c = *rx_byte;
err = nrfx_uarte_rx_buffer_set(instance, rx_byte, 1);
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
return 0;
}
return -1;
}
static void api_poll_out(const struct device *dev, unsigned char out_char)
{
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
nrfx_err_t err;
do {
/* When runtime PM is used we cannot use early return because then
* we have no information when UART is actually done with the
* transmission. It reduces UART performance however, polling in
* general is not power efficient and should be avoided in low
* power applications.
*/
err = nrfx_uarte_tx(nrfx_dev, &out_char, 1, NRFX_UARTE_TX_EARLY_RETURN);
__ASSERT(err != NRFX_ERROR_INVALID_ADDR, "Invalid address of the buffer");
if (err == NRFX_ERROR_BUSY) {
if (IS_ENABLED(CONFIG_MULTITHREADING) && k_is_preempt_thread()) {
k_msleep(1);
} else {
Z_SPIN_DELAY(3);
}
}
} while (err == NRFX_ERROR_BUSY);
}
#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(NRF_UARTE_Type *uarte, uint32_t baudrate)
{
nrf_uarte_baudrate_t nrf_baudrate = NRF_BAUDRATE(baudrate);
if (baudrate == 0) {
return -EINVAL;
}
nrfy_uarte_baudrate_set(uarte, nrf_baudrate);
return 0;
}
static int uarte_nrfx_configure(const struct device *dev,
const struct uart_config *cfg)
{
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
struct uarte_nrfx_data *data = dev->data;
nrf_uarte_config_t uarte_cfg;
#if defined(UARTE_CONFIG_STOP_Msk)
switch (cfg->stop_bits) {
case UART_CFG_STOP_BITS_1:
uarte_cfg.stop = NRF_UARTE_STOP_ONE;
break;
case UART_CFG_STOP_BITS_2:
uarte_cfg.stop = NRF_UARTE_STOP_TWO;
break;
default:
return -ENOTSUP;
}
#else
if (cfg->stop_bits != UART_CFG_STOP_BITS_1) {
return -ENOTSUP;
}
#endif
if (cfg->data_bits != UART_CFG_DATA_BITS_8) {
return -ENOTSUP;
}
switch (cfg->flow_ctrl) {
case UART_CFG_FLOW_CTRL_NONE:
uarte_cfg.hwfc = NRF_UARTE_HWFC_DISABLED;
break;
case UART_CFG_FLOW_CTRL_RTS_CTS:
uarte_cfg.hwfc = NRF_UARTE_HWFC_ENABLED;
break;
default:
return -ENOTSUP;
}
#if defined(UARTE_CONFIG_PARITYTYPE_Msk)
uarte_cfg.paritytype = NRF_UARTE_PARITYTYPE_EVEN;
#endif
switch (cfg->parity) {
case UART_CFG_PARITY_NONE:
uarte_cfg.parity = NRF_UARTE_PARITY_EXCLUDED;
break;
case UART_CFG_PARITY_EVEN:
uarte_cfg.parity = NRF_UARTE_PARITY_INCLUDED;
break;
#if defined(UARTE_CONFIG_PARITYTYPE_Msk)
case UART_CFG_PARITY_ODD:
uarte_cfg.parity = NRF_UARTE_PARITY_INCLUDED;
uarte_cfg.paritytype = NRF_UARTE_PARITYTYPE_ODD;
break;
#endif
default:
return -ENOTSUP;
}
if (baudrate_set(nrfx_dev->p_reg, cfg->baudrate) != 0) {
return -ENOTSUP;
}
nrfy_uarte_configure(nrfx_dev->p_reg, &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 */
#if UARTE_ANY_POLLING || UARTE_ANY_INTERRUPT_DRIVEN
static int api_err_check(const struct device *dev)
{
if (IS_POLLING_API(dev)) {
const struct uarte_nrfx_config *cfg = dev->config;
const nrfx_uarte_t *instance = &cfg->nrfx_dev;
uint32_t mask = nrfx_uarte_errorsrc_get(instance);
return mask;
}
struct uarte_nrfx_data *data = dev->data;
uint32_t rv = data->async->err;
data->async->err = 0;
return rv;
}
#endif
static const struct uart_async_to_irq_async_api a2i_api = {
.callback_set = callback_set,
.tx = api_tx,
.tx_abort = api_tx_abort,
.rx_enable = api_rx_enable,
.rx_buf_rsp = api_rx_buf_rsp,
.rx_disable = api_rx_disable,
};
static const struct uart_driver_api uart_nrfx_uarte_driver_api = {
.poll_in = api_poll_in,
.poll_out = api_poll_out,
#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
.configure = uarte_nrfx_configure,
.config_get = uarte_nrfx_config_get,
#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */
#if UARTE_ANY_POLLING || UARTE_ANY_INTERRUPT_DRIVEN
.err_check = api_err_check,
#endif
#if UARTE_ANY_ASYNC
.callback_set = api_callback_set,
.tx = api_tx,
.tx_abort = api_tx_abort,
.rx_enable = api_rx_enable,
.rx_buf_rsp = api_rx_buf_rsp,
.rx_disable = api_rx_disable,
#endif /* UARTE_ANY_ASYNC */
#if UARTE_ANY_INTERRUPT_DRIVEN
UART_ASYNC_TO_IRQ_API_INIT(),
#endif /* UARTE_ANY_INTERRUPT_DRIVEN */
};
static int endtx_stoptx_ppi_init(NRF_UARTE_Type *uarte)
{
nrfx_err_t ret;
uint8_t ch;
ret = nrfx_gppi_channel_alloc(&ch);
if (ret != NRFX_SUCCESS) {
LOG_ERR("Failed to allocate PPI Channel");
return -EIO;
}
nrfx_gppi_channel_endpoints_setup(ch,
nrfy_uarte_event_address_get(uarte, NRF_UARTE_EVENT_ENDTX),
nrfy_uarte_task_address_get(uarte, NRF_UARTE_TASK_STOPTX));
nrfx_gppi_channels_enable(BIT(ch));
return 0;
}
static int start_rx(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
if (IS_INT_DRIVEN_API(dev)) {
return uart_async_to_irq_rx_enable(dev);
}
__ASSERT_NO_MSG(IS_POLLING_API(dev));
nrfx_err_t err;
const nrfx_uarte_t *instance = &cfg->nrfx_dev;
uint8_t *rx_byte = cfg->nrfx_config.rx_cache.p_buffer;
err = nrfx_uarte_rx_buffer_set(instance, rx_byte, 1);
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
err = nrfx_uarte_rx_enable(instance, 0);
__ASSERT_NO_MSG(err == NRFX_SUCCESS || err == NRFX_ERROR_BUSY);
(void)err;
return 0;
}
static void async_to_irq_trampoline(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
uint32_t prev = atomic_or(&data->flags, UARTE_DATA_FLAG_TRAMPOLINE);
if (!(prev & UARTE_DATA_FLAG_TRAMPOLINE)) {
nrfx_uarte_int_trigger(&cfg->nrfx_dev);
}
}
static int uarte_nrfx_init(const struct device *dev)
{
int err;
nrfx_err_t nerr;
const nrfx_uarte_t *nrfx_dev = get_nrfx_dev(dev);
const struct uarte_nrfx_config *cfg = dev->config;
struct uarte_nrfx_data *data = dev->data;
#ifdef CONFIG_ARCH_POSIX
/* For simulation the DT provided peripheral address needs to be corrected */
((struct pinctrl_dev_config *)cfg->pcfg)->reg = (uintptr_t)nrfx_dev->p_reg;
#endif
err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (err < 0) {
return err;
}
if (UARTE_ENHANCED_POLL_OUT && cfg->nrfx_config.tx_stop_on_end) {
err = endtx_stoptx_ppi_init(nrfx_dev->p_reg);
if (err < 0) {
return err;
}
}
if (UARTE_ANY_INTERRUPT_DRIVEN) {
if (cfg->a2i_config) {
err = uart_async_to_irq_init(data->a2i_data, cfg->a2i_config);
if (err < 0) {
return err;
}
}
}
if (IS_ENABLED(UARTE_INT_ASYNC) && data->async) {
k_timer_init(&data->async->rx_timer, rx_timeout_handler, NULL);
k_timer_user_data_set(&data->async->rx_timer, (void *)dev);
k_timer_init(&data->async->tx_timer, tx_timeout_handler, NULL);
k_timer_user_data_set(&data->async->tx_timer, (void *)dev);
}
nerr = nrfx_uarte_init(nrfx_dev, &cfg->nrfx_config,
IS_ENABLED(UARTE_INT_ASYNC) ?
(IS_POLLING_API(dev) ? NULL : evt_handler) : NULL);
if (nerr == NRFX_SUCCESS && !IS_ASYNC_API(dev) && !(cfg->flags & UARTE_CFG_FLAG_NO_RX)) {
err = start_rx(dev);
}
switch (nerr) {
case NRFX_ERROR_INVALID_STATE:
return -EBUSY;
case NRFX_ERROR_BUSY:
return -EACCES;
case NRFX_ERROR_INVALID_PARAM:
return -EINVAL;
default:
return 0;
}
}
#ifdef CONFIG_PM_DEVICE
static int stop_rx(const struct device *dev)
{
const struct uarte_nrfx_config *cfg = dev->config;
if (IS_INT_DRIVEN_API(dev)) {
return uart_async_to_irq_rx_disable(dev);
}
__ASSERT_NO_MSG(IS_POLLING_API(dev));
nrfx_err_t err;
const nrfx_uarte_t *instance = &cfg->nrfx_dev;
err = nrfx_uarte_rx_abort(instance, true, true);
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
return 0;
}
static int uarte_nrfx_pm_action(const struct device *dev,
enum pm_device_action action)
{
const struct uarte_nrfx_config *cfg = dev->config;
int ret;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
if (cfg->flags & UARTE_CFG_FLAG_GPIO_MGMT) {
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
}
if (!IS_ASYNC_API(dev) && !(cfg->flags & UARTE_CFG_FLAG_NO_RX)) {
return start_rx(dev);
}
break;
case PM_DEVICE_ACTION_SUSPEND:
if (!IS_ASYNC_API(dev) && !(cfg->flags & UARTE_CFG_FLAG_NO_RX)) {
stop_rx(dev);
}
if (cfg->flags & UARTE_CFG_FLAG_GPIO_MGMT) {
ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
return ret;
}
}
break;
default:
return -ENOTSUP;
}
return 0;
}
#endif
#if defined(UARTE_CONFIG_STOP_Msk)
#define UARTE_HAS_STOP_CONFIG 1
#endif
#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)
/* Macro returning initial log level. Logs are off for UART used for console. */
#define GET_INIT_LOG_LEVEL(idx) \
COND_CODE_1(DT_HAS_CHOSEN(zephyr_console), \
(DT_SAME_NODE(UARTE(idx), \
DT_CHOSEN(zephyr_console)) ? \
LOG_LEVEL_NONE : CONFIG_UART_LOG_LEVEL), \
(CONFIG_UART_LOG_LEVEL))
/* Macro puts buffers in dedicated section if device tree property is set. */
#define UARTE_MEMORY_SECTION(idx) \
COND_CODE_1(UARTE_HAS_PROP(idx, memory_regions), \
(__attribute__((__section__(LINKER_DT_NODE_REGION_NAME( \
DT_PHANDLE(UARTE(idx), memory_regions)))))), \
())
#define UART_NRF_UARTE_DEVICE(idx) \
LOG_INSTANCE_REGISTER(LOG_MODULE_NAME, idx, GET_INIT_LOG_LEVEL(idx)); \
static uint8_t uarte##idx##_tx_cache[CONFIG_UART_##idx##_TX_CACHE_SIZE] \
UARTE_MEMORY_SECTION(idx) __aligned(4); \
static uint8_t uarte##idx##_rx_cache[CONFIG_UART_##idx##_RX_CACHE_SIZE] \
UARTE_MEMORY_SECTION(idx) __aligned(4); \
static nrfx_uarte_rx_cache_t uarte##idx##_rx_cache_scratch; \
IF_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN, \
(static uint8_t a2i_rx_buf##idx[CONFIG_UART_##idx##_A2I_RX_SIZE];)) \
PINCTRL_DT_DEFINE(UARTE(idx)); \
static const struct uart_async_to_irq_config uarte_a2i_config_##idx = \
UART_ASYNC_TO_IRQ_API_CONFIG_INITIALIZER(&a2i_api, \
async_to_irq_trampoline, \
UARTE_PROP(idx, current_speed), \
uarte##idx##_tx_cache, \
/* nrfx_uarte driver is using the last byte in the */ \
/* cache buffer for keeping a byte that is currently*/\
/* polled out so it cannot be used as a cache buffer*/\
/* by the adaptation layer. */ \
sizeof(uarte##idx##_tx_cache) - 1, \
COND_CODE_1(CONFIG_UART_##idx##_INTERRUPT_DRIVEN, \
(a2i_rx_buf##idx), (NULL)), \
COND_CODE_1(CONFIG_UART_##idx##_INTERRUPT_DRIVEN, \
(sizeof(a2i_rx_buf##idx)), (0)), \
CONFIG_UART_##idx##_A2I_RX_BUF_COUNT, \
LOG_INSTANCE_PTR(LOG_MODULE_NAME, idx)); \
static const struct uarte_nrfx_config uarte_config_##idx = { \
.a2i_config = IS_ENABLED(CONFIG_UART_##idx## _INTERRUPT_DRIVEN) ? \
&uarte_a2i_config_##idx : NULL, \
.nrfx_dev = NRFX_UARTE_INSTANCE(idx), \
.nrfx_config = { \
.p_context = (void *)DEVICE_DT_GET(UARTE(idx)), \
.tx_cache = { \
.p_buffer = uarte##idx##_tx_cache, \
.length = CONFIG_UART_##idx##_TX_CACHE_SIZE \
}, \
.rx_cache = { \
.p_buffer = uarte##idx##_rx_cache, \
.length = CONFIG_UART_##idx##_RX_CACHE_SIZE \
}, \
.p_rx_cache_scratch = &uarte##idx##_rx_cache_scratch, \
.baudrate = NRF_BAUDRATE(UARTE_PROP(idx, current_speed)), \
.interrupt_priority = DT_IRQ(UARTE(idx), priority), \
.config = { \
.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,)) \
}, \
.tx_stop_on_end = IS_ENABLED(CONFIG_UART_##idx##_ENHANCED_POLL_OUT), \
.skip_psel_cfg = true, \
.skip_gpio_cfg = true, \
}, \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx)), \
.flags = (UARTE_PROP(idx, disable_rx) ? UARTE_CFG_FLAG_NO_RX : 0) | \
(IS_ENABLED(CONFIG_UART_##idx##_GPIO_MANAGEMENT) ? \
UARTE_CFG_FLAG_GPIO_MGMT : 0) | \
(IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN) ? \
UARTE_CFG_FLAG_INTERRUPT_DRIVEN_API : 0), \
LOG_INSTANCE_PTR_INIT(log, LOG_MODULE_NAME, idx) \
}; \
static struct uart_async_to_irq_data uarte_a2i_data_##idx; \
static struct uarte_async_data uarte_async_##idx; \
static struct uarte_nrfx_data uarte_data_##idx = { \
.a2i_data = IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN) ? \
&uarte_a2i_data_##idx : NULL, \
IF_ENABLED(CONFIG_UART_USE_RUNTIME_CONFIGURE, \
(.uart_config = { \
.baudrate = UARTE_PROP(idx, current_speed), \
.parity = IS_ENABLED(CONFIG_UART_##idx##_NRF_PARITY_BIT) ? \
UART_CFG_PARITY_EVEN : UART_CFG_PARITY_NONE, \
.stop_bits = UART_CFG_STOP_BITS_1, \
.data_bits = UART_CFG_DATA_BITS_8, \
.flow_ctrl = UARTE_PROP(idx, hw_flow_control) ? \
UART_CFG_FLOW_CTRL_RTS_CTS : UART_CFG_FLOW_CTRL_NONE, \
},)) \
.async = (IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN) || \
IS_ENABLED(CONFIG_UART_##idx##_ASYNC)) ? &uarte_async_##idx : NULL \
}; \
static int uarte_init_##idx(const struct device *dev) \
{ \
COND_CODE_1(INSTANCE_POLLING(_, /*empty*/, idx, _), (), \
( \
IRQ_CONNECT(DT_IRQN(UARTE(idx)), DT_IRQ(UARTE(idx), priority), \
nrfx_isr, nrfx_uarte_##idx##_irq_handler, 0); \
irq_enable(DT_IRQN(UARTE(idx))); \
) \
) \
return uarte_nrfx_init(dev); \
} \
PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action); \
DEVICE_DT_DEFINE(UARTE(idx), \
uarte_init_##idx, \
PM_DEVICE_DT_GET(UARTE(idx)), \
&uarte_data_##idx, \
&uarte_config_##idx, \
PRE_KERNEL_1, \
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
&uart_nrfx_uarte_driver_api)
/* Macro creates device instance if it is enabled in devicetree. */
#define UARTE_DEVICE(periph, prefix, id, _) \
IF_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##id, (UART_NRF_UARTE_DEVICE(prefix##id);))
/* Macro iterates over nrfx_uarte instances enabled in the nrfx_config.h. */
NRFX_FOREACH_ENABLED(UARTE, UARTE_DEVICE, (), (), _)