Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 125 additions & 2 deletions drivers/serial/uart_nrfx_uarte.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,26 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL);
#define UARTE_HAS_FRAME_TIMEOUT 1
#endif

#define IS_HALF_DUPLEX(unused, prefix, i, _) \
(IS_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i) && DT_PROP_OR(UARTE(i), half_duplex, 0))

#if UARTE_FOR_EACH_INSTANCE(IS_HALF_DUPLEX, (||), (0))
#define UARTE_HALF_DUPLEX 1

#define IS_HALF_DUPLEX_REQUIREMENT_SATISFIED(unused, prefix, i, _) \
(IS_INT_DRIVEN(unused, prefix, i, _) || !IS_HALF_DUPLEX(unused, prefix, i, _))
BUILD_ASSERT(UARTE_FOR_EACH_INSTANCE(IS_HALF_DUPLEX_REQUIREMENT_SATISFIED, (&&), (1)),
"half-duplex requires interrupt driven mode. Other modes are not supported yet.");
#endif

#ifdef UARTE_HALF_DUPLEX
// half-duplex mode uses three pinctrl states
// - pinctrl-0 (default) for RX mode
// - pinctrl-2 for TX mode
#define PINCTRL_STATE_RX PINCTRL_STATE_DEFAULT
#define PINCTRL_STATE_TX PINCTRL_STATE_PRIV_START
#endif

/*
* RX timeout is divided into time slabs, this define tells how many divisions
* should be made. More divisions - higher timeout accuracy and processor usage.
Expand Down Expand Up @@ -180,6 +200,13 @@ struct uarte_nrfx_data {
#ifdef UARTE_ENHANCED_POLL_OUT
uint8_t ppi_ch_endtx;
#endif
#ifdef UARTE_HALF_DUPLEX
enum {
UARTE_MODE_IDLE,
UARTE_MODE_TX,
UARTE_MODE_RX
} pin_mode;
#endif
};

#define UARTE_FLAG_LOW_POWER_TX BIT(0)
Expand Down Expand Up @@ -248,6 +275,10 @@ struct uarte_nrfx_config {
#endif
uint8_t *poll_out_byte;
uint8_t *poll_in_byte;

#ifdef UARTE_HALF_DUPLEX
bool half_duplex;
#endif
};

static inline NRF_UARTE_Type *get_uarte_instance(const struct device *dev)
Expand All @@ -272,6 +303,65 @@ static void endtx_isr(const struct device *dev)

}

#ifdef UARTE_HALF_DUPLEX

static int switch_to_tx_mode(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
const struct uarte_nrfx_config *config = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);

if (data->pin_mode == UARTE_MODE_TX) {
return 0;
}
if (data->pin_mode == UARTE_MODE_IDLE) { // Resume from IDLE state should be in PM action
return -ECANCELED;
}
// NOTE: application is responsible to ensure no ongoing RX
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX);
// NOTE: disabling uart might be practically not required
nrf_uarte_disable(uarte);
int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_TX);
if (ret < 0) {
LOG_WRN("Failed to apply PINCTRL_STATE_TX");
return ret;
}
data->pin_mode = UARTE_MODE_TX;

nrf_uarte_enable(uarte);
return 0;
}

static int switch_to_rx_mode(const struct device *dev)
{
struct uarte_nrfx_data *data = dev->data;
const struct uarte_nrfx_config *config = dev->config;
NRF_UARTE_Type *uarte = get_uarte_instance(dev);

if (data->pin_mode == UARTE_MODE_RX) {
return 0;
}
if (data->pin_mode == UARTE_MODE_IDLE) { // Resume from IDLE state should be in PM action
return -ECANCELED;
}
nrf_uarte_disable(uarte);
int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_RX);
if (ret < 0) {
LOG_WRN("Failed to apply PINCTRL_STATE_RX");
return ret;
}
data->pin_mode = UARTE_MODE_RX;

nrf_uarte_enable(uarte);
if (!config->disable_rx) {
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX);
nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX);
}
return 0;
}

#endif /* UARTE_HALF_DUPLEX */

#ifdef UARTE_ANY_NONE_ASYNC
/**
* @brief Interrupt service routine.
Expand Down Expand Up @@ -328,6 +418,11 @@ static void uarte_nrfx_isr_int(const void *arg)
nrf_uarte_int_disable(uarte,
NRF_UARTE_INT_TXSTOPPED_MASK);
data->int_driven->disable_tx_irq = false;
#ifdef UARTE_HALF_DUPLEX
if (config->half_duplex) {
switch_to_rx_mode(dev);
}
#endif
return;
}

Expand Down Expand Up @@ -1740,9 +1835,16 @@ static int uarte_nrfx_fifo_read(const struct device *dev,
static void uarte_nrfx_irq_tx_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;
unsigned int key = irq_lock();

#ifdef UARTE_HALF_DUPLEX
if (config->half_duplex) {
switch_to_tx_mode(dev);
}
#endif

data->int_driven->disable_tx_irq = false;
data->int_driven->tx_irq_enabled = true;
nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK);
Expand All @@ -1757,6 +1859,8 @@ static void uarte_nrfx_irq_tx_disable(const struct device *dev)
/* TX IRQ will be disabled after current transmission is finished */
data->int_driven->disable_tx_irq = true;
data->int_driven->tx_irq_enabled = false;

// NOTE: switch_to_rx_mode is done in IRQ at the end of transmission
}

/** Interrupt driven transfer ready function */
Expand Down Expand Up @@ -1913,8 +2017,8 @@ static int uarte_instance_init(const struct device *dev,
NRF_UARTE_Type *uarte = get_uarte_instance(dev);
const struct uarte_nrfx_config *cfg = dev->config;

#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) || defined(UARTE_ENHANCED_POLL_OUT) || \
defined(UARTE_ANY_ASYNC)
#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) || defined(UARTE_ENHANCED_POLL_OUT) || \
defined(UARTE_ANY_ASYNC) || defined(UARTE_HALF_DUPLEX)
struct uarte_nrfx_data *data = dev->data;
#endif

Expand All @@ -1930,6 +2034,12 @@ static int uarte_instance_init(const struct device *dev,
return err;
}

#ifdef UARTE_HALF_DUPLEX
if (cfg->half_duplex) {
data->pin_mode = UARTE_MODE_RX;
}
#endif

#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE
err = uarte_nrfx_configure(dev, &data->uart_config);
if (err) {
Expand Down Expand Up @@ -2047,6 +2157,12 @@ static int uarte_nrfx_pm_action(const struct device *dev,
return ret;
}

#ifdef UARTE_HALF_DUPLEX
if (cfg->half_duplex) {
data->pin_mode = UARTE_MODE_RX;
}
#endif

nrf_uarte_enable(uarte);

#ifdef UARTE_ANY_ASYNC
Expand Down Expand Up @@ -2119,6 +2235,12 @@ static int uarte_nrfx_pm_action(const struct device *dev,
wait_for_tx_stopped(dev);
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX | UARTE_FLAG_LOW_POWER_RX, 0);

#ifdef UARTE_HALF_DUPLEX
if (cfg->half_duplex) {
data->pin_mode = UARTE_MODE_IDLE;
}
#endif

ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
return ret;
Expand Down Expand Up @@ -2242,6 +2364,7 @@ static int uarte_nrfx_pm_action(const struct device *dev,
IF_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC, \
(.timer = NRFX_TIMER_INSTANCE( \
CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER),)) \
.half_duplex = DT_PROP_OR(UARTE(idx), half_duplex, false), \
}; \
static int uarte_##idx##_init(const struct device *dev) \
{ \
Expand Down
10 changes: 10 additions & 0 deletions dts/bindings/serial/nordic,nrf-uarte.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@ description: Nordic nRF family UARTE (UART with EasyDMA)
compatible: "nordic,nrf-uarte"

include: ["nordic,nrf-uart-common.yaml", "memory-region.yaml"]

properties:
half-duplex:
type: boolean
description: >
Specifies whether the UARTE instance is configured for half-duplex operation.
In half-duplex mode, the same physical line is used for both transmission and reception,
but not simultaneously. **This feature is implemented on software and TX collision may damage
the controller.** Application is responsible to avoid TX collision and the circuit should
implement protection from TX collision for unexpected situations.
Loading