From 0292896e05f632b412d239d510ff07d0f563bbf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 20 Sep 2024 16:04:58 +0200 Subject: [PATCH 01/21] drivers: serial: nrfx_uarte: Remove CONFIG_UART_n_GPIO_MANAGEMENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a leftover from pre-pinctrl era and no longer makes sense. Driver always manages gpio through pinctrl. Support removed from uart and uarte shims. Signed-off-by: Krzysztof Chruściński --- drivers/serial/Kconfig.nrfx_uart_instance | 9 ------- drivers/serial/uart_nrfx_uart.c | 19 +++++-------- drivers/serial/uart_nrfx_uarte.c | 33 +++++------------------ 3 files changed, 12 insertions(+), 49 deletions(-) diff --git a/drivers/serial/Kconfig.nrfx_uart_instance b/drivers/serial/Kconfig.nrfx_uart_instance index 0840cad7760c99..ed4c011da51d11 100644 --- a/drivers/serial/Kconfig.nrfx_uart_instance +++ b/drivers/serial/Kconfig.nrfx_uart_instance @@ -119,12 +119,3 @@ config UART_$(nrfx_uart_num)_A2I_RX_BUF_COUNT default 0 help Number of chunks into RX space is divided. - -config UART_$(nrfx_uart_num)_GPIO_MANAGEMENT - bool "GPIO management on port $(nrfx_uart_num)" - depends on PM_DEVICE - default y - help - If enabled, the driver will configure the GPIOs used by the uart to - their default configuration when device is powered down. The GPIOs - will be configured back to correct state when UART is powered up. diff --git a/drivers/serial/uart_nrfx_uart.c b/drivers/serial/uart_nrfx_uart.c index 25dab17ea74a0c..618d476e55e2ff 100644 --- a/drivers/serial/uart_nrfx_uart.c +++ b/drivers/serial/uart_nrfx_uart.c @@ -1086,12 +1086,9 @@ static int uart_nrfx_pm_action(const struct device *dev, switch (action) { case PM_DEVICE_ACTION_RESUME: - if (IS_ENABLED(CONFIG_UART_0_GPIO_MANAGEMENT)) { - ret = pinctrl_apply_state(config->pcfg, - PINCTRL_STATE_DEFAULT); - if (ret < 0) { - return ret; - } + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; } nrf_uart_enable(uart0_addr); @@ -1102,13 +1099,9 @@ static int uart_nrfx_pm_action(const struct device *dev, break; case PM_DEVICE_ACTION_SUSPEND: nrf_uart_disable(uart0_addr); - - if (IS_ENABLED(CONFIG_UART_0_GPIO_MANAGEMENT)) { - ret = pinctrl_apply_state(config->pcfg, - PINCTRL_STATE_SLEEP); - if (ret < 0) { - return ret; - } + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); + if (ret < 0) { + return ret; } break; default: diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index c0d0201d29df6d..327fedd180e62b 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -191,19 +191,16 @@ struct uarte_nrfx_data { #define UARTE_LOW_POWER_TX BIT(0) #define UARTE_LOW_POWER_RX BIT(1) -/* If enabled, pins are managed when going to low power mode. */ -#define UARTE_CFG_FLAG_GPIO_MGMT BIT(0) - /* If enabled then ENDTX is PPI'ed to TXSTOP */ -#define UARTE_CFG_FLAG_PPI_ENDTX BIT(1) +#define UARTE_CFG_FLAG_PPI_ENDTX BIT(0) /* If enabled then TIMER and PPI is used for byte counting. */ -#define UARTE_CFG_FLAG_HW_BYTE_COUNTING BIT(2) +#define UARTE_CFG_FLAG_HW_BYTE_COUNTING BIT(1) /* If enabled then UARTE peripheral is disabled when not used. This allows * to achieve lowest power consumption in idle. */ -#define UARTE_CFG_FLAG_LOW_POWER BIT(4) +#define UARTE_CFG_FLAG_LOW_POWER BIT(2) /* Macro for converting numerical baudrate to register value. It is convenient * to use this approach because for constant input it can calculate nrf setting @@ -516,20 +513,6 @@ static int wait_tx_ready(const struct device *dev) return key; } -#if defined(UARTE_ANY_ASYNC) || defined(CONFIG_PM_DEVICE) -static int pins_state_change(const struct device *dev, bool on) -{ - const struct uarte_nrfx_config *config = dev->config; - - if (config->flags & UARTE_CFG_FLAG_GPIO_MGMT) { - return pinctrl_apply_state(config->pcfg, - on ? PINCTRL_STATE_DEFAULT : PINCTRL_STATE_SLEEP); - } - - return 0; -} -#endif - #ifdef UARTE_ANY_ASYNC /* Using Macro instead of static inline function to handle NO_OPTIMIZATIONS case @@ -551,7 +534,7 @@ static int uarte_enable(const struct device *dev, uint32_t mask) int ret; data->async->low_power_mask |= mask; - ret = pins_state_change(dev, true); + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { return ret; } @@ -1293,7 +1276,6 @@ static void async_uart_release(const struct device *dev, uint32_t dir_mask) } uart_disable(dev); - int err = pins_state_change(dev, false); (void)err; __ASSERT_NO_MSG(err == 0); @@ -1924,8 +1906,7 @@ static int uarte_nrfx_pm_action(const struct device *dev, switch (action) { case PM_DEVICE_ACTION_RESUME: - - ret = pins_state_change(dev, true); + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); if (ret < 0) { return ret; } @@ -1994,7 +1975,7 @@ static int uarte_nrfx_pm_action(const struct device *dev, wait_for_tx_stopped(dev); uart_disable(dev); - ret = pins_state_change(dev, false); + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP); if (ret < 0) { return ret; } @@ -2095,8 +2076,6 @@ static int uarte_nrfx_pm_action(const struct device *dev, .pcfg = PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx)), \ .uarte_regs = _CONCAT(NRF_UARTE, idx), \ .flags = \ - (IS_ENABLED(CONFIG_UART_##idx##_GPIO_MANAGEMENT) ? \ - UARTE_CFG_FLAG_GPIO_MGMT : 0) | \ (IS_ENABLED(CONFIG_UART_##idx##_ENHANCED_POLL_OUT) ? \ UARTE_CFG_FLAG_PPI_ENDTX : 0) | \ (IS_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC) ? \ From 31e95ef0b629d1ea7fc84f4021c7edb5eab7a472 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 4 Jul 2024 14:18:54 +0200 Subject: [PATCH 02/21] drivers: serial: nrfx_uarte: Rework driver to support new features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework driver to support new way of asynchronous RX handling. Previously RX was handled in two modes: using RXDRDY interrupt for byte counting or TIMER + PPI. Both modes had flaws. RXDRDY interrupt mode could miscalculated amount of received bytes when interrupt was not handled on time. Data was not lost but was not reported on time that could lead to issues. PPI+TIMER mode requires additional resources thus it was not the default mode. Often user was not aware of that option and was expiriencing driver RX faults. New RX mode is switching buffers when there is new data (RXDRDY event not set for given amount of time). It does not require additional resources to get precise byte counting. Additionally, this is in line with new UARTE feature (RX frame timeout) which is present in nRF54X devices. The behavior of the driver is the same for legacy devices and new one. For legacy devices k_timer periodic interrupts are used to check if there are any new bytes and it is not needed when RX frame timeout is present. Improved RX mode is enabled by default (CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y) but legacy modes are still available though not recommended to be used. Note that new RX mode behaves a bit different because timeout always triggers switch of buffers which means that there will be no UART_RX_RDY events with non-zero offset. It also means that every UART_RX_RDY will be followed by UART_RX_BUF_RELEASED. After rework, driver is recommended to be used for all platforms as it performs much better and takes much less code than the second UART shim available for Nordic devices. Signed-off-by: Krzysztof Chruściński --- drivers/serial/Kconfig.nrfx | 12 + drivers/serial/Kconfig.nrfx_uart_instance | 1 + drivers/serial/uart_nrfx_uarte.c | 800 +++++++++++++--------- 3 files changed, 493 insertions(+), 320 deletions(-) diff --git a/drivers/serial/Kconfig.nrfx b/drivers/serial/Kconfig.nrfx index daf185cd7e86fc..16f667eaa50ee0 100644 --- a/drivers/serial/Kconfig.nrfx +++ b/drivers/serial/Kconfig.nrfx @@ -37,6 +37,18 @@ config UART_NRFX_UARTE_LEGACY_SHIM # New shim takes more ROM. Until it is fixed use legacy shim. default y +config UART_NRFX_UARTE_ENHANCED_RX + bool "Enhanced RX handling" + depends on UART_ASYNC_API + depends on UART_NRFX_UARTE_LEGACY_SHIM + default y if !(UART_0_NRF_HW_ASYNC || UART_1_NRF_HW_ASYNC || UART_2_NRF_HW_ASYNC) + help + Enable RX handling mode which is switching buffers on timeout. This is an + enhancement compared to other two modes (default and hardware assisted). + Default mode could miscount bytes when interrupt was not handled on time + and hardware assisted required TIMER peripheral instance and PPI channel + for accurate byte counting. + config UART_ASYNC_TX_CACHE_SIZE int "TX cache buffer size" depends on UART_ASYNC_API diff --git a/drivers/serial/Kconfig.nrfx_uart_instance b/drivers/serial/Kconfig.nrfx_uart_instance index ed4c011da51d11..4ea1393a2c1961 100644 --- a/drivers/serial/Kconfig.nrfx_uart_instance +++ b/drivers/serial/Kconfig.nrfx_uart_instance @@ -68,6 +68,7 @@ config UART_$(nrfx_uart_num)_NRF_ASYNC_LOW_POWER depends on HAS_HW_NRF_UARTE$(nrfx_uart_num) depends on UART_ASYNC_API depends on UART_NRFX_UARTE_LEGACY_SHIM + default y help When enabled, UARTE is enabled before each TX or RX usage and disabled when not used. Disabling UARTE while in idle allows to achieve lowest diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 327fedd180e62b..2d4f54ff0ebb80 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -8,36 +8,24 @@ * @brief Driver for Nordic Semiconductor nRF UARTE */ +#include #include +#include #include -#include -#include #include -#include -#include -#include #include #include - +#include +#include +#include +#include +#include #include -LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL); +LOG_MODULE_REGISTER(uart_nrfx_uarte, 0); -#include +#define DMM_MEMORY_SECTION(...) -/* Generalize PPI or DPPI channel management */ -#if defined(PPI_PRESENT) -#include -#define gppi_channel_t nrf_ppi_channel_t -#define gppi_channel_alloc nrfx_ppi_channel_alloc -#define gppi_channel_enable nrfx_ppi_channel_enable -#elif defined(DPPI_PRESENT) -#include -#define gppi_channel_t uint8_t -#define gppi_channel_alloc nrfx_dppi_channel_alloc -#define gppi_channel_enable nrfx_dppi_channel_enable -#else -#error "No PPI or DPPI" -#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) @@ -78,7 +66,7 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL); #define IS_HW_ASYNC(unused, prefix, i, _) IS_ENABLED(CONFIG_UART_##prefix##i##_NRF_HW_ASYNC) #if UARTE_FOR_EACH_INSTANCE(IS_HW_ASYNC, (||), (0)) -#define UARTE_HW_ASYNC 1 +#define UARTE_ANY_HW_ASYNC 1 #endif /* Determine if any instance is using enhanced poll_out feature. */ @@ -98,6 +86,11 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL); #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 + /* * RX timeout is divided into time slabs, this define tells how many divisions * should be made. More divisions - higher timeout accuracy and processor usage. @@ -127,6 +120,12 @@ struct uarte_async_rx { size_t offset; uint8_t *next_buf; size_t next_buf_len; +#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX +#if !defined(UARTE_HAS_FRAME_TIMEOUT) + uint32_t idle_cnt; +#endif + k_timeout_t timeout; +#else uint32_t total_byte_cnt; /* Total number of bytes received */ uint32_t total_user_byte_cnt; /* Total number of bytes passed to user */ int32_t timeout; /* Timeout set by user */ @@ -138,7 +137,7 @@ struct uarte_async_rx { } cnt; /* Flag to ensure that RX timeout won't be executed during ENDRX ISR */ volatile bool is_in_irq; - uint8_t flush_buffer[UARTE_HW_RX_FIFO_SIZE]; +#endif uint8_t flush_cnt; volatile bool enabled; volatile bool discard_fifo; @@ -148,11 +147,9 @@ struct uarte_async_rx { struct uarte_async_cb { uart_callback_t user_callback; void *user_data; - atomic_t low_power_mask; struct uarte_async_rx rx; struct uarte_async_tx tx; }; - #endif /* UARTE_ANY_ASYNC */ #ifdef UARTE_INTERRUPT_DRIVEN @@ -172,7 +169,6 @@ struct uarte_nrfx_int_driven { /* Device data structure */ struct uarte_nrfx_data { - const struct device *dev; #ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE struct uart_config uart_config; #endif @@ -183,13 +179,15 @@ struct uarte_nrfx_data { struct uarte_async_cb *async; #endif atomic_val_t poll_out_lock; - uint8_t *char_out; - uint8_t *rx_data; - gppi_channel_t ppi_ch_endtx; +#ifdef UARTE_ENHANCED_POLL_OUT + uint8_t ppi_ch_endtx; +#endif + atomic_t flags; }; -#define UARTE_LOW_POWER_TX BIT(0) -#define UARTE_LOW_POWER_RX BIT(1) +#define UARTE_FLAG_LOW_POWER_TX BIT(0) +#define UARTE_FLAG_LOW_POWER_RX BIT(1) +#define UARTE_FLAG_TRIG_RXTO BIT(2) /* If enabled then ENDTX is PPI'ed to TXSTOP */ #define UARTE_CFG_FLAG_PPI_ENDTX BIT(0) @@ -197,6 +195,9 @@ struct uarte_nrfx_data { /* If enabled then TIMER and PPI is used for byte counting. */ #define UARTE_CFG_FLAG_HW_BYTE_COUNTING BIT(1) +/* If enabled then cache management is required (except for dmm buffers). */ +#define UARTE_CFG_FLAG_CACHEABLE BIT(3) + /* If enabled then UARTE peripheral is disabled when not used. This allows * to achieve lowest power consumption in idle. */ @@ -232,18 +233,28 @@ struct uarte_nrfx_data { */ struct uarte_nrfx_config { NRF_UARTE_Type *uarte_regs; /* Instance address */ - uint32_t clock_freq; uint32_t flags; bool disable_rx; const struct pinctrl_dev_config *pcfg; -#ifndef CONFIG_UART_USE_RUNTIME_CONFIGURE - nrf_uarte_baudrate_t baudrate; - nrf_uarte_config_t hw_config; +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + /* None-zero in case of high speed instances. Baudrate is adjusted by that ratio. */ + uint32_t clock_freq; + uint32_t baudrate_div; +#else +#ifdef UARTE_HAS_FRAME_TIMEOUT + uint32_t baudrate; #endif + nrf_uarte_baudrate_t nrf_baudrate; + nrf_uarte_config_t hw_config; +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ + #ifdef UARTE_ANY_ASYNC nrfx_timer_t timer; uint8_t *tx_cache; + uint8_t *rx_flush_buf; #endif + uint8_t *poll_out_byte; + uint8_t *poll_in_byte; }; static inline NRF_UARTE_Type *get_uarte_instance(const struct device *dev) @@ -439,6 +450,9 @@ static int uarte_nrfx_configure(const struct device *dev, return -ENOTSUP; } +#ifdef UARTE_HAS_FRAME_TIMEOUT + uarte_cfg.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_EN; +#endif nrf_uarte_configure(get_uarte_instance(dev), &uarte_cfg); data->uart_config = *cfg; @@ -518,28 +532,31 @@ static int wait_tx_ready(const struct device *dev) /* Using Macro instead of static inline function to handle NO_OPTIMIZATIONS case * where static inline fails on linking. */ -#define HW_RX_COUNTING_ENABLED(config) \ - (IS_ENABLED(UARTE_HW_ASYNC) ? (config->flags & UARTE_CFG_FLAG_HW_BYTE_COUNTING) : false) +#define HW_RX_COUNTING_ENABLED(config) \ + (IS_ENABLED(UARTE_ANY_HW_ASYNC) ? \ + (config->flags & UARTE_CFG_FLAG_HW_BYTE_COUNTING) : false) #endif /* UARTE_ANY_ASYNC */ -static int uarte_enable(const struct device *dev, uint32_t mask) +static void uarte_enable(const struct device *dev, uint32_t act_mask, uint32_t sec_mask) { -#ifdef UARTE_ANY_ASYNC - const struct uarte_nrfx_config *config = dev->config; struct uarte_nrfx_data *data = dev->data; + int err; - if (data->async) { - bool disabled = data->async->low_power_mask == 0; - int ret; + if (atomic_or(&data->flags, act_mask) & sec_mask) { + /* Second direction already enabled so UARTE is enabled. */ + return; + } - data->async->low_power_mask |= mask; - ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); - if (ret < 0) { - return ret; - } + err = pins_state_change(dev, true); + if (err < 0) { + return err; + } +#ifdef UARTE_ANY_ASYNC + if (data->async) { + const struct uarte_nrfx_config *config = dev->config; - if (HW_RX_COUNTING_ENABLED(config) && disabled) { + if (HW_RX_COUNTING_ENABLED(config)) { const nrfx_timer_t *timer = &config->timer; nrfx_timer_enable(timer); @@ -551,8 +568,6 @@ static int uarte_enable(const struct device *dev, uint32_t mask) } #endif nrf_uarte_enable(get_uarte_instance(dev)); - - return 0; } /* At this point we should have irq locked and any previous transfer completed. @@ -571,12 +586,13 @@ static void tx_start(const struct device *dev, const uint8_t *buf, size_t len) return; } #endif + nrf_uarte_tx_buffer_set(uarte, buf, len); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_TXSTOPPED); if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { - (void)uarte_enable(dev, UARTE_LOW_POWER_TX); + (void)uarte_enable(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); } @@ -584,12 +600,18 @@ static void tx_start(const struct device *dev, const uint8_t *buf, size_t len) } #if defined(UARTE_ANY_ASYNC) || defined(CONFIG_PM_DEVICE) -static void uart_disable(const struct device *dev) +static void uarte_disable_locked(const struct device *dev, uint32_t dis_mask, uint32_t sec_mask) { -#ifdef UARTE_ANY_ASYNC - const struct uarte_nrfx_config *config = dev->config; struct uarte_nrfx_data *data = dev->data; + data->flags &= ~dis_mask; + if (data->flags & sec_mask) { + return; + } + +#if defined(UARTE_ANY_ASYNC) && !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + const struct uarte_nrfx_config *config = dev->config; + if (data->async && HW_RX_COUNTING_ENABLED(config)) { nrfx_timer_disable(&config->timer); /* Timer/counter value is reset when disabled. */ @@ -598,16 +620,19 @@ static void uart_disable(const struct device *dev) } #endif + (void)pins_state_change(dev, false); nrf_uarte_disable(get_uarte_instance(dev)); } #endif #ifdef UARTE_ANY_ASYNC -static void timer_handler(nrf_timer_event_t event_type, void *p_context) { } static void rx_timeout(struct k_timer *timer); static void tx_timeout(struct k_timer *timer); +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) +static void timer_handler(nrf_timer_event_t event_type, void *p_context) { } + static int uarte_nrfx_rx_counting_init(const struct device *dev) { struct uarte_nrfx_data *data = dev->data; @@ -618,6 +643,8 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) if (HW_RX_COUNTING_ENABLED(cfg)) { nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG( NRF_TIMER_BASE_FREQUENCY_GET(cfg->timer.p_reg)); + uint32_t evt_addr = nrf_uarte_event_address_get(uarte, NRF_UARTE_EVENT_RXDRDY); + uint32_t tsk_addr = nrfx_timer_task_address_get(&cfg->timer, NRF_TIMER_TASK_COUNT); tmr_config.mode = NRF_TIMER_MODE_COUNTER; tmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32; @@ -628,38 +655,18 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) LOG_ERR("Timer already initialized"); return -EINVAL; } else { - nrfx_timer_enable(&cfg->timer); nrfx_timer_clear(&cfg->timer); } - ret = gppi_channel_alloc(&data->async->rx.cnt.ppi); + ret = nrfx_gppi_channel_alloc(&data->async->rx.cnt.ppi); if (ret != NRFX_SUCCESS) { LOG_ERR("Failed to allocate PPI Channel"); nrfx_timer_uninit(&cfg->timer); return -EINVAL; } -#if CONFIG_HAS_HW_NRF_PPI - ret = nrfx_ppi_channel_assign( - data->async->rx.cnt.ppi, - nrf_uarte_event_address_get(uarte, - NRF_UARTE_EVENT_RXDRDY), - nrfx_timer_task_address_get(&cfg->timer, - NRF_TIMER_TASK_COUNT)); - - if (ret != NRFX_SUCCESS) { - return -EIO; - } -#else - nrf_uarte_publish_set(uarte, - NRF_UARTE_EVENT_RXDRDY, - data->async->rx.cnt.ppi); - nrf_timer_subscribe_set(cfg->timer.p_reg, - NRF_TIMER_TASK_COUNT, - data->async->rx.cnt.ppi); - -#endif - ret = gppi_channel_enable(data->async->rx.cnt.ppi); + nrfx_gppi_channel_endpoints_setup(data->async->rx.cnt.ppi, evt_addr, tsk_addr); + nrfx_gppi_channels_enable(BIT(data->async->rx.cnt.ppi)); if (ret != NRFX_SUCCESS) { return -EIO; } @@ -669,30 +676,35 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) return 0; } +#endif /* !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) */ static int uarte_nrfx_init(const struct device *dev) { struct uarte_nrfx_data *data = dev->data; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - + static const uint32_t rx_int_mask = + NRF_UARTE_INT_ENDRX_MASK | + NRF_UARTE_INT_RXSTARTED_MASK | + NRF_UARTE_INT_ERROR_MASK | + NRF_UARTE_INT_RXTO_MASK | + ((IS_ENABLED(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) && + !IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) ? NRF_UARTE_INT_RXDRDY_MASK : 0); + +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) int ret = uarte_nrfx_rx_counting_init(dev); if (ret != 0) { return ret; } +#endif - data->async->low_power_mask = UARTE_LOW_POWER_TX; - nrf_uarte_int_enable(uarte, - NRF_UARTE_INT_ENDRX_MASK | - NRF_UARTE_INT_RXSTARTED_MASK | - NRF_UARTE_INT_ERROR_MASK | - NRF_UARTE_INT_RXTO_MASK); + nrf_uarte_int_enable(uarte, rx_int_mask); nrf_uarte_enable(uarte); k_timer_init(&data->async->rx.timer, rx_timeout, NULL); - k_timer_user_data_set(&data->async->rx.timer, data); + 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, data); + k_timer_user_data_set(&data->async->tx.timer, (void *)dev); return 0; } @@ -768,7 +780,7 @@ static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf, data->async->tx.buf = buf; nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); - if (nrfx_is_in_ram(buf)) { + if (nrf_dma_accessible_check(uarte, buf)) { data->async->tx.xfer_buf = buf; data->async->tx.xfer_len = len; } else { @@ -824,17 +836,14 @@ static void notify_uart_rx_rdy(const struct device *dev, size_t len) user_callback(dev, &evt); } -static void rx_buf_release(const struct device *dev, uint8_t **buf) +static void rx_buf_release(const struct device *dev, uint8_t *buf) { - if (*buf) { - struct uart_event evt = { - .type = UART_RX_BUF_RELEASED, - .data.rx_buf.buf = *buf, - }; + struct uart_event evt = { + .type = UART_RX_BUF_RELEASED, + .data.rx_buf.buf = buf, + }; - user_callback(dev, &evt); - *buf = NULL; - } + user_callback(dev, &evt); } static void notify_rx_disable(const struct device *dev) @@ -846,14 +855,24 @@ static void notify_rx_disable(const struct device *dev) user_callback(dev, (struct uart_event *)&evt); } +#ifdef UARTE_HAS_FRAME_TIMEOUT +static uint32_t us_to_bauds(uint32_t baudrate, int32_t timeout) +{ + uint32_t divisor = 1000000 / timeout; + uint32_t bauds = baudrate / divisor; + + 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 *rdata = &data->async->rx; const struct uarte_nrfx_config *cfg = dev->config; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - int ret = 0; if (cfg->disable_rx) { __ASSERT(false, "TX only UARTE instance"); @@ -864,24 +883,44 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, * for the RXTO event after a call to uart_rx_disable() to discard * data from the UARTE internal RX FIFO. */ - if (data->async->rx.enabled || data->async->rx.discard_fifo) { + if (rdata->enabled || rdata->discard_fifo) { return -EBUSY; } - data->async->rx.timeout = timeout; - data->async->rx.timeout_slab = timeout / RX_TIMEOUT_DIV; +#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX +#ifdef UARTE_HAS_FRAME_TIMEOUT + if (timeout && (timeout != SYS_FOREVER_US)) { + uint32_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, + (data->uart_config.baudrate), (cfg->baudrate)); + + rdata->timeout = K_USEC(timeout); + nrf_uarte_frame_timeout_set(uarte, us_to_bauds(baudrate, timeout)); + nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX); + } else { + rdata->timeout = K_NO_WAIT; + } +#else + rdata->timeout = (timeout && timeout == SYS_FOREVER_US) ? + K_NO_WAIT : K_USEC(timeout / RX_TIMEOUT_DIV); + rdata->idle_cnt = RX_TIMEOUT_DIV - 1; +#endif +#else + rdata->timeout = timeout; + rdata->timeout_slab = timeout / RX_TIMEOUT_DIV; +#endif - data->async->rx.buf = buf; - data->async->rx.buf_len = len; - data->async->rx.offset = 0; - data->async->rx.next_buf = NULL; - data->async->rx.next_buf_len = 0; + rdata->buf = buf; + rdata->buf_len = len; + rdata->offset = 0; + rdata->next_buf = NULL; + rdata->next_buf_len = 0; if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { - if (data->async->rx.flush_cnt) { - int cpy_len = MIN(len, data->async->rx.flush_cnt); + if (rdata->flush_cnt) { + int cpy_len = MIN(len, rdata->flush_cnt); + + memcpy(buf, cfg->rx_flush_buf, cpy_len); - memcpy(buf, data->async->rx.flush_buffer, cpy_len); buf += cpy_len; len -= cpy_len; @@ -889,11 +928,19 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, * request and indicate rx being disabled. */ if (!len) { - data->async->rx.flush_cnt -= cpy_len; - notify_uart_rx_rdy(dev, cpy_len); - rx_buf_release(dev, &data->async->rx.buf); - notify_rx_disable(dev); + rdata->flush_cnt -= cpy_len; + memmove(cfg->rx_flush_buf, &cfg->rx_flush_buf[cpy_len], + rdata->flush_cnt); + atomic_or(&data->flags, UARTE_FLAG_TRIG_RXTO); + NRFX_IRQ_PENDING_SET(nrfx_get_irq_number(uarte)); return 0; + } else { +#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX + if (!K_TIMEOUT_EQ(rdata->timeout, K_NO_WAIT)) { + nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY); + k_timer_start(&rdata->timer, rdata->timeout, K_NO_WAIT); + } +#endif } } } @@ -903,11 +950,11 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); - data->async->rx.enabled = true; + rdata->enabled = true; if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { unsigned int key = irq_lock(); - ret = uarte_enable(dev, UARTE_LOW_POWER_RX); + uarte_enable(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); irq_unlock(key); } @@ -920,17 +967,28 @@ 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 *rdata = &data->async->rx; int err; NRF_UARTE_Type *uarte = get_uarte_instance(dev); unsigned int key = irq_lock(); - if (data->async->rx.buf == NULL) { + if (rdata->buf == NULL) { err = -EACCES; - } else if (data->async->rx.next_buf == NULL) { - data->async->rx.next_buf = buf; - data->async->rx.next_buf_len = len; + } else if (rdata->next_buf == NULL) { + rdata->next_buf = buf; + rdata->next_buf_len = len; nrf_uarte_rx_buffer_set(uarte, buf, len); - nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX); + /* 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; @@ -960,19 +1018,20 @@ static int uarte_nrfx_callback_set(const struct device *dev, static int uarte_nrfx_rx_disable(const struct device *dev) { struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - if (data->async->rx.buf == NULL) { + if (rdata->buf == NULL) { return -EFAULT; } - if (data->async->rx.next_buf != NULL) { + if (rdata->next_buf != NULL) { nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); } - k_timer_stop(&data->async->rx.timer); - data->async->rx.enabled = false; - data->async->rx.discard_fifo = true; + k_timer_stop(&rdata->timer); + rdata->enabled = false; + rdata->discard_fifo = true; nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); @@ -981,8 +1040,8 @@ static int uarte_nrfx_rx_disable(const struct device *dev) static void tx_timeout(struct k_timer *timer) { - struct uarte_nrfx_data *data = k_timer_user_data_get(timer); - (void) uarte_nrfx_tx_abort(data->dev); + const struct device *dev = k_timer_user_data_get(timer); + (void) uarte_nrfx_tx_abort(dev); } /** @@ -995,12 +1054,40 @@ static void tx_timeout(struct k_timer *timer) */ static void rx_timeout(struct k_timer *timer) { - struct uarte_nrfx_data *data = k_timer_user_data_get(timer); - const struct device *dev = data->dev; + const struct device *dev = k_timer_user_data_get(timer); + +#if CONFIG_UART_NRFX_UARTE_ENHANCED_RX + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + +#ifdef UARTE_HAS_FRAME_TIMEOUT + if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) { + nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); + } + return; +#else /* UARTE_HAS_FRAME_TIMEOUT */ + struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; + + if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) { + nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY); + rdata->idle_cnt = RX_TIMEOUT_DIV - 1; + } else { + rdata->idle_cnt--; + if (rdata->idle_cnt == 0) { + nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); + return; + } + } + + k_timer_start(&rdata->timer, rdata->timeout, K_NO_WAIT); +#endif /* UARTE_HAS_FRAME_TIMEOUT */ +#else /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */ const struct uarte_nrfx_config *cfg = dev->config; + struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; uint32_t read; - if (data->async->rx.is_in_irq) { + if (rdata->is_in_irq) { return; } @@ -1013,21 +1100,20 @@ static void rx_timeout(struct k_timer *timer) if (HW_RX_COUNTING_ENABLED(cfg)) { read = nrfx_timer_capture(&cfg->timer, 0); } else { - read = data->async->rx.cnt.cnt; + read = rdata->cnt.cnt; } /* Check if data was received since last function call */ - if (read != data->async->rx.total_byte_cnt) { - data->async->rx.total_byte_cnt = read; - data->async->rx.timeout_left = data->async->rx.timeout; + if (read != rdata->total_byte_cnt) { + rdata->total_byte_cnt = read; + rdata->timeout_left = rdata->timeout; } /* Check if there is data that was not sent to user yet * Note though that 'len' is a count of data bytes received, but not * necessarily the amount available in the current buffer */ - int32_t len = data->async->rx.total_byte_cnt - - data->async->rx.total_user_byte_cnt; + int32_t len = rdata->total_byte_cnt - rdata->total_user_byte_cnt; if (!HW_RX_COUNTING_ENABLED(cfg) && (len < 0)) { @@ -1036,7 +1122,7 @@ static void rx_timeout(struct k_timer *timer) * At this point, the number of received bytes is at least * equal to what was reported to the user. */ - data->async->rx.cnt.cnt = data->async->rx.total_user_byte_cnt; + rdata->cnt.cnt = rdata->total_user_byte_cnt; len = 0; } @@ -1048,37 +1134,34 @@ static void rx_timeout(struct k_timer *timer) */ bool clipped = false; - if (len + data->async->rx.offset > data->async->rx.buf_len) { - len = data->async->rx.buf_len - data->async->rx.offset; + if (len + rdata->offset > rdata->buf_len) { + len = rdata->buf_len - rdata->offset; clipped = true; } if (len > 0) { - if (clipped || - (data->async->rx.timeout_left - < data->async->rx.timeout_slab)) { + if (clipped || (rdata->timeout_left < rdata->timeout_slab)) { /* rx_timeout us elapsed since last receiving */ - if (data->async->rx.buf != NULL) { + if (rdata->buf != NULL) { notify_uart_rx_rdy(dev, len); - data->async->rx.offset += len; - data->async->rx.total_user_byte_cnt += len; + rdata->offset += len; + rdata->total_user_byte_cnt += len; } } else { - data->async->rx.timeout_left -= - data->async->rx.timeout_slab; + rdata->timeout_left -= rdata->timeout_slab; } /* If there's nothing left to report until the buffers are * switched then the timer can be stopped */ if (clipped) { - k_timer_stop(&data->async->rx.timer); + k_timer_stop(&rdata->timer); } } nrf_uarte_int_enable(get_uarte_instance(dev), NRF_UARTE_INT_ENDRX_MASK); - +#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */ } #define UARTE_ERROR_FROM_MASK(mask) \ @@ -1091,49 +1174,69 @@ static void rx_timeout(struct k_timer *timer) static void error_isr(const struct device *dev) { NRF_UARTE_Type *uarte = get_uarte_instance(dev); - uint32_t err = nrf_uarte_errorsrc_get_and_clear(uarte); + uint32_t err = nrf_uarte_errorsrc_get(uarte); struct uart_event evt = { .type = UART_RX_STOPPED, .data.rx_stop.reason = UARTE_ERROR_FROM_MASK(err), }; + + /* For VPR cores read and write may be reordered - barrier needed. */ + nrf_barrier_r(); + nrf_uarte_errorsrc_clear(uarte, err); + user_callback(dev, &evt); (void) uarte_nrfx_rx_disable(dev); } static void rxstarted_isr(const struct device *dev) { - struct uarte_nrfx_data *data = dev->data; struct uart_event evt = { .type = UART_RX_BUF_REQUEST, }; - user_callback(dev, &evt); - if (data->async->rx.timeout != SYS_FOREVER_US) { - data->async->rx.timeout_left = data->async->rx.timeout; - k_timer_start(&data->async->rx.timer, - K_USEC(data->async->rx.timeout_slab), - K_USEC(data->async->rx.timeout_slab)); + +#ifndef UARTE_HAS_FRAME_TIMEOUT + struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; + +#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + + if (!K_TIMEOUT_EQ(rdata->timeout, K_NO_WAIT)) { + nrf_uarte_int_enable(uarte, NRF_UARTE_INT_RXDRDY_MASK); + } +#else + if (rdata->timeout != SYS_FOREVER_US) { + k_timeout_t timeout = K_USEC(rdata->timeout_slab); + + rdata->timeout_left = rdata->timeout; + k_timer_start(&rdata->timer, timeout, timeout); } +#endif /* CONFIG_UART_NRFX_UARTE_ENHANCED_RX */ +#endif /* !UARTE_HAS_FRAME_TIMEOUT */ + user_callback(dev, &evt); } static void endrx_isr(const struct device *dev) { struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - data->async->rx.is_in_irq = true; +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + rdata->is_in_irq = true; +#endif /* ensure rx timer is stopped - it will be restarted in RXSTARTED * handler if needed */ - k_timer_stop(&data->async->rx.timer); + k_timer_stop(&rdata->timer); /* this is the amount that the EasyDMA controller has copied into the * buffer */ - const int rx_amount = nrf_uarte_rx_amount_get(uarte) + - data->async->rx.flush_cnt; + const int rx_amount = nrf_uarte_rx_amount_get(uarte) + rdata->flush_cnt; - data->async->rx.flush_cnt = 0; + rdata->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. @@ -1142,56 +1245,56 @@ static void endrx_isr(const struct device *dev) * here to handle this buffer. (The next buffer is now already active * because of the ENDRX_STARTRX shortcut) */ - int rx_len = rx_amount - data->async->rx.offset; + int rx_len = rx_amount - rdata->offset; if (rx_len < 0) { rx_len = 0; } - data->async->rx.total_user_byte_cnt += rx_len; +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + rdata->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); } - if (!data->async->rx.enabled) { - data->async->rx.is_in_irq = false; - return; - } - - rx_buf_release(dev, &data->async->rx.buf); - - /* 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 (data->async->rx.next_buf) { - data->async->rx.buf = data->async->rx.next_buf; - data->async->rx.buf_len = data->async->rx.next_buf_len; - data->async->rx.next_buf = NULL; - data->async->rx.next_buf_len = 0; + rx_buf_release(dev, rdata->buf); + rdata->buf = rdata->next_buf; + rdata->buf_len = rdata->next_buf_len; + rdata->next_buf = NULL; + rdata->next_buf_len = 0; + rdata->offset = 0; - data->async->rx.offset = 0; - /* Check is based on assumption that ISR handler handles - * ENDRX before RXSTARTED so if short was set on time, RXSTARTED - * event will be set. + if (rdata->enabled) { + /* If there is a next buffer, then STARTRX will have already been + * invoked by the short (the next buffer will be filling up already) + * and here we just do the swap of which buffer the driver is following, + * the next rx_timeout() will update the rx_offset. */ - if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) { - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); + unsigned int key = irq_lock(); + + if (rdata->buf) { + /* Check is based on assumption that ISR handler handles + * ENDRX before RXSTARTED so if short was set on time, RXSTARTED + * event will be set. + */ + if (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) { + nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); + } + /* Remove the short until the subsequent next buffer is setup */ + nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX); + } else { + nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); } - /* Remove the short until the subsequent next buffer is setup */ - nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX); - } else { - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); - } - irq_unlock(key); + irq_unlock(key); + } - data->async->rx.is_in_irq = false; +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + rdata->is_in_irq = false; +#endif } /* Function for flushing internal RX fifo. Function can be called in case @@ -1217,26 +1320,24 @@ static void endrx_isr(const struct device *dev) * * @return number of bytes flushed from the fifo. */ -static uint8_t rx_flush(const struct device *dev, uint8_t *buf, uint32_t len) + +static uint8_t rx_flush(const struct device *dev, uint8_t *buf) { /* Flushing RX fifo requires buffer bigger than 4 bytes to empty fifo*/ - static const uint8_t dirty; + static const uint8_t dirty = 0xAA; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - uint32_t prev_rx_amount = nrf_uarte_rx_amount_get(uarte); - uint8_t tmp_buf[UARTE_HW_RX_FIFO_SIZE]; - uint8_t *flush_buf = buf ? buf : tmp_buf; - size_t flush_len = buf ? len : sizeof(tmp_buf); - - if (buf) { - flush_buf = buf; - flush_len = len; + const struct uarte_nrfx_config *config = dev->config; + uint32_t prev_rx_amount; + uint32_t rx_amount; + + if (IS_ENABLED(RX_FLUSH_WORKAROUND)) { + memset(buf, dirty, UARTE_HW_RX_FIFO_SIZE); + prev_rx_amount = nrf_uarte_rx_amount_get(uarte); } else { - flush_buf = tmp_buf; - flush_len = sizeof(tmp_buf); + prev_rx_amount = 0; } - memset(flush_buf, dirty, flush_len); - nrf_uarte_rx_buffer_set(uarte, flush_buf, flush_len); + nrf_uarte_rx_buffer_set(uarte, buf, UARTE_HW_RX_FIFO_SIZE); /* Final part of handling RXTO event is in ENDRX interrupt * handler. ENDRX is generated as a result of FLUSHRX task. */ @@ -1246,42 +1347,28 @@ static uint8_t rx_flush(const struct device *dev, uint8_t *buf, uint32_t len) /* empty */ } nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); + nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); - uint32_t rx_amount = nrf_uarte_rx_amount_get(uarte); + rx_amount = nrf_uarte_rx_amount_get(uarte); + if (!buf || !IS_ENABLED(RX_FLUSH_WORKAROUND)) { + return rx_amount; + } if (rx_amount != prev_rx_amount) { return rx_amount; } - for (int i = 0; i < flush_len; i++) { - if (flush_buf[i] != dirty) { - return rx_amount; - } + if (rx_amount > UARTE_HW_RX_FIFO_SIZE) { + return 0; } - return 0; -} - -static void async_uart_release(const struct device *dev, uint32_t dir_mask) -{ - struct uarte_nrfx_data *data = dev->data; - unsigned int key = irq_lock(); - - data->async->low_power_mask &= ~dir_mask; - if (!data->async->low_power_mask) { - if (dir_mask == UARTE_LOW_POWER_RX) { - data->async->rx.flush_cnt = - rx_flush(dev, data->async->rx.flush_buffer, - sizeof(data->async->rx.flush_buffer)); + for (int i = 0; i < rx_amount; i++) { + if (buf[i] != dirty) { + return rx_amount; } - - uart_disable(dev); - - (void)err; - __ASSERT_NO_MSG(err == 0); } - irq_unlock(key); + return 0; } /* This handler is called when the receiver is stopped. If rx was aborted @@ -1291,9 +1378,12 @@ 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 *rdata = &data->async->rx; - rx_buf_release(dev, &data->async->rx.buf); - rx_buf_release(dev, &data->async->rx.next_buf); + if (rdata->buf) { + rx_buf_release(dev, rdata->buf); + rdata->buf = NULL; + } /* This point can be reached in two cases: * 1. RX is disabled because all provided RX buffers have been filled. @@ -1303,22 +1393,37 @@ static void rxto_isr(const struct device *dev) * In the second case, additionally, data from the UARTE internal RX * FIFO need to be discarded. */ - data->async->rx.enabled = false; - if (data->async->rx.discard_fifo) { - uint8_t flushed; - - data->async->rx.discard_fifo = false; - flushed = rx_flush(dev, NULL, 0); + rdata->enabled = false; + if (rdata->discard_fifo) { + rdata->discard_fifo = false; +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) if (HW_RX_COUNTING_ENABLED(config)) { + uint8_t buf[UARTE_HW_RX_FIFO_SIZE]; + /* It need to be included because TIMER+PPI got RXDRDY events * and counted those flushed bytes. */ - data->async->rx.total_user_byte_cnt += flushed; + rdata->total_user_byte_cnt += rx_flush(dev, buf); + (void)buf; } +#endif + } else { + rdata->flush_cnt = rx_flush(dev, config->rx_flush_buf); } +#ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX + NRF_UARTE_Type *uarte = get_uarte_instance(dev); +#ifdef UARTE_HAS_FRAME_TIMEOUT + nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_FRAME_TIMEOUT_STOPRX); +#endif + nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY); +#endif + if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { - async_uart_release(dev, UARTE_LOW_POWER_RX); + uint32_t key = irq_lock(); + + uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); + irq_unlock(key); } notify_rx_disable(dev); @@ -1333,7 +1438,9 @@ static void txstopped_isr(const struct device *dev) if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); - async_uart_release(dev, UARTE_LOW_POWER_TX); + key = irq_lock(); + uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); + irq_unlock(key); if (!data->async->tx.len) { return; @@ -1375,10 +1482,10 @@ static void txstopped_isr(const struct device *dev) return; } - /* Amount is already included in tx_cache_offset. */ + /* Amount is already included in cache_offset. */ amount = data->async->tx.cache_offset; } else { - /* TX was aborted, include tx_cache_offset in amount. */ + /* TX was aborted, include cache_offset in amount. */ amount += data->async->tx.cache_offset; } } @@ -1402,28 +1509,54 @@ static void txstopped_isr(const struct device *dev) user_callback(dev, &evt); } +static void rxdrdy_isr(const struct device *dev) +{ +#if !defined(UARTE_HAS_FRAME_TIMEOUT) + struct uarte_nrfx_data *data = dev->data; + +#if defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + + data->async->rx.idle_cnt = RX_TIMEOUT_DIV - 1; + k_timer_start(&data->async->rx.timer, data->async->rx.timeout, K_NO_WAIT); + nrf_uarte_int_disable(uarte, NRF_UARTE_INT_RXDRDY_MASK); +#else + data->async->rx.cnt.cnt++; +#endif +#endif /* !UARTE_HAS_FRAME_TIMEOUT */ +} + +static bool event_check_clear(NRF_UARTE_Type *uarte, nrf_uarte_event_t event, + uint32_t int_mask, uint32_t int_en_mask) +{ + if (nrf_uarte_event_check(uarte, event) && (int_mask & int_en_mask)) { + nrf_uarte_event_clear(uarte, event); + return true; + } + + return false; +} + static void uarte_nrfx_isr_async(const void *arg) { const struct device *dev = arg; NRF_UARTE_Type *uarte = get_uarte_instance(dev); const struct uarte_nrfx_config *config = dev->config; + struct uarte_nrfx_data *data = dev->data; + struct uarte_async_rx *rdata = &data->async->rx; + uint32_t imask = nrf_uarte_int_enable_check(uarte, UINT32_MAX); - if (!HW_RX_COUNTING_ENABLED(config) - && nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXDRDY)) { - struct uarte_nrfx_data *data = dev->data; + if (!(HW_RX_COUNTING_ENABLED(config) || IS_ENABLED(UARTE_HAS_FRAME_TIMEOUT)) + && event_check_clear(uarte, NRF_UARTE_EVENT_RXDRDY, NRF_UARTE_INT_RXDRDY_MASK, imask)) { + rxdrdy_isr(dev); - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY); - data->async->rx.cnt.cnt++; - return; } - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ERROR)) { - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ERROR); + if (event_check_clear(uarte, NRF_UARTE_EVENT_ERROR, NRF_UARTE_INT_ERROR_MASK, imask)) { error_isr(dev); } - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX) - && nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDRX_MASK)) { + if (event_check_clear(uarte, NRF_UARTE_EVENT_ENDRX, NRF_UARTE_INT_ENDRX_MASK, imask)) { nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); endrx_isr(dev); } @@ -1435,7 +1568,8 @@ static void uarte_nrfx_isr_async(const void *arg) * UARTE interrupt got preempted. Events are not cleared * and isr will be called again. ENDRX will be handled first. */ - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED) && + if ((imask & NRF_UARTE_INT_RXSTARTED_MASK) && + nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED) && !nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) { nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); rxstarted_isr(dev); @@ -1447,22 +1581,31 @@ static void uarte_nrfx_isr_async(const void *arg) * UARTE interrupt got preempted. Events are not cleared * and isr will be called again. ENDRX will be handled first. */ - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO) && + if ((imask & NRF_UARTE_INT_RXTO_MASK) && + nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXTO) && !nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDRX)) { nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXTO); rxto_isr(dev); } if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT) && - (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX) && - nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_ENDTX_MASK))) { + (imask & NRF_UARTE_INT_ENDTX_MASK) && + nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) { endtx_isr(dev); } - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED) && - nrf_uarte_int_enable_check(uarte, NRF_UARTE_INT_TXSTOPPED_MASK)) { + if ((imask & NRF_UARTE_INT_TXSTOPPED_MASK) && + nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) { txstopped_isr(dev); } + + if (atomic_and(&data->flags, ~UARTE_FLAG_TRIG_RXTO) & UARTE_FLAG_TRIG_RXTO) { + notify_uart_rx_rdy(dev, rdata->buf_len); + rx_buf_release(dev, rdata->buf); + rdata->buf_len = 0; + rdata->buf = NULL; + notify_rx_disable(dev); + } } #endif /* UARTE_ANY_ASYNC */ @@ -1477,11 +1620,12 @@ static void uarte_nrfx_isr_async(const void *arg) */ static int uarte_nrfx_poll_in(const struct device *dev, unsigned char *c) { - - const struct uarte_nrfx_data *data = dev->data; + 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; } @@ -1491,7 +1635,7 @@ static int uarte_nrfx_poll_in(const struct device *dev, unsigned char *c) return -1; } - *c = *data->rx_data; + *c = *config->poll_in_byte; /* clear the interrupt */ nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); @@ -1508,7 +1652,7 @@ static int uarte_nrfx_poll_in(const struct device *dev, unsigned char *c) */ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c) { - struct uarte_nrfx_data *data = dev->data; + const struct uarte_nrfx_config *config = dev->config; bool isr_mode = k_is_in_isr() || k_is_pre_kernel(); unsigned int key; @@ -1517,11 +1661,12 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c) key = irq_lock(); if (is_tx_ready(dev)) { #if UARTE_ANY_ASYNC + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + struct uarte_nrfx_data *data = dev->data; + if (data->async && data->async->tx.len && data->async->tx.amount < 0) { - data->async->tx.amount = - nrf_uarte_tx_amount_get( - get_uarte_instance(dev)); + data->async->tx.amount = nrf_uarte_tx_amount_get(uarte); } #endif break; @@ -1534,8 +1679,8 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c) key = wait_tx_ready(dev); } - *data->char_out = c; - tx_start(dev, data->char_out, 1); + *config->poll_out_byte = c; + tx_start(dev, config->poll_out_byte, 1); irq_unlock(key); } @@ -1578,14 +1723,14 @@ static int uarte_nrfx_fifo_read(const struct device *dev, { int num_rx = 0; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - const struct uarte_nrfx_data *data = dev->data; + 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); /* Receive a character */ - rx_data[num_rx++] = *data->rx_data; + rx_data[num_rx++] = *config->poll_in_byte; nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); } @@ -1748,7 +1893,7 @@ static int endtx_stoptx_ppi_init(NRF_UARTE_Type *uarte, { nrfx_err_t ret; - ret = gppi_channel_alloc(&data->ppi_ch_endtx); + ret = nrfx_gppi_channel_alloc(&data->ppi_ch_endtx); if (ret != NRFX_SUCCESS) { LOG_ERR("Failed to allocate PPI Channel"); return -EIO; @@ -1768,12 +1913,14 @@ static int uarte_instance_init(const struct device *dev, { int err; NRF_UARTE_Type *uarte = get_uarte_instance(dev); - struct uarte_nrfx_data *data = dev->data; const struct uarte_nrfx_config *cfg = dev->config; - nrf_uarte_disable(uarte); +#if defined(CONFIG_UART_USE_RUNTIME_CONFIGURE) || defined(UARTE_ENHANCED_POLL_OUT) || \ + defined(UARTE_ANY_ASYNC) + struct uarte_nrfx_data *data = dev->data; +#endif - data->dev = dev; + nrf_uarte_disable(uarte); #ifdef CONFIG_ARCH_POSIX /* For simulation the DT provided peripheral address needs to be corrected */ @@ -1791,7 +1938,7 @@ static int uarte_instance_init(const struct device *dev, return err; } #else - nrf_uarte_baudrate_set(uarte, cfg->baudrate); + nrf_uarte_baudrate_set(uarte, cfg->nrf_baudrate); nrf_uarte_configure(uarte, &cfg->hw_config); #endif @@ -1805,7 +1952,6 @@ static int uarte_instance_init(const struct device *dev, } } #endif - #ifdef UARTE_ANY_ASYNC if (data->async) { err = uarte_nrfx_init(dev); @@ -1821,7 +1967,7 @@ static int uarte_instance_init(const struct device *dev, if (!cfg->disable_rx) { nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); - nrf_uarte_rx_buffer_set(uarte, data->rx_data, 1); + nrf_uarte_rx_buffer_set(uarte, cfg->poll_in_byte, 1); nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); } } @@ -1830,19 +1976,11 @@ static int uarte_instance_init(const struct device *dev, nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK); } - if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { - nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); - } - /* Set TXSTOPPED event by requesting fake (zero-length) transfer. * Pointer to RAM variable (data->tx_buffer) is set because otherwise * such operation may result in HardFault or RAM corruption. */ - nrf_uarte_tx_buffer_set(uarte, data->char_out, 0); - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTTX); - - /* switch off transmitter to save an energy */ - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX); + tx_start(dev, cfg->poll_out_byte, 0); return 0; } @@ -1946,6 +2084,14 @@ static int uarte_nrfx_pm_action(const struct device *dev, */ __ASSERT_NO_MSG(!data->async->rx.enabled); __ASSERT_NO_MSG(!data->async->tx.len); +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) + if (data->async && HW_RX_COUNTING_ENABLED(cfg)) { + nrfx_timer_disable(&cfg->timer); + /* Timer/counter value is reset when disabled. */ + data->async->rx.total_byte_cnt = 0; + data->async->rx.total_user_byte_cnt = 0; + } +#endif } #endif @@ -1973,7 +2119,8 @@ static int uarte_nrfx_pm_action(const struct device *dev, } wait_for_tx_stopped(dev); - uart_disable(dev); + uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); + uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP); if (ret < 0) { @@ -1999,27 +2146,39 @@ static int uarte_nrfx_pm_action(const struct device *dev, /* 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) \ - ((!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 USE_LOW_POWER(idx) \ + COND_CODE_1(CONFIG_PM_DEVICE, (0), \ + (((!UARTE_PROP(idx, disable_rx) && \ + COND_CODE_1(CONFIG_UART_##idx##_ASYNC, \ + (!IS_ENABLED(CONFIG_UART_##idx##_NRF_ASYNC_LOW_POWER)),\ + (1))) ? 0 : UARTE_CFG_FLAG_LOW_POWER))) #define UARTE_DISABLE_RX_INIT(node_id) \ .disable_rx = DT_PROP(node_id, disable_rx) +#define UARTE_MEM_REGION(idx) DT_PHANDLE(UARTE(idx), memory_regions) +#define UARTE_GET_FREQ(idx) DT_PROP(DT_CLOCKS_CTLR(UARTE(idx)), clock_frequency) + +#define UARTE_GET_MEM_ATTR(idx)\ + COND_CODE_1(UARTE_HAS_PROP(idx, memory_regions), \ + (COND_CODE_1(DT_NODE_HAS_PROP(UARTE_MEM_REGION(idx), zephyr_memory_attr), \ + (DT_PROP(UARTE_MEM_REGION(idx), zephyr_memory_attr)), \ + (0))), \ + (0)) + #define UARTE_GET_FREQ(idx) DT_PROP(DT_CLOCKS_CTLR(UARTE(idx)), clock_frequency) #define UARTE_GET_BAUDRATE_DIV(idx) \ COND_CODE_1(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \ ((UARTE_GET_FREQ(idx) / NRF_UARTE_BASE_FREQUENCY_16MHZ)), (1)) -/* When calculating baudrate we need to take into account that some instances - * must have baudrate adjusted to the ratio between UARTE clocking frequency and 16 MHz. +/* When calculating baudrate we need to take into account that high speed instances + * must have baudrate adjust to the ratio between UARTE clocking frequency and 16 MHz. */ #define UARTE_GET_BAUDRATE(idx) \ (NRF_BAUDRATE(UARTE_PROP(idx, current_speed)) / UARTE_GET_BAUDRATE_DIV(idx)) + /* Macro for setting nRF specific configuration structures. */ #define UARTE_NRF_CONFIG(idx) { \ .hwfc = (UARTE_PROP(idx, hw_flow_control) == \ @@ -2030,6 +2189,8 @@ static int uarte_nrfx_pm_action(const struct device *dev, IF_ENABLED(UARTE_HAS_STOP_CONFIG, (.stop = NRF_UARTE_STOP_ONE,))\ IF_ENABLED(UARTE_ODD_PARITY_ALLOWED, \ (.paritytype = NRF_UARTE_PARITYTYPE_EVEN,)) \ + IF_ENABLED(UARTE_HAS_FRAME_TIMEOUT, \ + (.frame_timeout = NRF_UARTE_FRAME_TIMEOUT_EN,)) \ } /* Macro for setting zephyr specific configuration structures. */ @@ -2052,13 +2213,13 @@ static int uarte_nrfx_pm_action(const struct device *dev, IF_ENABLED(CONFIG_UART_##idx##_ASYNC, ( \ static uint8_t \ uarte##idx##_tx_cache[CONFIG_UART_ASYNC_TX_CACHE_SIZE] \ - UARTE_MEMORY_SECTION(idx); \ + 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##_char_out UARTE_MEMORY_SECTION(idx); \ - static uint8_t uarte##idx##_rx_data UARTE_MEMORY_SECTION(idx); \ + 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 = { \ - .char_out = &uarte##idx##_char_out, \ - .rx_data = &uarte##idx##_rx_data, \ IF_ENABLED(CONFIG_UART_USE_RUNTIME_CONFIGURE, \ (.uart_config = UARTE_CONFIG(idx),)) \ IF_ENABLED(CONFIG_UART_##idx##_ASYNC, \ @@ -2070,8 +2231,12 @@ static int uarte_nrfx_pm_action(const struct device *dev, (BUILD_ASSERT(NRF_BAUDRATE(UARTE_PROP(idx, current_speed)) > 0,\ "Unsupported baudrate");)) \ static const struct uarte_nrfx_config uarte_##idx##z_config = { \ - COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, (), \ - (.baudrate = UARTE_GET_BAUDRATE(idx), \ + COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, \ + (IF_ENABLED(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \ + (.clock_freq = UARTE_GET_FREQ(idx),))), \ + (IF_ENABLED(UARTE_HAS_FRAME_TIMEOUT, \ + (.baudrate = UARTE_PROP(idx, current_speed),)) \ + .nrf_baudrate = UARTE_GET_BAUDRATE(idx), \ .hw_config = UARTE_NRF_CONFIG(idx),)) \ .pcfg = PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx)), \ .uarte_regs = _CONCAT(NRF_UARTE, idx), \ @@ -2082,14 +2247,14 @@ static int uarte_nrfx_pm_action(const struct device *dev, UARTE_CFG_FLAG_HW_BYTE_COUNTING : 0) | \ USE_LOW_POWER(idx), \ UARTE_DISABLE_RX_INIT(UARTE(idx)), \ + .poll_out_byte = &uarte##idx##_poll_out_byte, \ + .poll_in_byte = &uarte##idx##_poll_in_byte, \ IF_ENABLED(CONFIG_UART_##idx##_ASYNC, \ - (.tx_cache = uarte##idx##_tx_cache,)) \ + (.tx_cache = uarte##idx##_tx_cache, \ + .rx_flush_buf = uarte##idx##_flush_buf,)) \ IF_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC, \ (.timer = NRFX_TIMER_INSTANCE( \ CONFIG_UART_##idx##_NRF_HW_ASYNC_TIMER),)) \ - IF_ENABLED(DT_CLOCKS_HAS_IDX(UARTE(idx), 0), \ - (.clock_freq = DT_PROP(DT_CLOCKS_CTLR(UARTE(idx)), \ - clock_frequency),)) \ }; \ static int uarte_##idx##_init(const struct device *dev) \ { \ @@ -2101,7 +2266,8 @@ static int uarte_nrfx_pm_action(const struct device *dev, IS_ENABLED(CONFIG_UART_##idx##_INTERRUPT_DRIVEN)); \ } \ \ - PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action); \ + PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action, \ + COND_CODE_1(CONFIG_UART_##idx##_ASYNC, (PM_DEVICE_ISR_SAFE), (0))); \ \ DEVICE_DT_DEFINE(UARTE(idx), \ uarte_##idx##_init, \ @@ -2117,19 +2283,13 @@ static int uarte_nrfx_pm_action(const struct device *dev, (static uint8_t uarte##idx##_tx_buffer \ [MIN(CONFIG_UART_##idx##_NRF_TX_BUFFER_SIZE, \ BIT_MASK(UARTE##idx##_EASYDMA_MAXCNT_SIZE))] \ - UARTE_MEMORY_SECTION(idx); \ + 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 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 COND_UART_NRF_UARTE_DEVICE(unused, prefix, i, _) \ IF_ENABLED(CONFIG_HAS_HW_NRF_UARTE##prefix##i, (UART_NRF_UARTE_DEVICE(prefix##i);)) From 523733a1c7e1a159a3e8ada4383b01f1bcd8a8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Mon, 16 Sep 2024 10:39:21 +0200 Subject: [PATCH 03/21] drivers: serial: nrfx_uarte: Add support for DMM and cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for DMM which manages cache and dedicated memory spaces. Added support for data cache for buffers which are not DMM managed. Signed-off-by: Krzysztof Chruściński --- drivers/serial/uart_nrfx_uarte.c | 108 ++++++++++++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 2d4f54ff0ebb80..dc44c68cac6681 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -22,8 +22,12 @@ #include #include LOG_MODULE_REGISTER(uart_nrfx_uarte, 0); - +#ifdef CONFIG_HAS_NORDIC_DMM +#include +#else #define DMM_MEMORY_SECTION(...) +#define DMM_IS_REG_CACHEABLE(...) false +#endif #define RX_FLUSH_WORKAROUND 1 @@ -31,6 +35,8 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, 0); #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__) @@ -91,6 +97,12 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, 0); #define UARTE_HAS_FRAME_TIMEOUT 1 #endif +#define INSTANCE_NEEDS_CACHE_MGMT(unused, prefix, i, prop) UARTE_IS_CACHEABLE(prefix##i) + +#if UARTE_FOR_EACH_INSTANCE(INSTANCE_NEEDS_CACHE_MGMT, (+), (0), _) +#define UARTE_ANY_CACHE 1 +#endif + /* * RX timeout is divided into time slabs, this define tells how many divisions * should be made. More divisions - higher timeout accuracy and processor usage. @@ -115,6 +127,10 @@ struct uarte_async_tx { 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; @@ -236,6 +252,9 @@ struct uarte_nrfx_config { 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; @@ -587,6 +606,10 @@ static void tx_start(const struct device *dev, const uint8_t *buf, size_t len) } #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); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_TXSTOPPED); @@ -887,6 +910,19 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, return -EBUSY; } +#ifdef CONFIG_HAS_NORDIC_DMM + uint8_t *dma_buf; + int ret = 0; + + ret = dmm_buffer_in_prepare(cfg->mem_reg, buf, len, (void **)&dma_buf); + if (ret < 0) { + return ret; + } + + rdata->usr_buf = buf; + buf = dma_buf; +#endif + #ifdef CONFIG_UART_NRFX_UARTE_ENHANCED_RX #ifdef UARTE_HAS_FRAME_TIMEOUT if (timeout && (timeout != SYS_FOREVER_US)) { @@ -919,8 +955,18 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, if (rdata->flush_cnt) { int cpy_len = MIN(len, rdata->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; @@ -931,6 +977,11 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, rdata->flush_cnt -= cpy_len; memmove(cfg->rx_flush_buf, &cfg->rx_flush_buf[cpy_len], rdata->flush_cnt); + if (IS_ENABLED(UARTE_ANY_CACHE) && + (cfg->flags & UARTE_CFG_FLAG_CACHEABLE)) { + sys_cache_data_flush_range(cfg->rx_flush_buf, + rdata->flush_cnt); + } atomic_or(&data->flags, UARTE_FLAG_TRIG_RXTO); NRFX_IRQ_PENDING_SET(nrfx_get_irq_number(uarte)); return 0; @@ -975,6 +1026,17 @@ static int uarte_nrfx_rx_buf_rsp(const struct device *dev, uint8_t *buf, if (rdata->buf == NULL) { err = -EACCES; } else if (rdata->next_buf == NULL) { +#ifdef CONFIG_HAS_NORDIC_DMM + uint8_t *dma_buf; + const struct uarte_nrfx_config *config = dev->config; + + err = dmm_buffer_in_prepare(config->mem_reg, buf, len, (void **)&dma_buf); + if (err < 0) { + return err; + } + rdata->next_usr_buf = buf; + buf = dma_buf; +#endif rdata->next_buf = buf; rdata->next_buf_len = len; nrf_uarte_rx_buffer_set(uarte, buf, len); @@ -1236,6 +1298,14 @@ static void endrx_isr(const struct device *dev) */ const int rx_amount = nrf_uarte_rx_amount_get(uarte) + rdata->flush_cnt; +#ifdef CONFIG_HAS_NORDIC_DMM + const struct uarte_nrfx_config *config = dev->config; + int err = dmm_buffer_in_release(config->mem_reg, rdata->usr_buf, rx_amount, rdata->buf); + + (void)err; + __ASSERT_NO_MSG(err == 0); + rdata->buf = rdata->usr_buf; +#endif rdata->flush_cnt = 0; /* The 'rx_offset' can be bigger than 'rx_amount', so it the length @@ -1263,6 +1333,9 @@ static void endrx_isr(const struct device *dev) rx_buf_release(dev, rdata->buf); rdata->buf = rdata->next_buf; rdata->buf_len = rdata->next_buf_len; +#ifdef CONFIG_HAS_NORDIC_DMM + rdata->usr_buf = rdata->next_usr_buf; +#endif rdata->next_buf = NULL; rdata->next_buf_len = 0; rdata->offset = 0; @@ -1332,6 +1405,9 @@ static uint8_t rx_flush(const struct device *dev, uint8_t *buf) if (IS_ENABLED(RX_FLUSH_WORKAROUND)) { memset(buf, dirty, UARTE_HW_RX_FIFO_SIZE); + if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) { + sys_cache_data_flush_range(buf, UARTE_HW_RX_FIFO_SIZE); + } prev_rx_amount = nrf_uarte_rx_amount_get(uarte); } else { prev_rx_amount = 0; @@ -1362,6 +1438,10 @@ static uint8_t rx_flush(const struct device *dev, uint8_t *buf) return 0; } + if (IS_ENABLED(UARTE_ANY_CACHE) && (config->flags & UARTE_CFG_FLAG_CACHEABLE)) { + sys_cache_data_invd_range(buf, UARTE_HW_RX_FIFO_SIZE); + } + for (int i = 0; i < rx_amount; i++) { if (buf[i] != dirty) { return rx_amount; @@ -1381,6 +1461,10 @@ static void rxto_isr(const struct device *dev) struct uarte_async_rx *rdata = &data->async->rx; if (rdata->buf) { +#ifdef CONFIG_HAS_NORDIC_DMM + (void)dmm_buffer_in_release(config->mem_reg, rdata->usr_buf, 0, rdata->buf); + rdata->buf = rdata->usr_buf; +#endif rx_buf_release(dev, rdata->buf); rdata->buf = NULL; } @@ -1600,6 +1684,16 @@ static void uarte_nrfx_isr_async(const void *arg) } if (atomic_and(&data->flags, ~UARTE_FLAG_TRIG_RXTO) & UARTE_FLAG_TRIG_RXTO) { +#ifdef CONFIG_HAS_NORDIC_DMM + int ret; + + ret = dmm_buffer_in_release(config->mem_reg, rdata->usr_buf, + rdata->buf_len, rdata->buf); + + (void)ret; + __ASSERT_NO_MSG(ret == 0); + rdata->buf = rdata->usr_buf; +#endif notify_uart_rx_rdy(dev, rdata->buf_len); rx_buf_release(dev, rdata->buf); rdata->buf_len = 0; @@ -1635,6 +1729,10 @@ static int uarte_nrfx_poll_in(const struct device *dev, unsigned char *c) 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 */ @@ -1729,6 +1827,10 @@ static int uarte_nrfx_fifo_read(const struct device *dev, /* 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; @@ -2240,11 +2342,15 @@ static int uarte_nrfx_pm_action(const struct device *dev, .hw_config = UARTE_NRF_CONFIG(idx),)) \ .pcfg = PINCTRL_DT_DEV_CONFIG_GET(UARTE(idx)), \ .uarte_regs = _CONCAT(NRF_UARTE, idx), \ + IF_ENABLED(CONFIG_HAS_NORDIC_DMM, \ + (.mem_reg = DMM_DEV_TO_REG(UARTE(idx)),)) \ .flags = \ (IS_ENABLED(CONFIG_UART_##idx##_ENHANCED_POLL_OUT) ? \ UARTE_CFG_FLAG_PPI_ENDTX : 0) | \ (IS_ENABLED(CONFIG_UART_##idx##_NRF_HW_ASYNC) ? \ UARTE_CFG_FLAG_HW_BYTE_COUNTING : 0) | \ + (!IS_ENABLED(CONFIG_HAS_NORDIC_DMM) ? 0 : \ + (UARTE_IS_CACHEABLE(idx) ? UARTE_CFG_FLAG_CACHEABLE : 0)) | \ USE_LOW_POWER(idx), \ UARTE_DISABLE_RX_INIT(UARTE(idx)), \ .poll_out_byte = &uarte##idx##_poll_out_byte, \ From 4005737bb2f367f4a3109f1ecf96ca41cb4b2d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Tue, 9 Jul 2024 11:44:36 +0200 Subject: [PATCH 04/21] drivers: serial: nrfx_uarte: Improve RX FIFO flush workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configurable magic byte instead of fixed 0xAA. In some cases, it is known that certain bytes are less likely in the transmission and picking specific magic byte may reduce probability of failure of detection of the correct amount of flushed data. Signed-off-by: Krzysztof Chruściński --- drivers/serial/Kconfig.nrfx | 10 ++++++++++ drivers/serial/uart_nrfx_uarte.c | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/serial/Kconfig.nrfx b/drivers/serial/Kconfig.nrfx index 16f667eaa50ee0..2f71c815e64442 100644 --- a/drivers/serial/Kconfig.nrfx +++ b/drivers/serial/Kconfig.nrfx @@ -59,6 +59,16 @@ config UART_ASYNC_TX_CACHE_SIZE in RAM, because EasyDMA in UARTE peripherals can only transfer data from RAM. +config UART_NRFX_UARTE_RX_FLUSH_MAGIC_BYTE + int "Byte used for RX FIFO flush workaround" + default 171 + range 0 255 + help + Byte used to fill the buffer before RX FIFO is flushed into it. Due to the + HW anomaly a workaround need to be applied which checks if content of the + buffer changed. There are cases when specific value of the magic byte is + used if it is known that certain bytes are less likely to occur. + if HAS_HW_NRF_UART0 || HAS_HW_NRF_UARTE0 nrfx_uart_num = 0 rsource "Kconfig.nrfx_uart_instance" diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index dc44c68cac6681..4d25615e2b5907 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -1397,7 +1397,7 @@ static void endrx_isr(const struct device *dev) static uint8_t rx_flush(const struct device *dev, uint8_t *buf) { /* Flushing RX fifo requires buffer bigger than 4 bytes to empty fifo*/ - static const uint8_t dirty = 0xAA; + static const uint8_t dirty = CONFIG_UART_NRFX_UARTE_RX_FLUSH_MAGIC_BYTE; NRF_UARTE_Type *uarte = get_uarte_instance(dev); const struct uarte_nrfx_config *config = dev->config; uint32_t prev_rx_amount; From f6ebcec948b1c06b3cf357a66122fb1b7ee517c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Tue, 17 Sep 2024 14:15:04 +0200 Subject: [PATCH 05/21] drivers: serial: nrfx_uarte: Add workaround for BAUDRATE register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On uart120 BAUDRATE register is not retained when ENABLE=0 and because of that BAUDRATE must be set after enabling. Add workaround to the driver. Signed-off-by: Krzysztof Chruściński --- drivers/serial/uart_nrfx_uarte.c | 39 ++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 4d25615e2b5907..6ea61a4787d1fb 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -103,6 +103,16 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, 0); #define UARTE_ANY_CACHE 1 #endif +#ifdef UARTE_ANY_CACHE +/* uart120 instance does not retain BAUDRATE register when ENABLE=0. When this instance + * is used then baudrate must be set after enabling the peripheral and not before. + * This approach works for all instances so can be generally applied when uart120 is used. + * It is not default for all because it costs some resources. Since currently only uart120 + * needs cache, that is used to determine if workaround shall be applied. + */ +#define UARTE_BAUDRATE_RETENTION_WORKAROUND 1 +#endif + /* * RX timeout is divided into time slabs, this define tells how many divisions * should be made. More divisions - higher timeout accuracy and processor usage. @@ -187,6 +197,9 @@ struct uarte_nrfx_int_driven { 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; @@ -387,7 +400,6 @@ static int baudrate_set(const struct device *dev, uint32_t baudrate) const struct uarte_nrfx_config *config = dev->config; /* calculated baudrate divisor */ nrf_uarte_baudrate_t nrf_baudrate = NRF_BAUDRATE(baudrate); - NRF_UARTE_Type *uarte = get_uarte_instance(dev); if (nrf_baudrate == 0) { return -EINVAL; @@ -398,7 +410,15 @@ static int baudrate_set(const struct device *dev, uint32_t baudrate) nrf_baudrate /= config->clock_freq / NRF_UARTE_BASE_FREQUENCY_16MHZ; } +#ifdef UARTE_BAUDRATE_RETENTION_WORKAROUND + struct uarte_nrfx_data *data = dev->data; + + data->nrf_baudrate = nrf_baudrate; +#else + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + nrf_uarte_baudrate_set(uarte, nrf_baudrate); +#endif return 0; } @@ -587,6 +607,13 @@ static void uarte_enable(const struct device *dev, uint32_t act_mask, uint32_t s } #endif nrf_uarte_enable(get_uarte_instance(dev)); +#if UARTE_BAUDRATE_RETENTION_WORKAROUND + nrf_uarte_baudrate_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, + (data->nrf_baudrate), + (((const struct uarte_nrfx_config *)dev->config)->nrf_baudrate)); + + nrf_uarte_baudrate_set(get_uarte_instance(dev), baudrate); +#endif } /* At this point we should have irq locked and any previous transfer completed. @@ -2146,12 +2173,19 @@ static int uarte_nrfx_pm_action(const struct device *dev, switch (action) { case PM_DEVICE_ACTION_RESUME: - ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + { + ret = pins_state_change(dev, true); if (ret < 0) { return ret; } nrf_uarte_enable(uarte); +#if UARTE_BAUDRATE_RETENTION_WORKAROUND + nrf_uarte_baudrate_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, + (data->nrf_baudrate), (cfg->nrf_baudrate)); + + nrf_uarte_baudrate_set(get_uarte_instance(dev), baudrate); +#endif #ifdef UARTE_ANY_ASYNC if (data->async) { @@ -2175,6 +2209,7 @@ static int uarte_nrfx_pm_action(const struct device *dev, #endif } break; + } case PM_DEVICE_ACTION_SUSPEND: /* Disabling UART requires stopping RX, but stop RX event is * only sent after each RX if async UART API is used. From 70919e5d055955c21cfbf19786ac67e8605e38e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Tue, 17 Sep 2024 14:24:05 +0200 Subject: [PATCH 06/21] drivers: serial: nrfx_uarte: Add lock to uart_rx_disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add lock to fix race when uart_rx_disable is interrupted by RXTO event which lead to driver state corruption. Signed-off-by: Krzysztof Chruściński --- drivers/serial/uart_nrfx_uarte.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 6ea61a4787d1fb..62eb88101e906c 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -1109,20 +1109,26 @@ static int uarte_nrfx_rx_disable(const struct device *dev) struct uarte_nrfx_data *data = dev->data; struct uarte_async_rx *rdata = &data->async->rx; NRF_UARTE_Type *uarte = get_uarte_instance(dev); + int key; if (rdata->buf == NULL) { return -EFAULT; } + + k_timer_stop(&rdata->timer); + + key = irq_lock(); + if (rdata->next_buf != NULL) { nrf_uarte_shorts_disable(uarte, NRF_UARTE_SHORT_ENDRX_STARTRX); nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); } - k_timer_stop(&rdata->timer); rdata->enabled = false; rdata->discard_fifo = true; nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPRX); + irq_unlock(key); return 0; } From cd57e0d7d10dd5c6d79773196202f88182da338a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Mon, 16 Sep 2024 10:32:22 +0200 Subject: [PATCH 07/21] drivers: serial: nrfx_uarte: Add runtime PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add runtime PM to the driver. When asynchronous or interrupt driven API is used, there are 3 independent paths for enabling the device: RX, TX and TX poll_out. TX poll_out requires special handling because number of poll_out calls does not need to much number of TXSTOPPED interrupts. To handle that special flag is added. For standard RX and TX is simpler. Signed-off-by: Krzysztof Chruściński --- drivers/serial/Kconfig.nrfx_uart_instance | 1 + drivers/serial/uart_nrfx_uarte.c | 458 ++++++++++++---------- 2 files changed, 261 insertions(+), 198 deletions(-) diff --git a/drivers/serial/Kconfig.nrfx_uart_instance b/drivers/serial/Kconfig.nrfx_uart_instance index 4ea1393a2c1961..907f350c6359ff 100644 --- a/drivers/serial/Kconfig.nrfx_uart_instance +++ b/drivers/serial/Kconfig.nrfx_uart_instance @@ -68,6 +68,7 @@ config UART_$(nrfx_uart_num)_NRF_ASYNC_LOW_POWER depends on HAS_HW_NRF_UARTE$(nrfx_uart_num) depends on UART_ASYNC_API depends on UART_NRFX_UARTE_LEGACY_SHIM + depends on !PM_DEVICE default y help When enabled, UARTE is enabled before each TX or RX usage and disabled diff --git a/drivers/serial/uart_nrfx_uarte.c b/drivers/serial/uart_nrfx_uarte.c index 62eb88101e906c..3f870d249cef72 100644 --- a/drivers/serial/uart_nrfx_uarte.c +++ b/drivers/serial/uart_nrfx_uarte.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,7 @@ struct uarte_nrfx_data { #define UARTE_FLAG_LOW_POWER_TX BIT(0) #define UARTE_FLAG_LOW_POWER_RX BIT(1) #define UARTE_FLAG_TRIG_RXTO BIT(2) +#define UARTE_FLAG_POLL_OUT BIT(3) /* If enabled then ENDTX is PPI'ed to TXSTOP */ #define UARTE_CFG_FLAG_PPI_ENDTX BIT(0) @@ -323,6 +325,7 @@ 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 @@ -334,34 +337,36 @@ static void uarte_nrfx_isr_int(const void *arg) endtx_isr(dev); } - if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { + bool txstopped = nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED); + + if (txstopped && (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || + (config->flags & UARTE_CFG_FLAG_LOW_POWER))) { unsigned int key = irq_lock(); - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) { + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) && + (data->flags & UARTE_FLAG_POLL_OUT)) { + data->flags &= ~UARTE_FLAG_POLL_OUT; + pm_device_runtime_put(dev); + } else { nrf_uarte_disable(uarte); } #ifdef UARTE_INTERRUPT_DRIVEN - struct uarte_nrfx_data *data = dev->data; - if (!data->int_driven || data->int_driven->fifo_fill_lock == 0) #endif { - nrf_uarte_int_disable(uarte, - NRF_UARTE_INT_TXSTOPPED_MASK); + nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); } irq_unlock(key); } #ifdef UARTE_INTERRUPT_DRIVEN - struct uarte_nrfx_data *data = dev->data; - if (!data->int_driven) { return; } - if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) { + if (txstopped) { data->int_driven->fifo_fill_lock = 0; if (data->int_driven->disable_tx_irq) { nrf_uarte_int_disable(uarte, @@ -580,17 +585,12 @@ static int wait_tx_ready(const struct device *dev) static void uarte_enable(const struct device *dev, uint32_t act_mask, uint32_t sec_mask) { struct uarte_nrfx_data *data = dev->data; - int err; if (atomic_or(&data->flags, act_mask) & sec_mask) { /* Second direction already enabled so UARTE is enabled. */ return; } - err = pins_state_change(dev, true); - if (err < 0) { - return err; - } #ifdef UARTE_ANY_ASYNC if (data->async) { const struct uarte_nrfx_config *config = dev->config; @@ -624,32 +624,37 @@ 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); -#ifdef CONFIG_PM_DEVICE - enum pm_device_state state; + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + /* When PM or low power mode is not enabled then TXSTOPPED interrupt is + * not enabled as there is nothing to do there but for PM/low power we + * need to track when TX is no longer in use. + */ + nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); + } else if (IS_ENABLED(CONFIG_PM_DEVICE)) { + enum pm_device_state state; - (void)pm_device_state_get(dev, &state); - if (state != PM_DEVICE_STATE_ACTIVE) { - return; + (void)pm_device_state_get(dev, &state); + if (state != PM_DEVICE_STATE_ACTIVE) { + return; + } + } else if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { + uarte_enable(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); + nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); } -#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); - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX); - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_TXSTOPPED); - - if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { - (void)uarte_enable(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); - nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); + 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); nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTTX); } -#if defined(UARTE_ANY_ASYNC) || defined(CONFIG_PM_DEVICE) +#if defined(UARTE_ANY_ASYNC) static void uarte_disable_locked(const struct device *dev, uint32_t dis_mask, uint32_t sec_mask) { struct uarte_nrfx_data *data = dev->data; @@ -659,7 +664,7 @@ static void uarte_disable_locked(const struct device *dev, uint32_t dis_mask, ui return; } -#if defined(UARTE_ANY_ASYNC) && !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) +#if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) const struct uarte_nrfx_config *config = dev->config; if (data->async && HW_RX_COUNTING_ENABLED(config)) { @@ -670,7 +675,6 @@ static void uarte_disable_locked(const struct device *dev, uint32_t dis_mask, ui } #endif - (void)pins_state_change(dev, false); nrf_uarte_disable(get_uarte_instance(dev)); } #endif @@ -728,7 +732,7 @@ static int uarte_nrfx_rx_counting_init(const struct device *dev) } #endif /* !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) */ -static int uarte_nrfx_init(const struct device *dev) +static int uarte_async_init(const struct device *dev) { struct uarte_nrfx_data *data = dev->data; NRF_UARTE_Type *uarte = get_uarte_instance(dev); @@ -749,7 +753,6 @@ static int uarte_nrfx_init(const struct device *dev) #endif nrf_uarte_int_enable(uarte, rx_int_mask); - nrf_uarte_enable(uarte); k_timer_init(&data->async->rx.timer, rx_timeout, NULL); k_timer_user_data_set(&data->async->rx.timer, (void *)dev); @@ -828,7 +831,6 @@ static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf, data->async->tx.len = len; data->async->tx.buf = buf; - nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); if (nrf_dma_accessible_check(uarte, buf)) { data->async->tx.xfer_buf = buf; @@ -838,6 +840,11 @@ static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf, (void)setup_tx_cache(dev); } + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + pm_device_runtime_get(dev); + } + + nrf_uarte_int_enable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); start_tx_locked(dev, data); irq_unlock(key); @@ -978,7 +985,7 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, rdata->next_buf = NULL; rdata->next_buf_len = 0; - if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || (cfg->flags & UARTE_CFG_FLAG_LOW_POWER)) { if (rdata->flush_cnt) { int cpy_len = MIN(len, rdata->flush_cnt); @@ -1029,7 +1036,10 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf, nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXSTARTED); rdata->enabled = true; - if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { + + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + pm_device_runtime_get(dev); + } else if (cfg->flags & UARTE_CFG_FLAG_LOW_POWER) { unsigned int key = irq_lock(); uarte_enable(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); @@ -1536,7 +1546,9 @@ static void rxto_isr(const struct device *dev) nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY); #endif - if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + pm_device_runtime_put(dev); + } else if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { uint32_t key = irq_lock(); uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); @@ -1553,25 +1565,27 @@ static void txstopped_isr(const struct device *dev) NRF_UARTE_Type *uarte = get_uarte_instance(dev); unsigned int key; - if (config->flags & UARTE_CFG_FLAG_LOW_POWER) { - nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); - key = irq_lock(); - uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); - irq_unlock(key); + key = irq_lock(); - if (!data->async->tx.len) { - return; + 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 (config->flags & UARTE_CFG_FLAG_LOW_POWER) { + nrf_uarte_int_disable(uarte, NRF_UARTE_INT_TXSTOPPED_MASK); + uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_TX, UARTE_FLAG_LOW_POWER_RX); } if (!data->async->tx.buf) { + irq_unlock(key); return; } - key = irq_lock(); - size_t amount = (data->async->tx.amount >= 0) ? - data->async->tx.amount : nrf_uarte_tx_amount_get(uarte); - irq_unlock(key); /* If there is a pending tx request, it means that uart_tx() @@ -1623,6 +1637,10 @@ static void txstopped_isr(const struct device *dev) data->async->tx.buf = NULL; data->async->tx.len = 0; + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + pm_device_runtime_put(dev); + } + user_callback(dev, &evt); } @@ -1785,6 +1803,7 @@ 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; unsigned int key; if (isr_mode) { @@ -1793,7 +1812,6 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c) if (is_tx_ready(dev)) { #if UARTE_ANY_ASYNC NRF_UARTE_Type *uarte = get_uarte_instance(dev); - struct uarte_nrfx_data *data = dev->data; if (data->async && data->async->tx.len && data->async->tx.amount < 0) { @@ -1810,6 +1828,13 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c) 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); @@ -2043,84 +2068,6 @@ static int endtx_stoptx_ppi_init(NRF_UARTE_Type *uarte, } #endif /* UARTE_ENHANCED_POLL_OUT */ -static int uarte_instance_init(const struct device *dev, - uint8_t interrupts_active) -{ - int err; - 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) - struct uarte_nrfx_data *data = dev->data; -#endif - - nrf_uarte_disable(uarte); - -#ifdef CONFIG_ARCH_POSIX - /* For simulation the DT provided peripheral address needs to be corrected */ - ((struct pinctrl_dev_config *)cfg->pcfg)->reg = (uintptr_t)cfg->uarte_regs; -#endif - - err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); - if (err < 0) { - return err; - } - -#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE - err = uarte_nrfx_configure(dev, &data->uart_config); - if (err) { - return err; - } -#else - nrf_uarte_baudrate_set(uarte, cfg->nrf_baudrate); - nrf_uarte_configure(uarte, &cfg->hw_config); -#endif - -#ifdef UARTE_HAS_ENDTX_STOPTX_SHORT - nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_ENDTX_STOPTX); -#elif defined(UARTE_ENHANCED_POLL_OUT) - if (cfg->flags & UARTE_CFG_FLAG_PPI_ENDTX) { - err = endtx_stoptx_ppi_init(uarte, data); - if (err < 0) { - return err; - } - } -#endif -#ifdef UARTE_ANY_ASYNC - if (data->async) { - err = uarte_nrfx_init(dev); - if (err < 0) { - return err; - } - } else -#endif - { - /* Enable receiver and transmitter */ - nrf_uarte_enable(uarte); - - if (!cfg->disable_rx) { - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); - - nrf_uarte_rx_buffer_set(uarte, cfg->poll_in_byte, 1); - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); - } - } - - if (!IS_ENABLED(UARTE_HAS_ENDTX_STOPTX_SHORT) && !(cfg->flags & UARTE_CFG_FLAG_PPI_ENDTX)) { - nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK); - } - - /* Set TXSTOPPED event by requesting fake (zero-length) transfer. - * Pointer to RAM variable (data->tx_buffer) is set because otherwise - * such operation may result in HardFault or RAM corruption. - */ - tx_start(dev, cfg->poll_out_byte, 0); - - return 0; -} - -#ifdef CONFIG_PM_DEVICE /** @brief Pend until TX is stopped. * * There are 2 configurations that must be handled: @@ -2144,7 +2091,9 @@ static void wait_for_tx_stopped(const struct device *dev) 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)) { - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX); + 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); } } @@ -2157,96 +2106,91 @@ static void wait_for_tx_stopped(const struct device *dev) } } - -static int uarte_nrfx_pm_action(const struct device *dev, - enum pm_device_action action) +static int uarte_pm_resume(const struct device *dev) { + const struct uarte_nrfx_config *cfg = dev->config; NRF_UARTE_Type *uarte = get_uarte_instance(dev); -#if defined(UARTE_ANY_ASYNC) || defined(UARTE_INTERRUPT_DRIVEN) +#if defined(UARTE_ANY_ASYNC) || (defined(UARTE_INTERRUPT_DRIVEN) && defined(CONFIG_PM_DEVICE)) struct uarte_nrfx_data *data = dev->data; #endif - const struct uarte_nrfx_config *cfg = dev->config; int ret; -#ifdef UARTE_ANY_ASYNC - /* If low power mode for asynchronous mode is used then there is nothing to do here. - * In low power mode UARTE is turned off whenever there is no activity. - */ - if (data->async && (cfg->flags & UARTE_CFG_FLAG_LOW_POWER)) { - return 0; + ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT); + if (ret < 0) { + return ret; } -#endif - - switch (action) { - case PM_DEVICE_ACTION_RESUME: - { - ret = pins_state_change(dev, true); - if (ret < 0) { - return ret; - } - nrf_uarte_enable(uarte); + nrf_uarte_enable(uarte); #if UARTE_BAUDRATE_RETENTION_WORKAROUND - nrf_uarte_baudrate_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, + nrf_uarte_baudrate_t baudrate = COND_CODE_1(CONFIG_UART_USE_RUNTIME_CONFIGURE, (data->nrf_baudrate), (cfg->nrf_baudrate)); - nrf_uarte_baudrate_set(get_uarte_instance(dev), baudrate); + nrf_uarte_baudrate_set(get_uarte_instance(dev), baudrate); #endif #ifdef UARTE_ANY_ASYNC - if (data->async) { - if (HW_RX_COUNTING_ENABLED(cfg)) { - nrfx_timer_enable(&cfg->timer); - } - - return 0; + if (data->async) { + if (HW_RX_COUNTING_ENABLED(cfg)) { + nrfx_timer_enable(&cfg->timer); } -#endif - if (!cfg->disable_rx) { - nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDRX); - nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STARTRX); -#ifdef UARTE_INTERRUPT_DRIVEN - if (data->int_driven && - data->int_driven->rx_irq_enabled) { - nrf_uarte_int_enable(uarte, - NRF_UARTE_INT_ENDRX_MASK); - } + return 0; + } #endif + if (!cfg->disable_rx) { + 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); } - break; +#endif } - case PM_DEVICE_ACTION_SUSPEND: - /* Disabling UART requires stopping RX, but stop RX event is - * only sent after each RX if async UART API is used. - */ + + return 0; +} + +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; + + (void)data; #ifdef UARTE_ANY_ASYNC - if (data->async) { - /* Entering inactive state requires device to be no - * active asynchronous calls. + if (data->async) { + /* Entering inactive state requires device to be no + * active asynchronous calls. + */ + __ASSERT_NO_MSG(!data->async->rx.enabled); + __ASSERT_NO_MSG(!data->async->tx.len); + if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + /* If runtime PM is enabled then reference counting ensures that + * suspend will not occur when TX is active. */ - __ASSERT_NO_MSG(!data->async->rx.enabled); - __ASSERT_NO_MSG(!data->async->tx.len); + __ASSERT_NO_MSG(nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)); + } else { + wait_for_tx_stopped(dev); + } #if !defined(CONFIG_UART_NRFX_UARTE_ENHANCED_RX) - if (data->async && HW_RX_COUNTING_ENABLED(cfg)) { - nrfx_timer_disable(&cfg->timer); - /* Timer/counter value is reset when disabled. */ - data->async->rx.total_byte_cnt = 0; - data->async->rx.total_user_byte_cnt = 0; - } -#endif - + if (data->async && HW_RX_COUNTING_ENABLED(cfg)) { + nrfx_timer_disable(&cfg->timer); + /* Timer/counter value is reset when disabled. */ + data->async->rx.total_byte_cnt = 0; + data->async->rx.total_user_byte_cnt = 0; } #endif + } else if (IS_ENABLED(UARTE_ANY_NONE_ASYNC)) +#endif + { if (nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_RXSTARTED)) { -#ifdef UARTE_INTERRUPT_DRIVEN +#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); + 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); + nrf_uarte_int_disable(uarte, NRF_UARTE_INT_ENDRX_MASK); } } #endif @@ -2262,22 +2206,140 @@ 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); - uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX, UARTE_FLAG_LOW_POWER_TX); + } - ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP); - if (ret < 0) { - return ret; - } + nrf_uarte_disable(uarte); + + return pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_SLEEP); +} +static int uarte_nrfx_pm_action(const struct device *dev, + enum pm_device_action action) +{ + int ret = -ENOTSUP; + + switch (action) { + case PM_DEVICE_ACTION_TURN_ON: + ret = 0; + break; + case PM_DEVICE_ACTION_RESUME: + ret = uarte_pm_resume(dev); + break; + case PM_DEVICE_ACTION_SUSPEND: + if (IS_ENABLED(CONFIG_PM_DEVICE)) { + ret = uarte_pm_suspend(dev); + } break; default: - return -ENOTSUP; + break; } + return ret; +} + +static int uarte_config_init(const struct device *dev) +{ +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE + return uarte_nrfx_configure(dev, &((struct uarte_nrfx_data *)dev->data)->uart_config); +#else + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + const struct uarte_nrfx_config *cfg = dev->config; + + nrf_uarte_baudrate_set(uarte, cfg->nrf_baudrate); + nrf_uarte_configure(uarte, &cfg->hw_config); return 0; +#endif +} + +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 handle_endtx = false; + + +#ifdef UARTE_HAS_ENDTX_STOPTX_SHORT + nrf_uarte_shorts_enable(uarte, NRF_UARTE_SHORT_ENDTX_STOPTX); +#else +#if defined(UARTE_ENHANCED_POLL_OUT) + struct uarte_nrfx_data *data = dev->data; + int err; + + if (cfg->flags & UARTE_CFG_FLAG_PPI_ENDTX) { + err = endtx_stoptx_ppi_init(uarte, data); + if (err < 0) { + return err; + } + } else +#endif + { + handle_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 (handle_endtx) { + while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_ENDTX)) { + } + nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_ENDTX); + nrf_uarte_task_trigger(uarte, NRF_UARTE_TASK_STOPTX); + nrf_uarte_int_enable(uarte, NRF_UARTE_INT_ENDTX_MASK); + } + while (!nrf_uarte_event_check(uarte, NRF_UARTE_EVENT_TXSTOPPED)) { + } + nrf_uarte_disable(uarte); + + return 0; +} + +static int uarte_instance_init(const struct device *dev, + uint8_t interrupts_active) +{ + int err; + NRF_UARTE_Type *uarte = get_uarte_instance(dev); + const struct uarte_nrfx_config *cfg = dev->config; + +#ifdef CONFIG_ARCH_POSIX + /* For simulation the DT provided peripheral address needs to be corrected */ + ((struct pinctrl_dev_config *)cfg->pcfg)->reg = (uintptr_t)cfg->uarte_regs; +#endif + + err = uarte_config_init(dev); + if (err) { + return err; + } + +#ifdef UARTE_ANY_ASYNC + struct uarte_nrfx_data *data = dev->data; + + if (data->async) { + err = uarte_async_init(dev); + if (err < 0) { + return err; + } + } else +#endif + { + nrf_uarte_rx_buffer_set(uarte, cfg->poll_in_byte, 1); + } + + err = uarte_tx_path_init(dev); + if (err) { + return err; + } + + return pm_device_driver_init(dev, uarte_nrfx_pm_action); } -#endif /* CONFIG_PM_DEVICE */ #define UARTE_IRQ_CONFIGURE(idx, isr_handler) \ do { \ @@ -2414,7 +2476,7 @@ static int uarte_nrfx_pm_action(const struct device *dev, } \ \ PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action, \ - COND_CODE_1(CONFIG_UART_##idx##_ASYNC, (PM_DEVICE_ISR_SAFE), (0))); \ + PM_DEVICE_ISR_SAFE); \ \ DEVICE_DT_DEFINE(UARTE(idx), \ uarte_##idx##_init, \ From 40c4f0cd4564cb981625277aeea16ecbd29b2c81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Mon, 7 Oct 2024 16:21:22 +0200 Subject: [PATCH 08/21] drivers: serial: nrfx: Use legacy shim by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So far new platforms did not use legacy shim but it shall be now a default uart shim for all platforms. Signed-off-by: Krzysztof Chruściński --- drivers/serial/Kconfig.nrfx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/serial/Kconfig.nrfx b/drivers/serial/Kconfig.nrfx index 2f71c815e64442..0ec43dfc6c43f8 100644 --- a/drivers/serial/Kconfig.nrfx +++ b/drivers/serial/Kconfig.nrfx @@ -31,10 +31,6 @@ config UART_NRFX_UARTE config UART_NRFX_UARTE_LEGACY_SHIM bool "Legacy UARTE shim" depends on UART_NRFX_UARTE - depends on !SOC_SERIES_NRF54LX - depends on !SOC_SERIES_NRF54HX || RISCV - depends on !SOC_SERIES_NRF92X || RISCV - # New shim takes more ROM. Until it is fixed use legacy shim. default y config UART_NRFX_UARTE_ENHANCED_RX From ffe83aaf67c5e8c3e34260cb1f8ed2db0730b15c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 13 Jun 2024 15:56:37 +0200 Subject: [PATCH 09/21] boards: nordic: nrf54h20dk: Disable asserts on cpuppr for tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpuppr is a small core with less than 64k of memory. In order to fit tests into that memory by default disable asserts (which saves few kB). Signed-off-by: Krzysztof Chruściński --- boards/nordic/nrf54h20dk/Kconfig.defconfig | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/boards/nordic/nrf54h20dk/Kconfig.defconfig b/boards/nordic/nrf54h20dk/Kconfig.defconfig index e37975f96adf47..9d16955f68df17 100644 --- a/boards/nordic/nrf54h20dk/Kconfig.defconfig +++ b/boards/nordic/nrf54h20dk/Kconfig.defconfig @@ -14,3 +14,11 @@ config BT_CTLR default y if BT endif # BOARD_NRF54H20DK_NRF54H20_CPURAD + +if BOARD_NRF54H20DK_NRF54H20_CPUPPR + +# As PPR has limited memory most of tests does not fit with asserts enabled. +config ASSERT + default n + +endif # BOARD_NRF54H20DK_NRF54H20_CPUPPR From 426e0c7e393958ed0abab072e9b824adb4ccb113 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 31 May 2024 13:14:49 +0200 Subject: [PATCH 10/21] tests: drivers: uart: Add test for async API using two instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add test which is using two independent UART devices. Validate behavior of asynchronous API. Signed-off-by: Krzysztof Chruściński --- .../uart/uart_async_dual/CMakeLists.txt | 8 + tests/drivers/uart/uart_async_dual/Kconfig | 15 + .../uart_async_dual/boards/nrf52_bsim.overlay | 44 ++ .../boards/nrf54h20dk_nrf54h20_common.dtsi | 70 ++ .../boards/nrf54h20dk_nrf54h20_cpuapp.conf | 1 + .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 11 + .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 7 + .../boards/nrf54h20dk_nrf54h20_cpurad.conf | 1 + .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 27 + .../nrf54l15pdk_nrf54l15_cpuapp.overlay | 70 ++ .../boards/nrf9160dk_nrf9160.conf | 1 + .../boards/nrf9160dk_nrf9160.overlay | 70 ++ tests/drivers/uart/uart_async_dual/prj.conf | 6 + tests/drivers/uart/uart_async_dual/src/main.c | 683 ++++++++++++++++++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 11 + .../sysbuild/vpr_launcher/prj.conf | 1 + .../uart/uart_async_dual/testcase.yaml | 31 + 17 files changed, 1057 insertions(+) create mode 100644 tests/drivers/uart/uart_async_dual/CMakeLists.txt create mode 100644 tests/drivers/uart/uart_async_dual/Kconfig create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.conf create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.conf create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.conf create mode 100644 tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.overlay create mode 100644 tests/drivers/uart/uart_async_dual/prj.conf create mode 100644 tests/drivers/uart/uart_async_dual/src/main.c create mode 100644 tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/prj.conf create mode 100644 tests/drivers/uart/uart_async_dual/testcase.yaml diff --git a/tests/drivers/uart/uart_async_dual/CMakeLists.txt b/tests/drivers/uart/uart_async_dual/CMakeLists.txt new file mode 100644 index 00000000000000..8dcea87b64876e --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(uart_async_dual) + +target_sources(app PRIVATE src/main.c) diff --git a/tests/drivers/uart/uart_async_dual/Kconfig b/tests/drivers/uart/uart_async_dual/Kconfig new file mode 100644 index 00000000000000..d1c5ad6134b2aa --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/Kconfig @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config UART_ASYNC_DUAL_TEST_TIMEOUT + int "Single test case length (in milliseconds)" + # For the simulated devices, which are run by default in CI, we set it to less to not spend too + # much CI time + default 500 if SOC_SERIES_BSIM_NRFXX + default 3000 + help + For how many loops will the stress test run. The higher this number the longer the + test and therefore the higher likelihood an unlikely race/event will be triggered. + +# Include Zephyr's Kconfig +source "Kconfig" diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay new file mode 100644 index 00000000000000..afd9264d190104 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&pinctrl { + uart0_default: uart0_default { + group1 { + psels = , + , + , + ; + }; + }; + + uart0_sleep: uart0_sleep { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; +}; + +dut: &uart0 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart0_default>; + pinctrl-1 = <&uart0_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +&timer0 { + status = "okay"; + interrupts = <8 0>; +}; + +/ { + busy-sim { + compatible = "vnd,busy-sim"; + status = "okay"; + counter = <&timer0>; + }; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi new file mode 100644 index 00000000000000..36d09322a544d7 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&pinctrl { + uart134_alt_default: uart134_alt_default { + group1 { + psels = ; + bias-pull-up; + }; + group2 { + psels = ; + }; + }; + + uart134_alt_sleep: uart134_alt_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + uart137_alt_default: uart137_alt_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart137_alt_sleep: uart137_alt_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +dut: &uart134 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart134_alt_default>; + pinctrl-1 = <&uart134_alt_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +dut_aux: &uart137 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart137_alt_default>; + pinctrl-1 = <&uart137_alt_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +&timer137 { + status = "okay"; + interrupts = <467 0>; +}; + +/ { + busy-sim { + compatible = "vnd,busy-sim"; + status = "okay"; + counter = <&timer137>; + }; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 00000000000000..97aee563c25590 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1 @@ +CONFIG_UART_ASYNC_TX_CACHE_SIZE=64 diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000000..3ef13b2ae0dcb2 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf54h20dk_nrf54h20_common.dtsi" + +&dut { + memory-regions = <&cpuapp_dma_region>; +}; + +&dut_aux { + memory-regions = <&cpuapp_dma_region>; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay new file mode 100644 index 00000000000000..5217f2dd7e9259 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf54h20dk_nrf54h20_common.dtsi" + +&timer137 { + interrupts = <467 (NRF_DEFAULT_IRQ_PRIORITY + 1)>; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.conf b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.conf new file mode 100644 index 00000000000000..033183df85dd10 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.conf @@ -0,0 +1 @@ +CONFIG_UART_ASYNC_TX_CACHE_SIZE=16 diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay new file mode 100644 index 00000000000000..abc5944565f39c --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf54h20dk_nrf54h20_common.dtsi" + +&cpurad_dma_region { + compatible = "zephyr,memory-region"; + reg = <0x1e80 0x180>; +}; + +&dut { + memory-regions = <&cpurad_dma_region>; +}; + +&dut_aux { + memory-regions = <&cpurad_dma_region>; +}; + +&timer131 { + status = "okay"; + prescaler = <0>; +}; + +/ { + chosen { + zephyr,cpu-stats-counter = &timer131; + }; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay new file mode 100644 index 00000000000000..cd06494115318b --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&pinctrl { + uart21_default: uart21_default { + group1 { + psels = ; + bias-pull-up; + }; + group2 { + psels = ; + }; + }; + + uart21_sleep: uart21_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + uart22_default: uart22_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart22_sleep: uart22_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +dut: &uart21 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart21_default>; + pinctrl-1 = <&uart21_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +dut_aux: &uart22 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart22_default>; + pinctrl-1 = <&uart22_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +&timer20 { + status = "okay"; + interrupts = <202 0>; +}; + +/ { + busy-sim { + compatible = "vnd,busy-sim"; + status = "okay"; + counter = <&timer20>; + }; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.conf b/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.conf new file mode 100644 index 00000000000000..4da3788618f34c --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.conf @@ -0,0 +1 @@ +CONFIG_UART_NRFX_UARTE_ENHANCED_RX=y diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.overlay new file mode 100644 index 00000000000000..d47b0db3ad9c40 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/boards/nrf9160dk_nrf9160.overlay @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +&pinctrl { + uart1_default: uart1_default { + group1 { + psels = ; + bias-pull-up; + }; + group2 { + psels = ; + }; + }; + + uart1_sleep: uart1_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; + + uart2_default: uart2_default { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart2_sleep: uart2_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +dut: &uart1 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart1_default>; + pinctrl-1 = <&uart1_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +dut_aux: &uart2 { + status = "okay"; + current-speed = <115200>; + pinctrl-0 = <&uart2_default>; + pinctrl-1 = <&uart2_sleep>; + pinctrl-names = "default", "sleep"; + hw-flow-control; +}; + +&timer2 { + status = "okay"; + interrupts = <17 0>; +}; + +/ { + busy-sim { + compatible = "vnd,busy-sim"; + status = "okay"; + counter = <&timer2>; + }; +}; diff --git a/tests/drivers/uart/uart_async_dual/prj.conf b/tests/drivers/uart/uart_async_dual/prj.conf new file mode 100644 index 00000000000000..8c564ed02b3971 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/prj.conf @@ -0,0 +1,6 @@ +CONFIG_ZTEST=y +CONFIG_RING_BUFFER=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_SERIAL=y +CONFIG_UART_ASYNC_API=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y diff --git a/tests/drivers/uart/uart_async_dual/src/main.c b/tests/drivers/uart/uart_async_dual/src/main.c new file mode 100644 index 00000000000000..764c5fd7e61658 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/src/main.c @@ -0,0 +1,683 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +LOG_MODULE_REGISTER(test); + +#if DT_NODE_EXISTS(DT_NODELABEL(dut)) +#define DUT_NODE DT_NODELABEL(dut) +#else +#error "dut not defined" +#endif + +#if DT_NODE_EXISTS(DT_NODELABEL(dut_aux)) +#define DUT_AUX_NODE DT_NODELABEL(dut_aux) +#else +#define DUT_AUX_NODE DT_NODELABEL(dut) +#endif + +#define TX_TIMEOUT 100000 +#define RX_TIMEOUT 2000 + +#define MAX_PACKET_LEN 128 + +static const struct device *const rx_dev = DEVICE_DT_GET(DUT_NODE); +static const struct device *const tx_dev = DEVICE_DT_GET(DUT_AUX_NODE); + +enum test_tx_mode { + TX_BULK, + TX_PACKETS, +}; + +struct test_tx_data { + uint8_t buf[512]; + struct ring_buf rbuf; + atomic_t busy; + uint8_t packet_len; + uint8_t cnt; + volatile bool cont; + volatile enum test_tx_mode mode; + struct k_sem sem; +}; + +enum test_rx_state { + RX_HDR, + RX_PAYLOAD +}; + +enum test_rx_mode { + RX_CONT, + RX_DIS, +}; + +struct test_rx_data { + uint8_t hdr[1]; + uint8_t buf[256]; + uint32_t rx_cnt; + enum test_rx_state state; + enum test_rx_mode mode; + volatile bool cont; + bool buf_req; + struct k_sem sem; +}; + +static struct test_tx_data tx_data; +static struct test_rx_data rx_data; + +static void fill_tx(struct test_tx_data *data) +{ + uint8_t *buf; + uint32_t len; + int err; + + if (data->mode == TX_PACKETS) { + err = k_sem_take(&data->sem, K_MSEC(100)); + if (err < 0 && !data->cont) { + return; + } + zassert_equal(err, 0); + + uint8_t len = sys_rand8_get(); + + len = len % MAX_PACKET_LEN; + len = MAX(2, len); + + data->packet_len = len; + for (int i = 0; i < len; i++) { + data->buf[i] = len - i; + } + + return; + } + + while ((len = ring_buf_put_claim(&data->rbuf, &buf, 255)) > 1) { + uint8_t r = (sys_rand8_get() % MAX_PACKET_LEN) % len; + uint8_t packet_len = MAX(r, 2); + uint8_t rem = len - packet_len; + + packet_len = (rem < 3) ? len : packet_len; + buf[0] = packet_len; + for (int i = 1; i < packet_len; i++) { + buf[i] = packet_len - i; + } + + ring_buf_put_finish(&data->rbuf, packet_len); + } +} + +static void try_tx(const struct device *dev, bool irq) +{ + uint8_t *buf; + uint32_t len; + int err; + + if (!tx_data.cont) { + rx_data.cont = false; + return; + } + + if ((tx_data.mode == TX_PACKETS) && (tx_data.packet_len > 0)) { + uint8_t len = tx_data.packet_len; + + tx_data.packet_len = 0; + err = uart_tx(dev, tx_data.buf, len, TX_TIMEOUT); + zassert_equal(err, 0, + "Unexpected err:%d irq:%d cont:%d\n", + err, irq, tx_data.cont); + return; + } + zassert_true(tx_data.mode == TX_BULK); + + if (!atomic_cas(&tx_data.busy, 0, 1)) { + return; + } + + len = ring_buf_get_claim(&tx_data.rbuf, &buf, 255); + if (len > 0) { + err = uart_tx(dev, buf, len, TX_TIMEOUT); + zassert_equal(err, 0, + "Unexpected err:%d irq:%d cont:%d\n", + err, irq, tx_data.cont); + } +} + +static void on_tx_done(const struct device *dev, struct uart_event *evt) +{ + if (tx_data.mode == TX_PACKETS) { + k_sem_give(&tx_data.sem); + return; + } + + /* Finish previous data chunk and start new if any pending. */ + ring_buf_get_finish(&tx_data.rbuf, evt->data.tx.len); + atomic_set(&tx_data.busy, 0); + try_tx(dev, true); +} + +static void on_rx_rdy(const struct device *dev, struct uart_event *evt) +{ + uint32_t len = evt->data.rx.len; + uint32_t off = evt->data.rx.offset; + int err; + + if (!rx_data.cont) { + return; + } + + rx_data.rx_cnt += evt->data.rx.len; + if (evt->data.rx.buf == rx_data.hdr) { + rx_data.state = RX_PAYLOAD; + if ((rx_data.mode == RX_CONT) && rx_data.buf_req) { + size_t l = rx_data.hdr[0] - 1; + + zassert_true(l > 0); + rx_data.buf_req = false; + err = uart_rx_buf_rsp(dev, rx_data.buf, rx_data.hdr[0] - 1); + } + } else { + /* Payload received */ + rx_data.state = RX_HDR; + zassert_equal(len, rx_data.hdr[0] - 1); + + for (int i = 0; i < len; i++) { + bool ok = evt->data.rx.buf[off + i] == (uint8_t)(len - i); + + if (!ok) { + LOG_ERR("Unexpected data at %d, exp:%02x got:%02x", + i, len - i, evt->data.rx.buf[off + i]); + } + + zassert_true(ok, "Unexpected data at %d, exp:%02x got:%02x", + i, len - i, evt->data.rx.buf[off + i]); + if (!ok) { + rx_data.cont = false; + tx_data.cont = false; + /* Avoid flood of errors as we are in the interrupt and ztest + * cannot abort from here. + */ + return; + } + } + if ((rx_data.mode == RX_CONT) && rx_data.buf_req) { + rx_data.buf_req = false; + err = uart_rx_buf_rsp(dev, rx_data.hdr, 1); + } + } +} + +static void on_rx_dis(const struct device *dev, struct uart_event *evt, void *user_data) +{ + ARG_UNUSED(evt); + struct test_rx_data *data = user_data; + int err; + uint8_t *buf = (data->state == RX_HDR) ? data->hdr : data->buf; + uint32_t len = (data->state == RX_HDR) ? 1 : (data->hdr[0] - 1); + + data->buf_req = false; + + if (!data->cont) { + return; + } + + + zassert_true(len > 0); + err = uart_rx_enable(dev, buf, len, RX_TIMEOUT); + zassert_equal(err, 0, "Unexpected err:%d", err); +} + +static void test_end(void) +{ + tx_data.cont = false; +} + +static void test_timeout(struct k_timer *timer) +{ + test_end(); +} + +static K_TIMER_DEFINE(test_timer, test_timeout, NULL); + +static void uart_callback(const struct device *dev, struct uart_event *evt, void *user_data) +{ + switch (evt->type) { + case UART_TX_DONE: + zassert_true(dev == tx_dev); + on_tx_done(dev, evt); + break; + case UART_TX_ABORTED: + zassert_true(dev == tx_dev); + zassert_false(tx_data.cont, + "Unexpected TX abort, receiver not reading data on time"); + break; + case UART_RX_RDY: + zassert_true(dev == rx_dev); + on_rx_rdy(dev, evt); + break; + case UART_RX_BUF_RELEASED: + zassert_true(dev == rx_dev); + break; + case UART_RX_BUF_REQUEST: + rx_data.buf_req = true; + zassert_true(dev == rx_dev); + break; + case UART_RX_DISABLED: + zassert_true(dev == rx_dev); + on_rx_dis(dev, evt, &rx_data); + break; + case UART_RX_STOPPED: + zassert_true(false); + break; + default: + zassert_true(false); + break; + } +} + +static void config_baudrate(uint32_t rate) +{ + struct uart_config config; + int err; + + err = uart_config_get(rx_dev, &config); + zassert_equal(err, 0, "Unexpected err:%d", err); + + config.baudrate = rate; + + err = uart_configure(rx_dev, &config); + zassert_equal(err, 0, "Unexpected err:%d", err); + + if (rx_dev != tx_dev) { + err = uart_configure(tx_dev, &config); + zassert_equal(err, 0, "Unexpected err:%d", err); + } +} + +/* Test is running following scenario. Transmitter is sending packets which + * has 1 byte header with length followed by the payload. Transmitter can send + * packets in two modes: bulk where data is send in chunks without gaps between + * packets or in packet mode where transmitter sends packet by packet. + * Receiver receives the header and sets reception of the payload based on the + * length in the header. It can also work in two modes: continuous (CONT) where new + * transfer is started from UART_RX_RDY event or slower mode (DIS) where reception is + * started from UART_RX_DISABLED event. + * + * Test has busy simulator running if it is enabled in the configuration. + */ +static void var_packet_hwfc(uint32_t baudrate, bool tx_packets, bool cont) +{ + int err; + + config_baudrate(baudrate); + + if (IS_ENABLED(CONFIG_TEST_BUSY_SIM)) { + uint32_t active_avg = (baudrate == 1000000) ? 5 : 30; + uint32_t active_delta = (baudrate == 1000000) ? 2 : 10; + + busy_sim_start(active_avg, active_delta, 100, 50, NULL); + } + + memset(&tx_data, 0, sizeof(tx_data)); + memset(&rx_data, 0, sizeof(rx_data)); + tx_data.cont = true; + tx_data.mode = tx_packets ? TX_PACKETS : TX_BULK; + k_sem_init(&tx_data.sem, tx_packets ? 1 : 0, 1); + + rx_data.cont = true; + rx_data.rx_cnt = 0; + rx_data.state = RX_HDR; + rx_data.mode = cont ? RX_CONT : RX_DIS; + + ring_buf_init(&tx_data.rbuf, sizeof(tx_data.buf), tx_data.buf); + + k_timer_start(&test_timer, K_MSEC(CONFIG_UART_ASYNC_DUAL_TEST_TIMEOUT), K_NO_WAIT); + + err = uart_callback_set(rx_dev, uart_callback, &rx_data); + zassert_equal(err, 0, "Unexpected err:%d", err); + + err = uart_callback_set(tx_dev, uart_callback, &tx_data); + zassert_equal(err, 0, "Unexpected err:%d", err); + + on_rx_dis(rx_dev, NULL, &rx_data); + + while (tx_data.cont || rx_data.cont) { + fill_tx(&tx_data); + k_msleep(1); + try_tx(tx_dev, false); + } + + if (IS_ENABLED(CONFIG_TEST_BUSY_SIM)) { + busy_sim_stop(); + } + + (void)uart_tx_abort(tx_dev); + (void)uart_rx_disable(rx_dev); + + /* Flush all TX data that may be already started. */ + k_msleep(10); + (void)uart_rx_enable(rx_dev, rx_data.buf, sizeof(rx_data.buf), RX_TIMEOUT); + k_msleep(10); + (void)uart_rx_disable(rx_dev); + k_msleep(10); + + TC_PRINT("Received %d bytes for %d ms\n", + rx_data.rx_cnt, CONFIG_UART_ASYNC_DUAL_TEST_TIMEOUT); + zassert_true(rx_data.rx_cnt > 1000, "Unexected RX cnt: %d", rx_data.rx_cnt); +} + +ZTEST(uart_async_dual, test_var_packets_tx_bulk_dis_hwfc) +{ + /* TX in bulk mode, RX in DIS mode, 115k2 */ + var_packet_hwfc(115200, false, false); +} + +ZTEST(uart_async_dual, test_var_packets_tx_bulk_cont_hwfc) +{ + /* TX in bulk mode, RX in CONT mode, 115k2 */ + var_packet_hwfc(115200, false, true); +} + +ZTEST(uart_async_dual, test_var_packets_tx_bulk_dis_hwfc_1m) +{ + /* TX in bulk mode, RX in DIS mode, 1M */ + var_packet_hwfc(1000000, false, false); +} + +ZTEST(uart_async_dual, test_var_packets_tx_bulk_cont_hwfc_1m) +{ + /* TX in bulk mode, RX in CONT mode, 1M */ + var_packet_hwfc(1000000, false, true); +} + +ZTEST(uart_async_dual, test_var_packets_dis_hwfc) +{ + /* TX in packet mode, RX in DIS mode, 115k2 */ + var_packet_hwfc(115200, true, false); +} + +ZTEST(uart_async_dual, test_var_packets_cont_hwfc) +{ + /* TX in packet mode, RX in CONT mode, 115k2 */ + var_packet_hwfc(115200, true, true); +} + +ZTEST(uart_async_dual, test_var_packets_dis_hwfc_1m) +{ + /* TX in packet mode, RX in DIS mode, 1M */ + var_packet_hwfc(1000000, true, false); +} + +ZTEST(uart_async_dual, test_var_packets_cont_hwfc_1m) +{ + /* TX in packet mode, RX in CONT mode, 1M */ + var_packet_hwfc(1000000, true, true); +} + +static void hci_like_callback(const struct device *dev, struct uart_event *evt, void *user_data) +{ + switch (evt->type) { + case UART_TX_DONE: + zassert_true(dev == tx_dev); + k_sem_give(&tx_data.sem); + break; + case UART_TX_ABORTED: + zassert_true(dev == tx_dev); + zassert_false(tx_data.cont, + "Unexpected TX abort, receiver not reading data on time"); + break; + case UART_RX_RDY: + rx_data.rx_cnt += evt->data.rx.len; + zassert_true(dev == rx_dev); + break; + case UART_RX_BUF_RELEASED: + zassert_true(dev == rx_dev); + break; + case UART_RX_BUF_REQUEST: + zassert_true(dev == rx_dev); + break; + case UART_RX_DISABLED: + zassert_true(dev == rx_dev); + k_sem_give(&rx_data.sem); + break; + case UART_RX_STOPPED: + zassert_true(false); + break; + default: + zassert_true(false); + break; + } +} + +static bool rx(uint8_t *buf, size_t len) +{ + int err; + + err = uart_rx_enable(rx_dev, buf, len, RX_TIMEOUT); + zassert_equal(err, 0, "Unexpected err:%d", err); + + err = k_sem_take(&rx_data.sem, K_MSEC(100)); + if ((err < 0) || !tx_data.cont) { + zassert_false(tx_data.cont); + err = uart_rx_disable(rx_dev); + if (err == 0) { + err = k_sem_take(&rx_data.sem, K_MSEC(100)); + zassert_equal(err, 0, "Unexpected err:%d", err); + } + + return false; + } + + return true; +} + +static void check_pre_hdr(uint8_t *buf, uint8_t last_hdr) +{ + uint8_t exp_idx = (last_hdr + 1) & 0x7F; + + zassert_true(buf[0] & 0x80); + zassert_equal(exp_idx, buf[0] & 0x7F); +} + +static uint8_t get_len(uint8_t *buf) +{ + static const uint8_t exp_buf[] = {'a', 'b', 'c'}; + int rv; + + rv = memcmp(buf, exp_buf, sizeof(exp_buf)); + + zassert_equal(rv, 0, "exp: %02x %02x %02x, got: %02x %02x %02x", + exp_buf[0], exp_buf[1], exp_buf[2], buf[0], buf[1], buf[2]); + + return buf[sizeof(exp_buf)]; +} + +static void check_payload(uint8_t *buf, uint8_t len) +{ + for (int i = 0; i < len; i++) { + uint8_t exp_val = len - i; + bool ok = buf[i] == exp_val; + + zassert_true(ok, "Unexpected byte at %d, got:%02x exp:%02x", i, buf[i], exp_val); + if (!ok) { + test_end(); + return; + } + } +} + +static void hci_like_tx_prepare(struct test_tx_data *tx_data) +{ + int idx = tx_data->cnt & 0x1 ? sizeof(tx_data->buf) / 2 : 0; + uint8_t *buf = &tx_data->buf[idx]; + uint8_t len = sys_rand8_get() & 0x1F; + + len = MAX(1, len); + + *buf++ = 0x80 | (tx_data->cnt & 0x7F); + *buf++ = 'a'; + *buf++ = 'b'; + *buf++ = 'c'; + *buf++ = len; + + for (int i = 0; i < len; i++) { + *buf++ = len - i; + } + + tx_data->cnt++; +} + +static void hci_like_tx(struct test_tx_data *tx_data) +{ + int idx = tx_data->cnt & 0x1 ? 0 : sizeof(tx_data->buf) / 2; + uint8_t *buf = &tx_data->buf[idx]; + uint8_t len = buf[4] + 5; + int err; + + err = uart_tx(tx_dev, buf, len, TX_TIMEOUT); + zassert_equal(err, 0, "Unexpected err:%d", err); +} + +static void hci_like_tx_thread_entry(void *p1, void *p2, void *p3) +{ + int err; + + while (tx_data.cont) { + hci_like_tx_prepare(&tx_data); + + err = k_sem_take(&tx_data.sem, K_MSEC(500)); + if (err < 0) { + break; + } + zassert_equal(err, 0, "Unexpected err:%d", err); + + hci_like_tx(&tx_data); + } +} + + +static void hci_like_rx(void) +{ + uint8_t last_hdr = 0xff; + uint8_t len; + bool cont; + + while (1) { + cont = rx(rx_data.buf, 1); + if (!cont) { + break; + } + check_pre_hdr(rx_data.buf, last_hdr); + last_hdr = rx_data.buf[0]; + + cont = rx(rx_data.buf, 4); + if (!cont) { + break; + } + len = get_len(rx_data.buf); + + cont = rx(rx_data.buf, len); + if (!cont) { + break; + } + check_payload(rx_data.buf, len); + } +} + +#define HCI_LIKE_TX_STACK_SIZE 2048 +static K_THREAD_STACK_DEFINE(hci_like_tx_thread_stack, HCI_LIKE_TX_STACK_SIZE); +static struct k_thread hci_like_tx_thread; + +/* Test is emulating behavior that is implemented in the bluetooth HCI controller sample + * which is using UART asynchronous API. Each packet consists of 3 parts: 1 byte header + * indicating a type, 4 byte second header with length of the variable part which is + * a third part. + * + * Receiver sets reception of each part after receiver is disabled. + * + * If enabled, busy simulator is used in the test. + */ +static void hci_like_test(uint32_t baudrate) +{ + int err; + + config_baudrate(baudrate); + + if (IS_ENABLED(CONFIG_TEST_BUSY_SIM)) { + uint32_t active_avg = (baudrate == 1000000) ? 10 : 50; + uint32_t active_delta = (baudrate == 1000000) ? 5 : 20; + + busy_sim_start(active_avg, active_delta, 100, 50, NULL); + } + + memset(&tx_data, 0, sizeof(tx_data)); + memset(&rx_data, 0, sizeof(rx_data)); + tx_data.cnt = 0; + tx_data.cont = true; + rx_data.cont = true; + + k_sem_init(&tx_data.sem, 1, 1); + k_sem_init(&rx_data.sem, 0, 1); + + k_timer_start(&test_timer, K_MSEC(CONFIG_UART_ASYNC_DUAL_TEST_TIMEOUT), K_NO_WAIT); + + err = uart_callback_set(rx_dev, hci_like_callback, NULL); + zassert_equal(err, 0); + + err = uart_callback_set(tx_dev, hci_like_callback, NULL); + zassert_equal(err, 0); + + k_thread_create(&hci_like_tx_thread, hci_like_tx_thread_stack, + K_THREAD_STACK_SIZEOF(hci_like_tx_thread_stack), + hci_like_tx_thread_entry, NULL, NULL, NULL, + K_PRIO_COOP(7), 0, K_MSEC(10)); + + k_msleep(1); + + hci_like_rx(); + + if (IS_ENABLED(CONFIG_TEST_BUSY_SIM)) { + busy_sim_stop(); + } + + /* Flush data. */ + (void)uart_tx_abort(tx_dev); + k_msleep(10); + (void)uart_rx_enable(rx_dev, rx_data.buf, sizeof(rx_data.buf), RX_TIMEOUT); + k_msleep(1); + (void)uart_rx_disable(rx_dev); + + k_thread_abort(&hci_like_tx_thread); + k_msleep(10); + + TC_PRINT("Received %d bytes for %d ms\n", + rx_data.rx_cnt, CONFIG_UART_ASYNC_DUAL_TEST_TIMEOUT); +} + +ZTEST(uart_async_dual, test_hci_like_115k) +{ + /* HCI like test at 115k2 */ + hci_like_test(115200); +} + +ZTEST(uart_async_dual, test_hci_like_1M) +{ + /* HCI like test at 1M */ + hci_like_test(1000000); +} + +static void *setup(void) +{ + zassert_true(device_is_ready(rx_dev)); + zassert_true(device_is_ready(tx_dev)); + + return NULL; +} + +ZTEST_SUITE(uart_async_dual, NULL, setup, NULL, NULL, NULL); diff --git a/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000000..fcdc838a54e431 --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,11 @@ +#include "../../../boards/nrf54h20dk_nrf54h20_common.dtsi" + +&dut { + status = "reserved"; + interrupt-parent = <&cpuppr_clic>; +}; + +&dut_aux { + status = "reserved"; + interrupt-parent = <&cpuppr_clic>; +}; diff --git a/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/prj.conf b/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/prj.conf new file mode 100644 index 00000000000000..b2a4ba591044ee --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/sysbuild/vpr_launcher/prj.conf @@ -0,0 +1 @@ +# nothing here diff --git a/tests/drivers/uart/uart_async_dual/testcase.yaml b/tests/drivers/uart/uart_async_dual/testcase.yaml new file mode 100644 index 00000000000000..76870d4e2c394c --- /dev/null +++ b/tests/drivers/uart/uart_async_dual/testcase.yaml @@ -0,0 +1,31 @@ +common: + tags: + - drivers + - uart +tests: + drivers.uart.async_dual: + harness: ztest + harness_config: + fixture: uart_loopback + depends_on: gpio + platform_allow: + - nrf54l15pdk/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + - nrf54h20dk/nrf54h20/cpuppr + - nrf9160dk/nrf9160 + - nrf52_bsim + drivers.uart.async_dual.busy_sim: + harness: ztest + harness_config: + fixture: uart_loopback + depends_on: gpio + platform_allow: + - nrf54l15pdk/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + - nrf54h20dk/nrf54h20/cpuppr + - nrf9160dk/nrf9160 + - nrf52_bsim + extra_configs: + - CONFIG_TEST_BUSY_SIM=y From ae36199e87d84d9c5c9e2bec66e42054eac3edcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Wed, 26 Jun 2024 15:44:09 +0200 Subject: [PATCH 11/21] tests: drivers: uart: async_api: Add nrf54h20 cpuppr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configuration for nrf54h20dk/nrf54h20/cpuppr. Signed-off-by: Krzysztof Chruściński --- .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 3 +++ .../vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 6 ++++++ .../uart/uart_async_api/sysbuild/vpr_launcher/prj.conf | 1 + 3 files changed, 10 insertions(+) create mode 100644 tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay create mode 100644 tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/prj.conf diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay new file mode 100644 index 00000000000000..f65b4dd3b0ba0f --- /dev/null +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -0,0 +1,3 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf54h20dk_nrf54h20_common.dtsi" diff --git a/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000000..a1b825882f502b --- /dev/null +++ b/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,6 @@ +#include "../../../boards/nrf54h20dk_nrf54h20_common.dtsi" + +&dut { + status = "reserved"; + interrupt-parent = <&cpuppr_clic>; +}; diff --git a/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/prj.conf b/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/prj.conf new file mode 100644 index 00000000000000..b2a4ba591044ee --- /dev/null +++ b/tests/drivers/uart/uart_async_api/sysbuild/vpr_launcher/prj.conf @@ -0,0 +1 @@ +# nothing here From 3ec58365255fca7b63e7aec1aea5d39cf2753ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 18 Jul 2024 14:37:44 +0200 Subject: [PATCH 12/21] tests: drivers: uart: async_api: Add missing static keyword MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple variable in the test were missing static keyword. Signed-off-by: Krzysztof Chruściński --- .../uart/uart_async_api/src/test_uart_async.c | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/drivers/uart/uart_async_api/src/test_uart_async.c b/tests/drivers/uart/uart_async_api/src/test_uart_async.c index 15b4b6fba41a9a..58334fd9dbe6a3 100644 --- a/tests/drivers/uart/uart_async_api/src/test_uart_async.c +++ b/tests/drivers/uart/uart_async_api/src/test_uart_async.c @@ -24,12 +24,12 @@ K_SEM_DEFINE(rx_buf_coherency, 0, 255); K_SEM_DEFINE(rx_buf_released, 0, 1); K_SEM_DEFINE(rx_disabled, 0, 1); -ZTEST_BMEM volatile bool failed_in_isr; +static ZTEST_BMEM volatile bool failed_in_isr; static ZTEST_BMEM const struct device *const uart_dev = DEVICE_DT_GET(UART_NODE); static void read_abort_timeout(struct k_timer *timer); -K_TIMER_DEFINE(read_abort_timer, read_abort_timeout, NULL); +static K_TIMER_DEFINE(read_abort_timer, read_abort_timeout, NULL); static void init_test(void) @@ -100,7 +100,7 @@ struct test_data { #if NOCACHE_MEM static struct test_data tdata __used __NOCACHE; #else -ZTEST_BMEM struct test_data tdata; +static ZTEST_BMEM struct test_data tdata; #endif /* NOCACHE_MEM */ static void test_single_read_callback(const struct device *dev, @@ -327,13 +327,13 @@ ZTEST_USER(uart_async_multi_rx, test_multiple_rx_enable) static __aligned(32) uint8_t chained_read_buf[2][8] __used __NOCACHE; static __aligned(32) uint8_t chained_cpy_buf[10] __used __NOCACHE; #else -ZTEST_BMEM uint8_t chained_read_buf[2][8]; -ZTEST_BMEM uint8_t chained_cpy_buf[10]; +static ZTEST_BMEM uint8_t chained_read_buf[2][8]; +static ZTEST_BMEM uint8_t chained_cpy_buf[10]; #endif /* NOCACHE_MEM */ -ZTEST_BMEM volatile uint8_t rx_data_idx; -ZTEST_BMEM uint8_t rx_buf_idx; +static ZTEST_BMEM volatile uint8_t rx_data_idx; +static ZTEST_BMEM uint8_t rx_buf_idx; -ZTEST_BMEM uint8_t *read_ptr; +static ZTEST_BMEM uint8_t *read_ptr; static void test_chained_read_callback(const struct device *dev, struct uart_event *evt, void *user_data) @@ -417,9 +417,9 @@ ZTEST_USER(uart_async_chain_read, test_chained_read) #if NOCACHE_MEM static __aligned(32) uint8_t double_buffer[2][12] __used __NOCACHE; #else -ZTEST_BMEM uint8_t double_buffer[2][12]; +static ZTEST_BMEM uint8_t double_buffer[2][12]; #endif /* NOCACHE_MEM */ -ZTEST_DMEM uint8_t *next_buf = double_buffer[1]; +static ZTEST_DMEM uint8_t *next_buf = double_buffer[1]; static void test_double_buffer_callback(const struct device *dev, struct uart_event *evt, void *user_data) @@ -492,10 +492,10 @@ ZTEST_USER(uart_async_double_buf, test_double_buffer) static __aligned(32) uint8_t test_read_abort_rx_buf[2][100] __used __NOCACHE; static __aligned(32) uint8_t test_read_abort_read_buf[100] __used __NOCACHE; #else -ZTEST_BMEM uint8_t test_read_abort_rx_buf[2][100]; -ZTEST_BMEM uint8_t test_read_abort_read_buf[100]; +static ZTEST_BMEM uint8_t test_read_abort_rx_buf[2][100]; +static ZTEST_BMEM uint8_t test_read_abort_read_buf[100]; #endif /* NOCACHE_MEM */ -ZTEST_BMEM int test_read_abort_rx_cnt; +static ZTEST_BMEM int test_read_abort_rx_cnt; static void test_read_abort_callback(const struct device *dev, struct uart_event *evt, void *user_data) @@ -608,12 +608,12 @@ ZTEST_USER(uart_async_read_abort, test_read_abort) } -ZTEST_BMEM volatile size_t sent; -ZTEST_BMEM volatile size_t received; +static ZTEST_BMEM volatile size_t sent; +static ZTEST_BMEM volatile size_t received; #if NOCACHE_MEM static __aligned(32) uint8_t test_rx_buf[2][100] __used __NOCACHE; #else -ZTEST_BMEM uint8_t test_rx_buf[2][100]; +static ZTEST_BMEM uint8_t test_rx_buf[2][100]; #endif /* NOCACHE_MEM */ static void test_write_abort_callback(const struct device *dev, @@ -771,12 +771,12 @@ ZTEST_USER(uart_async_timeout, test_forever_timeout) #if NOCACHE_MEM -const uint8_t chained_write_tx_bufs[2][10] = {"Message 1", "Message 2"}; +static const uint8_t chained_write_tx_bufs[2][10] = {"Message 1", "Message 2"}; #else -ZTEST_DMEM uint8_t chained_write_tx_bufs[2][10] = {"Message 1", "Message 2"}; +static ZTEST_DMEM uint8_t chained_write_tx_bufs[2][10] = {"Message 1", "Message 2"}; #endif /* NOCACHE_MEM */ -ZTEST_DMEM bool chained_write_next_buf = true; -ZTEST_BMEM volatile uint8_t tx_sent; +static ZTEST_DMEM bool chained_write_next_buf = true; +static ZTEST_BMEM volatile uint8_t tx_sent; static void test_chained_write_callback(const struct device *dev, struct uart_event *evt, void *user_data) @@ -855,12 +855,12 @@ static __aligned(32) uint8_t long_rx_buf[1024] __used __NOCACHE; static __aligned(32) uint8_t long_rx_buf2[1024] __used __NOCACHE; static __aligned(32) uint8_t long_tx_buf[1000] __used __NOCACHE; #else -ZTEST_BMEM uint8_t long_rx_buf[1024]; -ZTEST_BMEM uint8_t long_rx_buf2[1024]; -ZTEST_BMEM uint8_t long_tx_buf[1000]; +static ZTEST_BMEM uint8_t long_rx_buf[1024]; +static ZTEST_BMEM uint8_t long_rx_buf2[1024]; +static ZTEST_BMEM uint8_t long_tx_buf[1000]; #endif /* NOCACHE_MEM */ -ZTEST_BMEM volatile uint8_t evt_num; -ZTEST_BMEM size_t long_received[2]; +static ZTEST_BMEM volatile uint8_t evt_num; +static ZTEST_BMEM size_t long_received[2]; static void test_long_buffers_callback(const struct device *dev, struct uart_event *evt, void *user_data) From 005bf6efae1c0e120cdd837b80b91cce3b4cdb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 18 Jul 2024 14:28:51 +0200 Subject: [PATCH 13/21] tests: drivers: uart: async_api: Rework for multi instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework the test to be able to run on multiple instances. Signed-off-by: Krzysztof Chruściński --- .../uart/uart_async_api/src/test_uart_async.c | 106 ++++++++++++------ 1 file changed, 73 insertions(+), 33 deletions(-) diff --git a/tests/drivers/uart/uart_async_api/src/test_uart_async.c b/tests/drivers/uart/uart_async_api/src/test_uart_async.c index 58334fd9dbe6a3..cbd7f7ae120a10 100644 --- a/tests/drivers/uart/uart_async_api/src/test_uart_async.c +++ b/tests/drivers/uart/uart_async_api/src/test_uart_async.c @@ -25,39 +25,49 @@ K_SEM_DEFINE(rx_buf_released, 0, 1); K_SEM_DEFINE(rx_disabled, 0, 1); static ZTEST_BMEM volatile bool failed_in_isr; -static ZTEST_BMEM const struct device *const uart_dev = - DEVICE_DT_GET(UART_NODE); + +struct dut_data { + const struct device *dev; + const char *name; +}; + +static ZTEST_DMEM struct dut_data duts[] = { + { + .dev = DEVICE_DT_GET(UART_NODE), + .name = DT_NODE_FULL_NAME(UART_NODE), + }, + /* More instances can be added here. */ +}; + +static ZTEST_BMEM const struct device *uart_dev; +static ZTEST_BMEM const char *uart_name; static void read_abort_timeout(struct k_timer *timer); static K_TIMER_DEFINE(read_abort_timer, read_abort_timeout, NULL); -static void init_test(void) -{ - __ASSERT_NO_MSG(device_is_ready(uart_dev)); - uart_rx_disable(uart_dev); - uart_tx_abort(uart_dev); - k_sem_reset(&tx_done); - k_sem_reset(&tx_aborted); - k_sem_reset(&rx_rdy); - k_sem_reset(&rx_buf_released); - k_sem_reset(&rx_disabled); -} - #ifdef CONFIG_USERSPACE static void set_permissions(void) { k_thread_access_grant(k_current_get(), &tx_done, &tx_aborted, &rx_rdy, &rx_buf_coherency, &rx_buf_released, &rx_disabled, uart_dev, &read_abort_timer); + + for (size_t i = 0; i < ARRAY_SIZE(duts); i++) { + k_thread_access_grant(k_current_get(), duts[i].dev); + } } #endif -static void uart_async_test_init(void) +static void uart_async_test_init(int idx) { static bool initialized; + uart_dev = duts[idx].dev; + uart_name = duts[idx].name; + __ASSERT_NO_MSG(device_is_ready(uart_dev)); + TC_PRINT("UART instance:%s\n", uart_name); uart_rx_disable(uart_dev); uart_tx_abort(uart_dev); k_sem_reset(&tx_done); @@ -79,7 +89,6 @@ static void uart_async_test_init(void) #endif if (!initialized) { - init_test(); initialized = true; #ifdef CONFIG_USERSPACE set_permissions(); @@ -143,11 +152,13 @@ static void test_single_read_callback(const struct device *dev, } } -ZTEST_BMEM volatile uint32_t tx_aborted_count; +static ZTEST_BMEM volatile uint32_t tx_aborted_count; static void *single_read_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); memset(&tdata, 0, sizeof(tdata)); tdata.supply_second_buffer = true; @@ -228,7 +239,9 @@ ZTEST_USER(uart_async_single_read, test_single_read) static void *multiple_rx_enable_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); memset(&tdata, 0, sizeof(tdata)); /* Reuse the callback from the single_read test case, as this test case @@ -369,7 +382,9 @@ static void test_chained_read_callback(const struct device *dev, static void *chained_read_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); uart_callback_set(uart_dev, test_chained_read_callback, NULL); @@ -450,7 +465,9 @@ static void test_double_buffer_callback(const struct device *dev, static void *double_buffer_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); uart_callback_set(uart_dev, test_double_buffer_callback, NULL); @@ -496,6 +513,7 @@ static ZTEST_BMEM uint8_t test_read_abort_rx_buf[2][100]; static ZTEST_BMEM uint8_t test_read_abort_read_buf[100]; #endif /* NOCACHE_MEM */ static ZTEST_BMEM int test_read_abort_rx_cnt; +static ZTEST_BMEM bool test_read_abort_rx_buf_req_once; static void test_read_abort_callback(const struct device *dev, struct uart_event *evt, void *user_data) @@ -510,14 +528,12 @@ static void test_read_abort_callback(const struct device *dev, break; case UART_RX_BUF_REQUEST: { - static bool once; - - if (!once) { + if (!test_read_abort_rx_buf_req_once) { k_sem_give(&rx_buf_coherency); uart_rx_buf_rsp(dev, test_read_abort_rx_buf[1], sizeof(test_read_abort_rx_buf[1])); - once = true; + test_read_abort_rx_buf_req_once = true; } break; } @@ -553,8 +569,11 @@ static void read_abort_timeout(struct k_timer *timer) static void *read_abort_setup(void) { - uart_async_test_init(); + static int idx; + uart_async_test_init(idx++); + + test_read_abort_rx_buf_req_once = false; failed_in_isr = false; uart_callback_set(uart_dev, test_read_abort_callback, NULL); @@ -649,7 +668,9 @@ static void test_write_abort_callback(const struct device *dev, static void *write_abort_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); uart_callback_set(uart_dev, test_write_abort_callback, NULL); @@ -722,7 +743,9 @@ static void test_forever_timeout_callback(const struct device *dev, static void *forever_timeout_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); uart_callback_set(uart_dev, test_forever_timeout_callback, NULL); @@ -811,8 +834,12 @@ static void test_chained_write_callback(const struct device *dev, static void *chained_write_setup(void) { - uart_async_test_init(); + static int idx; + + uart_async_test_init(idx++); + tx_sent = 0; + chained_write_next_buf = true; uart_callback_set(uart_dev, test_chained_write_callback, NULL); return NULL; @@ -861,11 +888,11 @@ static ZTEST_BMEM uint8_t long_tx_buf[1000]; #endif /* NOCACHE_MEM */ static ZTEST_BMEM volatile uint8_t evt_num; static ZTEST_BMEM size_t long_received[2]; +static ZTEST_BMEM uint8_t *long_next_buffer; static void test_long_buffers_callback(const struct device *dev, struct uart_event *evt, void *user_data) { - static uint8_t *next_buffer = long_rx_buf2; switch (evt->type) { case UART_TX_DONE: @@ -887,8 +914,8 @@ static void test_long_buffers_callback(const struct device *dev, k_sem_give(&rx_disabled); break; case UART_RX_BUF_REQUEST: - uart_rx_buf_rsp(dev, next_buffer, 1024); - next_buffer = (next_buffer == long_rx_buf2) ? long_rx_buf : long_rx_buf2; + uart_rx_buf_rsp(dev, long_next_buffer, 1024); + long_next_buffer = (long_next_buffer == long_rx_buf2) ? long_rx_buf : long_rx_buf2; break; default: break; @@ -897,8 +924,12 @@ static void test_long_buffers_callback(const struct device *dev, static void *long_buffers_setup(void) { - uart_async_test_init(); + static int idx; + uart_async_test_init(idx++); + + evt_num = 0; + long_next_buffer = long_rx_buf2; uart_callback_set(uart_dev, test_long_buffers_callback, NULL); return NULL; @@ -975,3 +1006,12 @@ ZTEST_SUITE(uart_async_write_abort, NULL, write_abort_setup, ZTEST_SUITE(uart_async_timeout, NULL, forever_timeout_setup, NULL, NULL, NULL); + +void test_main(void) +{ + /* Run all suites for each dut UART. Setup function for each suite is picking + * next UART from the array. + */ + ztest_run_all(NULL, false, ARRAY_SIZE(duts), 1); + ztest_verify_all_test_suites_ran(); +} From 74be5651a150649f0367a578931d1869321362e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 19 Jul 2024 14:52:43 +0200 Subject: [PATCH 14/21] tests: drivers: uart: async_api: Fix nrf54h20dk cpurad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix target which was failing due to lack of memory for DMA buffers. Signed-off-by: Krzysztof Chruściński --- tests/drivers/uart/uart_async_api/Kconfig | 4 ++ .../boards/nrf54h20dk_nrf54h20_cpurad.conf | 1 + .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 4 ++ .../uart/uart_async_api/src/test_uart_async.c | 38 +++++++++++-------- 4 files changed, 32 insertions(+), 15 deletions(-) create mode 100644 tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.conf diff --git a/tests/drivers/uart/uart_async_api/Kconfig b/tests/drivers/uart/uart_async_api/Kconfig index 6883eb23509676..b7ec969a5f534f 100644 --- a/tests/drivers/uart/uart_async_api/Kconfig +++ b/tests/drivers/uart/uart_async_api/Kconfig @@ -19,3 +19,7 @@ config DT_DEFINED_NOCACHE_NAME endif # DT_DEFINED_NOCACHE endif # DCACHE + +config TEST_LONG_BUFFER_SIZE + int "Long buffer size" + default 1024 diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.conf b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.conf new file mode 100644 index 00000000000000..3bd41c07964dac --- /dev/null +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.conf @@ -0,0 +1 @@ +CONFIG_TEST_LONG_BUFFER_SIZE=64 diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay index beca628a2f0805..87da56b19e70db 100644 --- a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -2,6 +2,10 @@ #include "nrf54h20dk_nrf54h20_common.dtsi" +&cpurad_dma_region { + reg = <0xe80 DT_SIZE_K(4)>; +}; + &dut { memory-regions = <&cpurad_dma_region>; }; diff --git a/tests/drivers/uart/uart_async_api/src/test_uart_async.c b/tests/drivers/uart/uart_async_api/src/test_uart_async.c index cbd7f7ae120a10..771b7a65a9c25a 100644 --- a/tests/drivers/uart/uart_async_api/src/test_uart_async.c +++ b/tests/drivers/uart/uart_async_api/src/test_uart_async.c @@ -877,14 +877,17 @@ ZTEST_USER(uart_async_chain_write, test_chained_write) "RX_DISABLED timeout"); } +#define RX_LONG_BUFFER CONFIG_TEST_LONG_BUFFER_SIZE +#define TX_LONG_BUFFER (CONFIG_TEST_LONG_BUFFER_SIZE - 8) + #if NOCACHE_MEM -static __aligned(32) uint8_t long_rx_buf[1024] __used __NOCACHE; -static __aligned(32) uint8_t long_rx_buf2[1024] __used __NOCACHE; -static __aligned(32) uint8_t long_tx_buf[1000] __used __NOCACHE; +static __aligned(32) uint8_t long_rx_buf[RX_LONG_BUFFER] __used __NOCACHE; +static __aligned(32) uint8_t long_rx_buf2[RX_LONG_BUFFER] __used __NOCACHE; +static __aligned(32) uint8_t long_tx_buf[TX_LONG_BUFFER] __used __NOCACHE; #else -static ZTEST_BMEM uint8_t long_rx_buf[1024]; -static ZTEST_BMEM uint8_t long_rx_buf2[1024]; -static ZTEST_BMEM uint8_t long_tx_buf[1000]; +static ZTEST_BMEM uint8_t long_rx_buf[RX_LONG_BUFFER]; +static ZTEST_BMEM uint8_t long_rx_buf2[RX_LONG_BUFFER]; +static ZTEST_BMEM uint8_t long_tx_buf[TX_LONG_BUFFER]; #endif /* NOCACHE_MEM */ static ZTEST_BMEM volatile uint8_t evt_num; static ZTEST_BMEM size_t long_received[2]; @@ -914,7 +917,7 @@ static void test_long_buffers_callback(const struct device *dev, k_sem_give(&rx_disabled); break; case UART_RX_BUF_REQUEST: - uart_rx_buf_rsp(dev, long_next_buffer, 1024); + uart_rx_buf_rsp(dev, long_next_buffer, RX_LONG_BUFFER); long_next_buffer = (long_next_buffer == long_rx_buf2) ? long_rx_buf : long_rx_buf2; break; default: @@ -937,16 +940,19 @@ static void *long_buffers_setup(void) ZTEST_USER(uart_async_long_buf, test_long_buffers) { + size_t tx_len1 = TX_LONG_BUFFER / 2; + size_t tx_len2 = TX_LONG_BUFFER; + memset(long_rx_buf, 0, sizeof(long_rx_buf)); memset(long_tx_buf, 1, sizeof(long_tx_buf)); uart_rx_enable(uart_dev, long_rx_buf, sizeof(long_rx_buf), 10 * USEC_PER_MSEC); - uart_tx(uart_dev, long_tx_buf, 500, 200 * USEC_PER_MSEC); + uart_tx(uart_dev, long_tx_buf, tx_len1, 200 * USEC_PER_MSEC); zassert_equal(k_sem_take(&tx_done, K_MSEC(200)), 0, "TX_DONE timeout"); zassert_equal(k_sem_take(&rx_rdy, K_MSEC(200)), 0, "RX_RDY timeout"); - zassert_equal(long_received[0], 500, "Wrong number of bytes received."); - zassert_equal(memcmp(long_tx_buf, long_rx_buf, 500), + zassert_equal(long_received[0], tx_len1, "Wrong number of bytes received."); + zassert_equal(memcmp(long_tx_buf, long_rx_buf, tx_len1), 0, "Buffers not equal"); k_msleep(10); @@ -954,19 +960,21 @@ ZTEST_USER(uart_async_long_buf, test_long_buffers) bool release_on_timeout = k_sem_take(&rx_buf_released, K_NO_WAIT) == 0; evt_num = 0; - uart_tx(uart_dev, long_tx_buf, 1000, 200 * USEC_PER_MSEC); + uart_tx(uart_dev, long_tx_buf, tx_len2, 200 * USEC_PER_MSEC); zassert_equal(k_sem_take(&tx_done, K_MSEC(200)), 0, "TX_DONE timeout"); zassert_equal(k_sem_take(&rx_rdy, K_MSEC(200)), 0, "RX_RDY timeout"); if (release_on_timeout) { - zassert_equal(long_received[0], 1000, "Wrong number of bytes received."); + zassert_equal(long_received[0], tx_len2, "Wrong number of bytes received."); zassert_equal(memcmp(long_tx_buf, long_rx_buf2, long_received[0]), 0, "Buffers not equal"); } else { zassert_equal(k_sem_take(&rx_rdy, K_MSEC(200)), 0, "RX_RDY timeout"); - zassert_equal(long_received[0], 524, "Wrong number of bytes received."); - zassert_equal(long_received[1], 476, "Wrong number of bytes received."); - zassert_equal(memcmp(long_tx_buf, long_rx_buf + 500, long_received[0]), 0, + zassert_equal(long_received[0], RX_LONG_BUFFER - tx_len1, + "Wrong number of bytes received."); + zassert_equal(long_received[1], tx_len2 - (RX_LONG_BUFFER - tx_len1), + "Wrong number of bytes received."); + zassert_equal(memcmp(long_tx_buf, long_rx_buf + tx_len1, long_received[0]), 0, "Buffers not equal"); zassert_equal(memcmp(long_tx_buf, long_rx_buf2, long_received[1]), 0, "Buffers not equal"); From 1ae57f71741da996bbd58f69d7c2baf64f9de199 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 18 Jul 2024 14:20:33 +0200 Subject: [PATCH 15/21] tests: drivers: uart: async_api: Add uart120 instance to nrf54h20dk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add second instance to be tested on nrf54h20dk. uart120 is a fast UARTE which works on fixed pin locations. It is not available for cpuppr core. Signed-off-by: Krzysztof Chruściński --- .../boards/nrf54h20dk_nrf54h20_common.dtsi | 32 +++++++++++++++++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 4 +++ .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 4 +++ .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 8 +++++ .../uart/uart_async_api/src/test_uart_async.c | 9 ++++-- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_common.dtsi index 9aa338deaad71a..ecfbef2d9d6370 100644 --- a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -3,8 +3,11 @@ &pinctrl { uart137_default_alt: uart137_default_alt { group1 { - psels = , - ; + psels = ; + }; + group2 { + psels = ; + bias-pull-up; }; }; @@ -15,6 +18,23 @@ low-power-enable; }; }; + uart120_default_alt: uart120_default_alt { + group1 { + psels = ; + }; + group2 { + psels = ; + bias-pull-up; + }; + }; + + uart120_sleep_alt: uart120_sleep_alt { + group1 { + psels = , + ; + low-power-enable; + }; + }; }; dut: &uart137 { @@ -24,3 +44,11 @@ dut: &uart137 { pinctrl-names = "default", "sleep"; current-speed = <115200>; }; + +dut2: &uart120 { + status = "okay"; + pinctrl-0 = <&uart120_default_alt>; + pinctrl-1 = <&uart120_sleep_alt>; + pinctrl-names = "default", "sleep"; + current-speed = <115200>; +}; diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index be5897fd0516fb..b7e8aad3364736 100644 --- a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -5,3 +5,7 @@ &dut { memory-regions = <&cpuapp_dma_region>; }; + +&dut2 { + memory-regions = <&dma_fast_region>; +}; diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay index f65b4dd3b0ba0f..8dd36819dff572 100644 --- a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -1,3 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 */ #include "nrf54h20dk_nrf54h20_common.dtsi" + +dut2: &uart120 { + status = "disabled"; +}; diff --git a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay index 87da56b19e70db..8881f7abc47fa8 100644 --- a/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay +++ b/tests/drivers/uart/uart_async_api/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -9,3 +9,11 @@ &dut { memory-regions = <&cpurad_dma_region>; }; + +&dma_fast_region { + status = "okay"; +}; + +&dut2 { + memory-regions = <&dma_fast_region>; +}; diff --git a/tests/drivers/uart/uart_async_api/src/test_uart_async.c b/tests/drivers/uart/uart_async_api/src/test_uart_async.c index 771b7a65a9c25a..340ebdf93d6bee 100644 --- a/tests/drivers/uart/uart_async_api/src/test_uart_async.c +++ b/tests/drivers/uart/uart_async_api/src/test_uart_async.c @@ -31,12 +31,17 @@ struct dut_data { const char *name; }; -static ZTEST_DMEM struct dut_data duts[] = { +ZTEST_DMEM struct dut_data duts[] = { { .dev = DEVICE_DT_GET(UART_NODE), .name = DT_NODE_FULL_NAME(UART_NODE), }, - /* More instances can be added here. */ +#if DT_NODE_EXISTS(DT_NODELABEL(dut2)) && DT_NODE_HAS_STATUS(DT_NODELABEL(dut2), okay) + { + .dev = DEVICE_DT_GET(DT_NODELABEL(dut2)), + .name = DT_NODE_FULL_NAME(DT_NODELABEL(dut2)), + }, +#endif }; static ZTEST_BMEM const struct device *uart_dev; From fc1aec3f8818d57c022128e85b724aebd1ecda01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Thu, 4 Jul 2024 13:27:45 +0200 Subject: [PATCH 16/21] tests: drivers: uart: mix_fifo_poll: Add nrf54h20 cpuppr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configuration for nrf54h20dk/nrf54h20/cpuppr. Signed-off-by: Krzysztof Chruściński --- .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 11 ++++ .../drivers/uart/uart_mix_fifo_poll/prj.conf | 10 +++- .../uart/uart_mix_fifo_poll/src/main.c | 29 ++++++++-- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 13 +++++ .../sysbuild/vpr_launcher/prj.conf | 1 + .../uart/uart_mix_fifo_poll/testcase.yaml | 53 +++---------------- 6 files changed, 63 insertions(+), 54 deletions(-) create mode 100644 tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay create mode 100644 tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay create mode 100644 tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/prj.conf diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay new file mode 100644 index 00000000000000..178aea353d518b --- /dev/null +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "nrf54h20dk_nrf54h20_common.dtsi" + +&timer137 { + interrupts = <467 (NRF_DEFAULT_IRQ_PRIORITY + 1)>; +}; + +&dut { + interrupts = <470 (NRF_DEFAULT_IRQ_PRIORITY + 1)>; +}; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/prj.conf b/tests/drivers/uart/uart_mix_fifo_poll/prj.conf index fa22b3a47b8988..41c7920a1f8a3b 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/prj.conf +++ b/tests/drivers/uart/uart_mix_fifo_poll/prj.conf @@ -1,11 +1,17 @@ CONFIG_SERIAL=y CONFIG_ZTEST=y -CONFIG_TEST_USERSPACE=y +CONFIG_TEST_USERSPACE=n CONFIG_ZTEST_THREAD_PRIORITY=5 CONFIG_MAIN_STACK_SIZE=2048 CONFIG_ZTEST_STACK_SIZE=2048 CONFIG_TEST_RANDOM_GENERATOR=y CONFIG_COUNTER=y -# CONFIG_NO_OPTIMIZATIONS=y +CONFIG_OUTPUT_DISASSEMBLY=y +#CONFIG_NO_OPTIMIZATIONS=y # CONFIG_LOG_MODE_MINIMAL=n +#CONFIG_LOG=y +#CONFIG_LOG_BUFFER_SIZE=16384 +#CONFIG_TEST_LOGGING_DEFAULTS=n + +CONFIG_TEST_EXTRA_STACK_SIZE=2048 diff --git a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c index eaebe8e132e543..23a5eb50613b21 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c +++ b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c @@ -15,6 +15,8 @@ #include #include #include +#include +LOG_MODULE_REGISTER(test, 0); /* RX and TX pins have to be connected together*/ #if DT_NODE_EXISTS(DT_NODELABEL(dut)) @@ -71,6 +73,7 @@ static const struct device *const uart_dev = static bool async; static bool int_driven; +static volatile bool error_found; static volatile bool async_rx_enabled; static struct k_sem async_tx_sem; @@ -84,6 +87,10 @@ static void process_byte(uint8_t b) struct rx_source *src = &source[base]; bool ok; + if (error_found) { + return; + } + b &= 0x0F; src->cnt++; @@ -94,6 +101,12 @@ static void process_byte(uint8_t b) ok = ((b - src->prev) == 1) || (!b && (src->prev == 0x0F)); + if (!ok) { + error_found = true; + LOG_ERR("Unexpected byte received:0x%02x, prev:0x%02x", + (base << 4) | b, (base << 4) | src->prev); + }; + zassert_true(ok, "Unexpected byte received:0x%02x, prev:0x%02x", (base << 4) | b, (base << 4) | src->prev); src->prev = b; @@ -224,6 +237,10 @@ static void bulk_poll_out(struct test_data *data, int wait_base, int wait_range) { for (int i = 0; i < data->max; i++) { + if (error_found) { + goto bail; + } + data->cnt++; uart_poll_out(uart_dev, data->buf[i % BUF_SIZE]); if (wait_base) { @@ -232,7 +249,7 @@ static void bulk_poll_out(struct test_data *data, int wait_base, int wait_range) k_sleep(K_USEC(wait_base + (r % wait_range))); } } - +bail: k_sem_give(&data->sem); } @@ -241,10 +258,10 @@ static void poll_out_thread(void *data, void *unused0, void *unused1) bulk_poll_out((struct test_data *)data, 200, 600); } -K_THREAD_STACK_DEFINE(high_poll_out_thread_stack, 1024); +K_THREAD_STACK_DEFINE(high_poll_out_thread_stack, 2048); static struct k_thread high_poll_out_thread; -K_THREAD_STACK_DEFINE(int_async_thread_stack, 1024); +K_THREAD_STACK_DEFINE(int_async_thread_stack, 2048); static struct k_thread int_async_thread; static void int_async_thread_func(void *p_data, void *base, void *range) @@ -255,7 +272,7 @@ static void int_async_thread_func(void *p_data, void *base, void *range) k_sem_init(&async_tx_sem, 1, 1); - while (data->cnt < data->max) { + while (!error_found && (data->cnt < data->max)) { if (async) { int err; @@ -287,7 +304,9 @@ static void poll_out_timer_handler(struct k_timer *timer) { struct test_data *data = k_timer_user_data_get(timer); - uart_poll_out(uart_dev, data->buf[data->cnt % BUF_SIZE]); + if (!error_found) { + uart_poll_out(uart_dev, data->buf[data->cnt % BUF_SIZE]); + } data->cnt++; if (data->cnt == data->max) { diff --git a/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 00000000000000..9e38ed36b13343 --- /dev/null +++ b/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +#include "../../../boards/nrf54h20dk_nrf54h20_common.dtsi" + +&dut { + status = "reserved"; + interrupt-parent = <&cpuppr_clic>; +}; + +&timer137 { + status = "reserved"; + interrupt-parent = <&cpuppr_clic>; +}; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/prj.conf b/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/prj.conf new file mode 100644 index 00000000000000..b2a4ba591044ee --- /dev/null +++ b/tests/drivers/uart/uart_mix_fifo_poll/sysbuild/vpr_launcher/prj.conf @@ -0,0 +1 @@ +# nothing here diff --git a/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml b/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml index 8be2429492e914..7be086a385208d 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml +++ b/tests/drivers/uart/uart_mix_fifo_poll/testcase.yaml @@ -22,32 +22,22 @@ tests: - CONFIG_UART_INTERRUPT_DRIVEN=n - CONFIG_UART_ASYNC_API=n - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n drivers.uart.uart_mix_poll_fifo: extra_configs: - CONFIG_UART_INTERRUPT_DRIVEN=y - - CONFIG_UART_0_INTERRUPT_DRIVEN=y - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n drivers.uart.uart_mix_poll_async_api: extra_configs: - CONFIG_UART_ASYNC_API=y - - CONFIG_UART_0_INTERRUPT_DRIVEN=n - - CONFIG_UART_0_ASYNC=y - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n drivers.uart.uart_mix_poll_async_api_const: extra_args: TEST_CONST_BUFFER=1 extra_configs: - CONFIG_UART_ASYNC_API=y - - CONFIG_UART_0_INTERRUPT_DRIVEN=n - - CONFIG_UART_0_ASYNC=y - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_0_TX_CACHE_SIZE=2 - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n tags: bsim_skip_CI # We skip a few tests to save CI time, as they give little extra coverage drivers.uart.uart_mix_poll_with_ppi: @@ -55,59 +45,28 @@ tests: - CONFIG_UART_INTERRUPT_DRIVEN=n - CONFIG_UART_ASYNC_API=n - CONFIG_UART_0_ENHANCED_POLL_OUT=y - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n tags: bsim_skip_CI - - drivers.uart.uart_mix_poll_fifo_with_ppi: - extra_configs: - - CONFIG_UART_INTERRUPT_DRIVEN=y - - CONFIG_UART_0_INTERRUPT_DRIVEN=y - - CONFIG_UART_0_ENHANCED_POLL_OUT=y - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n - tags: bsim_skip_CI - - drivers.uart.uart_mix_poll_async_api_with_ppi: - extra_configs: - - CONFIG_UART_ASYNC_API=y - - CONFIG_UART_0_INTERRUPT_DRIVEN=n - - CONFIG_UART_0_ASYNC=y - - CONFIG_UART_0_ENHANCED_POLL_OUT=y - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=n - tags: bsim_skip_CI - - drivers.uart.legacy.uart_mix_poll: - extra_configs: - - CONFIG_UART_INTERRUPT_DRIVEN=n - - CONFIG_UART_ASYNC_API=n - - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=y platform_exclude: - - nrf54l15pdk/nrf54l15/cpuapp - nrf54h20dk/nrf54h20/cpuapp - nrf54h20dk/nrf54h20/cpurad - drivers.uart.legacy.uart_mix_poll_fifo: + drivers.uart.uart_mix_poll_fifo_with_ppi: extra_configs: - CONFIG_UART_INTERRUPT_DRIVEN=y - CONFIG_UART_0_INTERRUPT_DRIVEN=y - - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=y + - CONFIG_UART_0_ENHANCED_POLL_OUT=y + tags: bsim_skip_CI platform_exclude: - - nrf54l15pdk/nrf54l15/cpuapp - nrf54h20dk/nrf54h20/cpuapp - nrf54h20dk/nrf54h20/cpurad - drivers.uart.legacy.uart_mix_poll_async_api: + drivers.uart.uart_mix_poll_async_api_with_ppi: extra_configs: - CONFIG_UART_ASYNC_API=y - CONFIG_UART_0_INTERRUPT_DRIVEN=n - CONFIG_UART_0_ASYNC=y - - CONFIG_UART_0_ENHANCED_POLL_OUT=n - - CONFIG_UART_0_NRF_HW_ASYNC=y - - CONFIG_UART_0_NRF_HW_ASYNC_TIMER=2 - - CONFIG_NRFX_TIMER2=y - - CONFIG_UART_NRFX_UARTE_LEGACY_SHIM=y + - CONFIG_UART_0_ENHANCED_POLL_OUT=y + tags: bsim_skip_CI platform_exclude: - - nrf54l15pdk/nrf54l15/cpuapp - nrf54h20dk/nrf54h20/cpuapp - nrf54h20dk/nrf54h20/cpurad From c7c225dee60f71bc1e77735e70e05e8fc325dbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 19 Jul 2024 08:19:16 +0200 Subject: [PATCH 17/21] tests: drivers: uart: mix_fifo_poll: Rework to support multi-instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework test to allow testing multiple UART instances in a single run. Signed-off-by: Krzysztof Chruściński --- .../uart/uart_mix_fifo_poll/src/main.c | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c index 23a5eb50613b21..37c3685d071cbf 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c +++ b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c @@ -42,6 +42,18 @@ struct rx_source { uint8_t prev; }; +struct dut_data { + const struct device *dev; + const char *name; +}; + +static struct dut_data duts[] = { + { + .dev = DEVICE_DT_GET(UART_NODE), + .name = DT_NODE_FULL_NAME(UART_NODE), + }, +}; + #define BUF_SIZE 16 /* Buffer used for polling. */ @@ -68,8 +80,7 @@ static struct test_data int_async_data; static const struct device *const counter_dev = DEVICE_DT_GET(COUNTER_NODE); -static const struct device *const uart_dev = - DEVICE_DT_GET(UART_NODE); +static const struct device *uart_dev; static bool async; static bool int_driven; @@ -112,8 +123,10 @@ static void process_byte(uint8_t b) src->prev = b; } +static volatile bool in_counter_isr; static void counter_top_handler(const struct device *dev, void *user_data) { + in_counter_isr = true; static bool enable = true; static uint8_t async_rx_buf[4]; @@ -139,9 +152,10 @@ static void counter_top_handler(const struct device *dev, void *user_data) process_byte(c); } } + in_counter_isr = false; } -static void init_test(void) +static void init_test(int idx) { int err; struct counter_top_cfg top_cfg = { @@ -150,6 +164,12 @@ static void init_test(void) .flags = 0 }; + memset(source, 0, sizeof(source)); + error_found = false; + async_rx_enabled = false; + uart_dev = duts[idx].dev; + TC_PRINT("UART instance:%s\n", duts[idx].name); + zassert_true(device_is_ready(uart_dev), "uart device is not ready"); if (uart_callback_set(uart_dev, async_callback, NULL) == 0) { @@ -388,10 +408,21 @@ ZTEST(uart_mix_fifo_poll, test_mixed_uart_access) void *uart_mix_setup(void) { - init_test(); + static int idx; + + init_test(idx++); return NULL; } ZTEST_SUITE(uart_mix_fifo_poll, NULL, uart_mix_setup, NULL, NULL, NULL); + +void test_main(void) +{ + /* Run all suites for each dut UART. Setup function for each suite is picking + * next UART from the array. + */ + ztest_run_all(NULL, false, ARRAY_SIZE(duts), 1); + ztest_verify_all_test_suites_ran(); +} From 50a473ca4bfddaadbac50973f04481cb10fb8b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 19 Jul 2024 08:43:16 +0200 Subject: [PATCH 18/21] tests: drivers: uart: mix_fifo_poll: Add UARTE120 to nrf54h20dk test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configuration for UARTE120 fast instance. Use multi-instance capability of the test to run same test on UARTE120 and slow UARTE peripheral. Signed-off-by: Krzysztof Chruściński --- .../boards/nrf54h20dk_nrf54h20_common.dtsi | 31 ++++++++++++++++++- .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 4 +++ .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 5 +++ .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 8 +++++ .../uart/uart_mix_fifo_poll/src/main.c | 6 ++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_common.dtsi index c102d617f900d1..7a76d46f3f07b3 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -19,6 +19,27 @@ low-power-enable; }; }; + uart120_default_alt: uart120_default_alt { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart120_sleep_alt: uart120_sleep_alt { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; }; dut: &uart137 { @@ -30,7 +51,15 @@ dut: &uart137 { hw-flow-control; }; -/* Use timer137 as only this one can generate interrupts on cpusys. */ +dut2: &uart120 { + status = "okay"; + pinctrl-0 = <&uart120_default_alt>; + pinctrl-1 = <&uart120_sleep_alt>; + pinctrl-names = "default", "sleep"; + current-speed = <115200>; + hw-flow-control; +}; + counter_dev: &timer137 { status = "okay"; }; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index c6ff4eb77af378..c89d2c2ba46224 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -6,6 +6,10 @@ memory-regions = <&cpuapp_dma_region>; }; +&dut2 { + memory-regions = <&dma_fast_region>; +}; + &grtc { interrupts = <109 2>; }; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay index 178aea353d518b..d846a5debd780c 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -9,3 +9,8 @@ &dut { interrupts = <470 (NRF_DEFAULT_IRQ_PRIORITY + 1)>; }; + +/* PPR cannot use that instance. */ +&uart120 { + status = "disabled" +}; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpurad.overlay index 2bde29c9fc51e3..7494428da3f3fd 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpurad.overlay +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -11,6 +11,14 @@ memory-regions = <&cpurad_dma_region>; }; +&dma_fast_region { + status = "okay"; +}; + +&dut2 { + memory-regions = <&dma_fast_region>; +}; + &grtc { interrupts = <109 2>; }; diff --git a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c index 37c3685d071cbf..e3935372dce4a4 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/src/main.c +++ b/tests/drivers/uart/uart_mix_fifo_poll/src/main.c @@ -52,6 +52,12 @@ static struct dut_data duts[] = { .dev = DEVICE_DT_GET(UART_NODE), .name = DT_NODE_FULL_NAME(UART_NODE), }, +#if DT_NODE_EXISTS(DT_NODELABEL(dut2)) + { + .dev = DEVICE_DT_GET(DT_NODELABEL(dut2)), + .name = DT_NODE_FULL_NAME(DT_NODELABEL(dut2)), + }, +#endif }; #define BUF_SIZE 16 From 4ce5e1ac05cb0d85422d22628d0178ee7e5258f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 19 Jul 2024 11:59:00 +0200 Subject: [PATCH 19/21] tests: drivers: uart: async_dual: Rework for multi-instance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rework test to be able to support running on multiple set of instance(s). Signed-off-by: Krzysztof Chruściński --- tests/drivers/uart/uart_async_dual/src/main.c | 53 ++++++++++++++++++- .../uart/uart_async_dual/testcase.yaml | 1 + 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/tests/drivers/uart/uart_async_dual/src/main.c b/tests/drivers/uart/uart_async_dual/src/main.c index 764c5fd7e61658..5a9483c83c7551 100644 --- a/tests/drivers/uart/uart_async_dual/src/main.c +++ b/tests/drivers/uart/uart_async_dual/src/main.c @@ -29,8 +29,38 @@ LOG_MODULE_REGISTER(test); #define MAX_PACKET_LEN 128 -static const struct device *const rx_dev = DEVICE_DT_GET(DUT_NODE); -static const struct device *const tx_dev = DEVICE_DT_GET(DUT_AUX_NODE); +struct dut_data { + const struct device *dev; + const struct device *dev_aux; + const char *name; + const char *name_aux; +}; + +ZTEST_DMEM struct dut_data duts[] = { + { + .dev = DEVICE_DT_GET(DUT_NODE), + .dev_aux = DT_SAME_NODE(DUT_NODE, DUT_AUX_NODE) ? + NULL : DEVICE_DT_GET(DUT_AUX_NODE), + .name = DT_NODE_FULL_NAME(DUT_NODE), + .name_aux = DT_SAME_NODE(DUT_NODE, DUT_AUX_NODE) ? + NULL : DT_NODE_FULL_NAME(DUT_AUX_NODE), + }, +#if 0 +#if DT_NODE_EXISTS(DT_NODELABEL(dut2)) + { + .dev = DEVICE_DT_GET(DT_NODELABEL(dut2)), + .name = DT_NODE_FULL_NAME(DT_NODELABEL(dut2)), +#if DT_NODE_EXISTS(DT_NODELABEL(dut_aux2)) + .dev_aux = DEVICE_DT_GET(DT_NODELABEL(dut_aux2)), + .name_aux = DT_NODE_FULL_NAME(DT_NODELABEL(dut_aux2)), +#endif + }, +#endif +#endif +}; + +static const struct device *rx_dev; +static const struct device *tx_dev; enum test_tx_mode { TX_BULK, @@ -674,6 +704,19 @@ ZTEST(uart_async_dual, test_hci_like_1M) static void *setup(void) { + static int idx; + + rx_dev = duts[idx].dev; + if (duts[idx].dev_aux == NULL) { + TC_PRINT("Single UART test on instance:%s\n", duts[idx].name); + tx_dev = rx_dev; + } else { + TC_PRINT("Dual UART test on instances:%s and %s\n", + duts[idx].name, duts[idx].name_aux); + tx_dev = duts[idx].dev_aux; + } + idx++; + zassert_true(device_is_ready(rx_dev)); zassert_true(device_is_ready(tx_dev)); @@ -681,3 +724,9 @@ static void *setup(void) } ZTEST_SUITE(uart_async_dual, NULL, setup, NULL, NULL, NULL); + +void test_main(void) +{ + ztest_run_all(NULL, false, ARRAY_SIZE(duts), 1); + ztest_verify_all_test_suites_ran(); +} diff --git a/tests/drivers/uart/uart_async_dual/testcase.yaml b/tests/drivers/uart/uart_async_dual/testcase.yaml index 76870d4e2c394c..507899a9e2e8f5 100644 --- a/tests/drivers/uart/uart_async_dual/testcase.yaml +++ b/tests/drivers/uart/uart_async_dual/testcase.yaml @@ -2,6 +2,7 @@ common: tags: - drivers - uart + timeout: 120 tests: drivers.uart.async_dual: harness: ztest From f489baa6cb10c80fabcfed5f2ab7fcaa3e2e88cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Fri, 19 Jul 2024 14:30:24 +0200 Subject: [PATCH 20/21] tests: drivers: uart: async_dual: Add testing of UARTE120 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use multi-instance testing and add UARTE120 configuration to nrf54h20 overlays. Signed-off-by: Krzysztof Chruściński --- .../boards/nrf54h20dk_nrf54h20_common.dtsi | 31 +++++++++++++++++++ .../boards/nrf54h20dk_nrf54h20_cpuapp.overlay | 4 +++ .../boards/nrf54h20dk_nrf54h20_cpuppr.overlay | 4 +++ .../boards/nrf54h20dk_nrf54h20_cpurad.overlay | 8 +++++ tests/drivers/uart/uart_async_dual/src/main.c | 6 ++-- 5 files changed, 49 insertions(+), 4 deletions(-) diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi index 36d09322a544d7..f4b9df95aa2bea 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -36,6 +36,28 @@ low-power-enable; }; }; + + uart120_default_alt: uart120_default_alt { + group1 { + psels = , + ; + }; + group2 { + psels = , + ; + bias-pull-up; + }; + }; + + uart120_sleep_alt: uart120_sleep_alt { + group1 { + psels = , + , + , + ; + low-power-enable; + }; + }; }; dut: &uart134 { @@ -56,6 +78,15 @@ dut_aux: &uart137 { hw-flow-control; }; +dut2: &uart120 { + status = "okay"; + pinctrl-0 = <&uart120_default_alt>; + pinctrl-1 = <&uart120_sleep_alt>; + pinctrl-names = "default", "sleep"; + current-speed = <115200>; + hw-flow-control; +}; + &timer137 { status = "okay"; interrupts = <467 0>; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay index 3ef13b2ae0dcb2..7c8190af5eb6d6 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -9,3 +9,7 @@ &dut_aux { memory-regions = <&cpuapp_dma_region>; }; + +&dut2 { + memory-regions = <&dma_fast_region>; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay index 5217f2dd7e9259..1121082094b756 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpuppr.overlay @@ -5,3 +5,7 @@ &timer137 { interrupts = <467 (NRF_DEFAULT_IRQ_PRIORITY + 1)>; }; + +&dut2 { + status = "disabled"; +}; diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay index abc5944565f39c..b177bf8d102ed1 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_cpurad.overlay @@ -15,6 +15,14 @@ memory-regions = <&cpurad_dma_region>; }; +&dma_fast_region { + status = "okay"; +}; + +&dut2 { + memory-regions = <&dma_fast_region>; +}; + &timer131 { status = "okay"; prescaler = <0>; diff --git a/tests/drivers/uart/uart_async_dual/src/main.c b/tests/drivers/uart/uart_async_dual/src/main.c index 5a9483c83c7551..7f82e2807dfd80 100644 --- a/tests/drivers/uart/uart_async_dual/src/main.c +++ b/tests/drivers/uart/uart_async_dual/src/main.c @@ -45,18 +45,16 @@ ZTEST_DMEM struct dut_data duts[] = { .name_aux = DT_SAME_NODE(DUT_NODE, DUT_AUX_NODE) ? NULL : DT_NODE_FULL_NAME(DUT_AUX_NODE), }, -#if 0 -#if DT_NODE_EXISTS(DT_NODELABEL(dut2)) +#if DT_NODE_EXISTS(DT_NODELABEL(dut2)) && DT_NODE_HAS_STATUS(DT_NODELABEL(dut2), okay) { .dev = DEVICE_DT_GET(DT_NODELABEL(dut2)), .name = DT_NODE_FULL_NAME(DT_NODELABEL(dut2)), -#if DT_NODE_EXISTS(DT_NODELABEL(dut_aux2)) +#if DT_NODE_EXISTS(DT_NODELABEL(dut_aux2)) && DT_NODE_HAS_STATUS(DT_NODELABEL(dut_aux2), okay) .dev_aux = DEVICE_DT_GET(DT_NODELABEL(dut_aux2)), .name_aux = DT_NODE_FULL_NAME(DT_NODELABEL(dut_aux2)), #endif }, #endif -#endif }; static const struct device *rx_dev; From 522bc7879609ef05627968bf30a396ed61e4fb2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Tue, 17 Sep 2024 11:43:35 +0200 Subject: [PATCH 21/21] tests: drivers: uart: async_dual: Add test cases with runtime PM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add configurations in which runtime PM is enabled. There are 2 configurations: - Test does not call PM API for getting and putting the device. In that configuration UART device shall be suspended whenever not active. - Test is calling PM API which should keep device active as long as requested (e.g. during packet reception which consists of multiple chunks). Signed-off-by: Krzysztof Chruściński --- tests/drivers/uart/uart_async_dual/Kconfig | 5 ++ .../uart_async_dual/boards/nrf52_bsim.overlay | 1 + .../boards/nrf54h20dk_nrf54h20_common.dtsi | 3 + .../nrf54l15pdk_nrf54l15_cpuapp.overlay | 2 + tests/drivers/uart/uart_async_dual/src/main.c | 83 +++++++++++++++++++ .../uart/uart_async_dual/testcase.yaml | 29 +++++++ 6 files changed, 123 insertions(+) diff --git a/tests/drivers/uart/uart_async_dual/Kconfig b/tests/drivers/uart/uart_async_dual/Kconfig index d1c5ad6134b2aa..6e80ec3a795787 100644 --- a/tests/drivers/uart/uart_async_dual/Kconfig +++ b/tests/drivers/uart/uart_async_dual/Kconfig @@ -11,5 +11,10 @@ config UART_ASYNC_DUAL_TEST_TIMEOUT For how many loops will the stress test run. The higher this number the longer the test and therefore the higher likelihood an unlikely race/event will be triggered. +config PM_RUNTIME_IN_TEST + bool "Use runtime PM in the test" + select PM_DEVICE + select PM_DEVICE_RUNTIME + # Include Zephyr's Kconfig source "Kconfig" diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay index afd9264d190104..ddae6f682f9764 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay +++ b/tests/drivers/uart/uart_async_dual/boards/nrf52_bsim.overlay @@ -28,6 +28,7 @@ dut: &uart0 { pinctrl-1 = <&uart0_sleep>; pinctrl-names = "default", "sleep"; hw-flow-control; + zephyr,pm-device-runtime-auto; }; &timer0 { diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi index f4b9df95aa2bea..28c3879984aa61 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54h20dk_nrf54h20_common.dtsi @@ -67,6 +67,7 @@ dut: &uart134 { pinctrl-1 = <&uart134_alt_sleep>; pinctrl-names = "default", "sleep"; hw-flow-control; + zephyr,pm-device-runtime-auto; }; dut_aux: &uart137 { @@ -76,6 +77,7 @@ dut_aux: &uart137 { pinctrl-1 = <&uart137_alt_sleep>; pinctrl-names = "default", "sleep"; hw-flow-control; + zephyr,pm-device-runtime-auto; }; dut2: &uart120 { @@ -85,6 +87,7 @@ dut2: &uart120 { pinctrl-names = "default", "sleep"; current-speed = <115200>; hw-flow-control; + zephyr,pm-device-runtime-auto; }; &timer137 { diff --git a/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay b/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay index cd06494115318b..fe79690cbbadc2 100644 --- a/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay +++ b/tests/drivers/uart/uart_async_dual/boards/nrf54l15pdk_nrf54l15_cpuapp.overlay @@ -45,6 +45,7 @@ dut: &uart21 { pinctrl-1 = <&uart21_sleep>; pinctrl-names = "default", "sleep"; hw-flow-control; + zephyr,pm-device-runtime-auto; }; dut_aux: &uart22 { @@ -54,6 +55,7 @@ dut_aux: &uart22 { pinctrl-1 = <&uart22_sleep>; pinctrl-names = "default", "sleep"; hw-flow-control; + zephyr,pm-device-runtime-auto; }; &timer20 { diff --git a/tests/drivers/uart/uart_async_dual/src/main.c b/tests/drivers/uart/uart_async_dual/src/main.c index 7f82e2807dfd80..02f148eef6770e 100644 --- a/tests/drivers/uart/uart_async_dual/src/main.c +++ b/tests/drivers/uart/uart_async_dual/src/main.c @@ -5,6 +5,8 @@ */ #include +#include +#include #include #include #include @@ -57,6 +59,40 @@ ZTEST_DMEM struct dut_data duts[] = { #endif }; +static void pm_check(const struct device *dev, const struct device *second_dev, bool exp_on, + int line) +{ + if (!IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) { + return; + } + + if (dev == second_dev) { + return; + } + + enum pm_device_state state; + int err; + int cnt; + + cnt = pm_device_runtime_usage(dev); + err = pm_device_state_get(dev, &state); + zassert_equal(err, 0); + + if (exp_on) { + zassert_not_equal(cnt, 0, "Wrong PM cnt:%d, line:%d", cnt, line); + zassert_equal(state, PM_DEVICE_STATE_ACTIVE, + "Wrong PM state %s, line:%d", pm_device_state_str(state), line); + return; + } + + /* Expect device to be off. */ + zassert_equal(cnt, 0, "Wrong PM count:%d, line:%d", cnt, line); + zassert_equal(state, PM_DEVICE_STATE_SUSPENDED, + "Wrong PM state %s, line:%d", pm_device_state_str(state), line); +} + +#define PM_CHECK(dev, second_dev, exp_on) pm_check(dev, second_dev, exp_on, __LINE__) + static const struct device *rx_dev; static const struct device *tx_dev; @@ -452,11 +488,20 @@ static void hci_like_callback(const struct device *dev, struct uart_event *evt, { switch (evt->type) { case UART_TX_DONE: + zassert_true(dev == tx_dev); + if (IS_ENABLED(CONFIG_PM_RUNTIME_IN_TEST)) { + PM_CHECK(tx_dev, rx_dev, true); + pm_device_runtime_put(tx_dev); + } + PM_CHECK(tx_dev, rx_dev, false); k_sem_give(&tx_data.sem); break; case UART_TX_ABORTED: zassert_true(dev == tx_dev); + if (IS_ENABLED(CONFIG_PM_RUNTIME_IN_TEST)) { + pm_device_runtime_put(tx_dev); + } zassert_false(tx_data.cont, "Unexpected TX abort, receiver not reading data on time"); break; @@ -568,6 +613,10 @@ static void hci_like_tx(struct test_tx_data *tx_data) uint8_t len = buf[4] + 5; int err; + if (IS_ENABLED(CONFIG_PM_RUNTIME_IN_TEST)) { + pm_device_runtime_get(tx_dev); + } + err = uart_tx(tx_dev, buf, len, TX_TIMEOUT); zassert_equal(err, 0, "Unexpected err:%d", err); } @@ -595,25 +644,57 @@ static void hci_like_rx(void) uint8_t last_hdr = 0xff; uint8_t len; bool cont; + bool explicit_pm = IS_ENABLED(CONFIG_PM_RUNTIME_IN_TEST); while (1) { + if (explicit_pm) { + pm_device_runtime_get(rx_dev); + } + cont = rx(rx_data.buf, 1); if (!cont) { + if (explicit_pm) { + pm_device_runtime_put(rx_dev); + } break; } check_pre_hdr(rx_data.buf, last_hdr); last_hdr = rx_data.buf[0]; + /* If explicitly requested device to stay on in should be on. + * If application did not touch PM, device should be off. + */ + PM_CHECK(rx_dev, tx_dev, explicit_pm); + cont = rx(rx_data.buf, 4); if (!cont) { + if (explicit_pm) { + pm_device_runtime_put(rx_dev); + } break; } len = get_len(rx_data.buf); + /* If explicitly requested device to stay on in should be on. + * If application did not touch PM, device should be off. + */ + PM_CHECK(rx_dev, tx_dev, explicit_pm); + cont = rx(rx_data.buf, len); if (!cont) { + if (explicit_pm) { + pm_device_runtime_put(rx_dev); + } break; } + + if (explicit_pm) { + pm_device_runtime_put(rx_dev); + } + + /* Device shall be released and off. */ + PM_CHECK(rx_dev, tx_dev, false); + check_payload(rx_data.buf, len); } } @@ -677,6 +758,8 @@ static void hci_like_test(uint32_t baudrate) /* Flush data. */ (void)uart_tx_abort(tx_dev); k_msleep(10); + PM_CHECK(tx_dev, rx_dev, false); + (void)uart_rx_enable(rx_dev, rx_data.buf, sizeof(rx_data.buf), RX_TIMEOUT); k_msleep(1); (void)uart_rx_disable(rx_dev); diff --git a/tests/drivers/uart/uart_async_dual/testcase.yaml b/tests/drivers/uart/uart_async_dual/testcase.yaml index 507899a9e2e8f5..b191959d26e334 100644 --- a/tests/drivers/uart/uart_async_dual/testcase.yaml +++ b/tests/drivers/uart/uart_async_dual/testcase.yaml @@ -30,3 +30,32 @@ tests: - nrf52_bsim extra_configs: - CONFIG_TEST_BUSY_SIM=y + drivers.uart.async_dual.pm_runtime: + harness: ztest + harness_config: + fixture: uart_loopback + depends_on: gpio + platform_allow: + - nrf54l15pdk/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + - nrf54h20dk/nrf54h20/cpuppr + - nrf9160dk/nrf9160 + - nrf52_bsim + extra_configs: + - CONFIG_PM_DEVICE_RUNTIME=y + - CONFIG_PM_DEVICE=y + drivers.uart.async_dual.pm_runtime.explicit: + harness: ztest + harness_config: + fixture: uart_loopback + depends_on: gpio + platform_allow: + - nrf54l15pdk/nrf54l15/cpuapp + - nrf54h20dk/nrf54h20/cpuapp + - nrf54h20dk/nrf54h20/cpurad + - nrf54h20dk/nrf54h20/cpuppr + - nrf9160dk/nrf9160 + - nrf52_bsim + extra_configs: + - CONFIG_PM_RUNTIME_IN_TEST=y