drivers: serial: nrfx_uarte: Add support for non ISR PM mode
When fast UARTE instance is used (e.g. UARTE120 in nrf54h20), PM actions are not ISR safe because they include communication over IPC so they can only be called from the thread context. Extend driver to support both PM modes. When non ISR mode is used then uart_rx_enable() and uart_tx() will return error if they are called from ISR and resume operation would need to be called because device is suspended. On completion, driver is calling pm_device_runtime_put_async which can be called from the ISR context. Additionally, suspending in the TXSTOPPED and RXTO events has been moved after user callback. It allows to support the case where uart_rx_enable() or uart_tx() are called from that callback context. Since suspending is called after returning from the callback it will not trigger suspend action because API called in the callback context will increment the usage counter (when pm_device_runtime_get() is called). Signed-off-by: Krzysztof Chruściński <krzysztof.chruscinski@nordicsemi.no>
This commit is contained in:
parent
4488ed1a20
commit
c8d50e4ef1
1 changed files with 89 additions and 12 deletions
|
@ -110,6 +110,19 @@ LOG_MODULE_REGISTER(uart_nrfx_uarte, CONFIG_UART_LOG_LEVEL);
|
|||
#define UARTE_ANY_LOW_POWER 1
|
||||
#endif
|
||||
|
||||
BUILD_ASSERT(NRF_GPD_FAST_ACTIVE1 == 0);
|
||||
/* Macro must resolve to literal 0 or 1 */
|
||||
#define INSTANCE_IS_FAST(unused, prefix, idx, _) \
|
||||
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(UARTE(idx)), \
|
||||
(COND_CODE_1(UTIL_AND(IS_ENABLED(CONFIG_SOC_NRF54H20_GPD), \
|
||||
DT_NODE_HAS_PROP(UARTE(idx), power_domains)), \
|
||||
(COND_CODE_0(DT_PHA(UARTE(idx), power_domains, id), (1), (0))),\
|
||||
(0))), (0))
|
||||
|
||||
#if UARTE_FOR_EACH_INSTANCE(INSTANCE_IS_FAST, (||), (0))
|
||||
#define UARTE_ANY_FAST 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.
|
||||
|
@ -269,6 +282,21 @@ struct uarte_nrfx_data {
|
|||
(IS_ENABLED(UARTE_ANY_LOW_POWER) && \
|
||||
!IS_ENABLED(CONFIG_PM_DEVICE) && \
|
||||
(_config->flags & UARTE_CFG_FLAG_LOW_POWER))
|
||||
|
||||
/** @brief Check if device has PM that works in ISR safe mode.
|
||||
*
|
||||
* Only fast UARTE instance does not work in that mode so check PM configuration
|
||||
* flags only if there is any fast instance present.
|
||||
*
|
||||
* @retval true if device PM is ISR safe.
|
||||
* @retval false if device PM is not ISR safe.
|
||||
*/
|
||||
#define IS_PM_ISR_SAFE(dev) \
|
||||
(!IS_ENABLED(UARTE_ANY_FAST) ||\
|
||||
COND_CODE_1(CONFIG_PM_DEVICE,\
|
||||
((dev->pm_base->flags & BIT(PM_DEVICE_FLAG_ISR_SAFE))), \
|
||||
(0)))
|
||||
|
||||
/**
|
||||
* @brief Structure for UARTE configuration.
|
||||
*/
|
||||
|
@ -873,6 +901,20 @@ static int uarte_nrfx_tx(const struct device *dev, const uint8_t *buf,
|
|||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
|
||||
/* If instance does not support PM from ISR device shall
|
||||
* already be turned on.
|
||||
*/
|
||||
enum pm_device_state state;
|
||||
int err;
|
||||
|
||||
err = pm_device_state_get(dev, &state);
|
||||
(void)err;
|
||||
__ASSERT_NO_MSG(err == 0);
|
||||
if (state != PM_DEVICE_STATE_ACTIVE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
pm_device_runtime_get(dev);
|
||||
}
|
||||
|
||||
|
@ -941,6 +983,10 @@ static void notify_rx_disable(const struct device *dev)
|
|||
};
|
||||
|
||||
user_callback(dev, (struct uart_event *)&evt);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
pm_device_runtime_put_async(dev, K_NO_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef UARTE_HAS_FRAME_TIMEOUT
|
||||
|
@ -1015,6 +1061,24 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf,
|
|||
async_rx->next_buf = NULL;
|
||||
async_rx->next_buf_len = 0;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
|
||||
/* If instance does not support PM from ISR device shall
|
||||
* already be turned on.
|
||||
*/
|
||||
enum pm_device_state state;
|
||||
int err;
|
||||
|
||||
err = pm_device_state_get(dev, &state);
|
||||
(void)err;
|
||||
__ASSERT_NO_MSG(err == 0);
|
||||
if (state != PM_DEVICE_STATE_ACTIVE) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
pm_device_runtime_get(dev);
|
||||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME) || LOW_POWER_ENABLED(cfg)) {
|
||||
if (async_rx->flush_cnt) {
|
||||
int cpy_len = MIN(len, async_rx->flush_cnt);
|
||||
|
@ -1068,9 +1132,7 @@ static int uarte_nrfx_rx_enable(const struct device *dev, uint8_t *buf,
|
|||
|
||||
async_rx->enabled = true;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
pm_device_runtime_get(dev);
|
||||
} else if (LOW_POWER_ENABLED(cfg)) {
|
||||
if (LOW_POWER_ENABLED(cfg)) {
|
||||
unsigned int key = irq_lock();
|
||||
|
||||
uarte_enable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
|
||||
|
@ -1543,9 +1605,7 @@ static void rxto_isr(const struct device *dev)
|
|||
nrf_uarte_event_clear(uarte, NRF_UARTE_EVENT_RXDRDY);
|
||||
#endif
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
pm_device_runtime_put(dev);
|
||||
} else if (LOW_POWER_ENABLED(config)) {
|
||||
if (LOW_POWER_ENABLED(config)) {
|
||||
uint32_t key = irq_lock();
|
||||
|
||||
uarte_disable_locked(dev, UARTE_FLAG_LOW_POWER_RX);
|
||||
|
@ -1570,7 +1630,7 @@ static void txstopped_isr(const struct device *dev)
|
|||
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);
|
||||
pm_device_runtime_put_async(dev, K_NO_WAIT);
|
||||
data->flags &= ~UARTE_FLAG_POLL_OUT;
|
||||
}
|
||||
} else if (LOW_POWER_ENABLED(config)) {
|
||||
|
@ -1633,11 +1693,11 @@ 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);
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
pm_device_runtime_put_async(dev, K_NO_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
static void rxdrdy_isr(const struct device *dev)
|
||||
|
@ -1824,6 +1884,22 @@ static void uarte_nrfx_poll_out(const struct device *dev, unsigned char c)
|
|||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
|
||||
if (!IS_PM_ISR_SAFE(dev) && k_is_in_isr()) {
|
||||
/* If instance does not support PM from ISR device shall
|
||||
* already be turned on.
|
||||
*/
|
||||
enum pm_device_state state;
|
||||
int err;
|
||||
|
||||
err = pm_device_state_get(dev, &state);
|
||||
(void)err;
|
||||
__ASSERT_NO_MSG(err == 0);
|
||||
if (state != PM_DEVICE_STATE_ACTIVE) {
|
||||
irq_unlock(key);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(data->flags & UARTE_FLAG_POLL_OUT)) {
|
||||
data->flags |= UARTE_FLAG_POLL_OUT;
|
||||
pm_device_runtime_get(dev);
|
||||
|
@ -2417,7 +2493,8 @@ static int uarte_instance_init(const struct device *dev,
|
|||
} \
|
||||
\
|
||||
PM_DEVICE_DT_DEFINE(UARTE(idx), uarte_nrfx_pm_action, \
|
||||
PM_DEVICE_ISR_SAFE); \
|
||||
COND_CODE_1(INSTANCE_IS_FAST(_, /*empty*/, idx, _),\
|
||||
(0), (PM_DEVICE_ISR_SAFE))); \
|
||||
\
|
||||
DEVICE_DT_DEFINE(UARTE(idx), \
|
||||
uarte_##idx##_init, \
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue