1077 lines
30 KiB
C
1077 lines
30 KiB
C
/*
|
|
* 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;
|
|
|
|
uint8_t *en_rx_buf;
|
|
size_t en_rx_len;
|
|
|
|
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 receiver is not used. */
|
|
#define UARTE_CFG_FLAG_NO_RX BIT(0)
|
|
|
|
/* If set then instance is using interrupt driven API. */
|
|
#define UARTE_CFG_FLAG_INTERRUPT_DRIVEN_API BIT(1)
|
|
|
|
/**
|
|
* @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 (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;
|
|
}
|
|
data->async->user_callback(dev, &evt, data->async->user_data);
|
|
} else if (event->data.rx.length) {
|
|
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);
|
|
|
|
/* If buffer is not null it indicates that event comes from RX enabling
|
|
* function context. We need to pass provided buffer to the driver.
|
|
*/
|
|
if (adata->en_rx_buf) {
|
|
uint8_t *buf = adata->en_rx_buf;
|
|
size_t len = adata->en_rx_len;
|
|
nrfx_err_t err;
|
|
|
|
adata->en_rx_buf = NULL;
|
|
adata->en_rx_len = 0;
|
|
|
|
err = nrfx_uarte_rx_buffer_set(nrfx_dev, buf, len);
|
|
__ASSERT_NO_MSG(err == NRFX_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
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)) {
|
|
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;
|
|
if (IS_ASYNC_API(dev)) {
|
|
(void)uart_rx_disable(dev);
|
|
}
|
|
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_PM_DEVICE
|
|
enum pm_device_state state;
|
|
|
|
(void)pm_device_state_get(dev, &state);
|
|
if (state != PM_DEVICE_STATE_ACTIVE) {
|
|
return -ECANCELED;
|
|
}
|
|
#endif
|
|
|
|
#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);
|
|
nrfx_uarte_rxdrdy_enable(nrfx_dev);
|
|
} else {
|
|
adata->rx_timeout = K_NO_WAIT;
|
|
}
|
|
|
|
/* Store the buffer. It will be passed to the driver in the event handler.
|
|
* We do that instead of calling nrfx_uarte_rx_buffer_set here to ensure
|
|
* that nrfx_uarte_rx_buffer_set is called when RX enable configuration
|
|
* flags are already known to the driver (e.g. if flushed data shall be
|
|
* kept or not).
|
|
*/
|
|
adata->err = 0;
|
|
adata->en_rx_buf = buf;
|
|
adata->en_rx_len = len;
|
|
|
|
atomic_or(&data->flags, UARTE_DATA_FLAG_RX_ENABLED);
|
|
|
|
err = nrfx_uarte_rx_enable(nrfx_dev, flags);
|
|
if (err != NRFX_SUCCESS) {
|
|
atomic_and(&data->flags, ~UARTE_DATA_FLAG_RX_ENABLED);
|
|
return (err == NRFX_ERROR_BUSY) ? -EBUSY : -EIO;
|
|
}
|
|
|
|
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;
|
|
|
|
#if CONFIG_PM_DEVICE
|
|
enum pm_device_state state;
|
|
|
|
(void)pm_device_state_get(dev, &state);
|
|
if (state != PM_DEVICE_STATE_ACTIVE) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
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 NRF_UARTE_HAS_FRAME_TIMEOUT
|
|
uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_DIS;
|
|
#endif
|
|
|
|
#if defined(UARTE_CONFIG_STOP_Msk)
|
|
switch (cfg->stop_bits) {
|
|
case UART_CFG_STOP_BITS_1:
|
|
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(dev);
|
|
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:
|
|
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);
|
|
}
|
|
|
|
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] \
|
|
UARTE_MEMORY_SECTION(idx) __aligned(4);)) \
|
|
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##_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, (), (), _)
|