From 60c5ef2eabb12bade80fceb67c3d192e6bc77f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Chru=C5=9Bci=C5=84ski?= Date: Mon, 11 May 2026 14:15:44 +0200 Subject: [PATCH 01/13] UPSTREAM: pm: device_runtime: Fix managing the device from multiple cont... MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 46e5ba3732f48ee9a903239a854079fc04f7b3fa upstream. Add early exist to the suspend function if suspending client is not the last one. It fixes the case where user calls put function from the thread context and it gets interrupted by the device interrupt where another asynchronous put is called. In that case, in the interrupt context the call will return error as semaphore cannot be taken and reference counting gets broken. By adding an early exit if context is not the last one, we ensure that only last client executes suspending procedure. Signed-off-by: Krzysztof Chruściński --- subsys/pm/device_runtime.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/subsys/pm/device_runtime.c b/subsys/pm/device_runtime.c index 12688bcbdec5..ee9e10a20a42 100644 --- a/subsys/pm/device_runtime.c +++ b/subsys/pm/device_runtime.c @@ -21,6 +21,7 @@ LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL); #define PM_DOMAIN(_pm) NULL #endif +static struct k_spinlock lock; #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC #ifdef CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ K_THREAD_STACK_DEFINE(pm_device_runtime_stack, CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_STACK_SIZE); @@ -57,6 +58,7 @@ static int runtime_suspend(const struct device *dev, bool async, { int ret = 0; struct pm_device *pm = dev->pm; + bool early_exit = false; /* * Early return if device runtime is not enabled. @@ -65,6 +67,18 @@ static int runtime_suspend(const struct device *dev, bool async, return 0; } + K_SPINLOCK(&lock) { + /* If we are not the last user, return. */ + if (pm->base.usage > 1U) { + pm->base.usage--; + early_exit = true; + } + } + + if (early_exit == true) { + return 0; + } + if (k_is_pre_kernel()) { async = false; } else { @@ -219,6 +233,21 @@ int pm_device_runtime_get(const struct device *dev) } if (!k_is_pre_kernel()) { + bool early_exit = false; + + K_SPINLOCK(&lock) { + /* If we are not the first user and device is active, return. */ + if ((pm->base.usage > 0U) && (pm->base.state == PM_DEVICE_STATE_ACTIVE)) { + pm->base.usage++; + early_exit = true; + } + } + + if (early_exit == true) { + ret = 0; + goto end; + } + ret = k_sem_take(&pm->lock, k_is_in_isr() ? K_NO_WAIT : K_FOREVER); if (ret < 0) { return -EWOULDBLOCK; From 4dd8d821e9663f57f4aa1bc6b0779d326e0ede75 Mon Sep 17 00:00:00 2001 From: Holt Sun Date: Sun, 26 Jul 2026 15:59:39 +0800 Subject: [PATCH 02/13] UPSTREAM: pm: device_runtime: fix lost usage count update race commit 8d6e134a392d8c9bae0368966f24b2d8bbe4fdd3 upstream. The get/put fast paths update pm->base.usage under the global runtime PM spinlock, but the slow paths update the same non-atomic counter while holding only the per-device semaphore. A pm_device_runtime_get() call from an ISR can then interleave with the last-user pm_device_runtime_put() read-modify-write running in a thread: the fast path sees usage == 1 with the device active, increments the counter and returns 0, and the interrupted thread overwrites the counter with 0 and suspends the device. The ISR ends up holding a runtime PM reference on a suspended device and the usage count is permanently off by one. Serialize all usage read-modify-write sequences of the non-ISR-safe paths through the global spinlock: the unbalanced check, decrement and last-user decision in runtime_suspend(), the suspend failure restores, the slow-path get increment and the resume failure decrement. State keeps being written under the per-device semaphore: every transition away from the active state happens with usage == 0, which is now visible under the spinlock, so the fast-path "usage > 0 and state active" check cannot claim a reference on a device that is about to be suspended. The ISR-safe paths already serialize everything under the per-device spinlock and are unchanged. The counter updates are factored into small runtime_usage_*() helpers so that every read-modify-write visibly goes through the same lock. The race window was confirmed on qemu_x86 by disassembly (the usage load and store are split across a branch, so the timer IRQ can be delivered in between) and reproduced deterministically with the stress test added in the following commit, which fails without this fix and passes with it. Fixes the race left open by commit 46e5ba3732f ("pm: device_runtime: Fix managing the device from multiple contexts"). Signed-off-by: Holt Sun --- subsys/pm/device_runtime.c | 116 ++++++++++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 22 deletions(-) diff --git a/subsys/pm/device_runtime.c b/subsys/pm/device_runtime.c index ee9e10a20a42..9752871ad8af 100644 --- a/subsys/pm/device_runtime.c +++ b/subsys/pm/device_runtime.c @@ -21,6 +21,12 @@ LOG_MODULE_DECLARE(pm_device, CONFIG_PM_DEVICE_LOG_LEVEL); #define PM_DOMAIN(_pm) NULL #endif +/* + * Serializes all pm->base.usage updates for devices without the ISR-safe + * flag. The get/put fast paths read and update the usage counter under this + * lock, possibly from interrupt context, so the slow paths must not modify + * it under the per-device semaphore alone. + */ static struct k_spinlock lock; #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC #ifdef CONFIG_PM_DEVICE_RUNTIME_USE_DEDICATED_WQ @@ -34,6 +40,82 @@ static struct k_work_q pm_device_runtime_wq; #define EVENT_MASK (EVENT_STATE_ACTIVE | EVENT_STATE_SUSPENDED) +/* Increment the usage counter of a device under the global lock. */ +static void runtime_usecount_inc(struct pm_device *pm) +{ + __ASSERT_NO_MSG(pm != NULL); + + K_SPINLOCK(&lock) { + pm->base.usage++; + } +} + +/* Decrement the usage counter of a device under the global lock. */ +static void runtime_usecount_dec(struct pm_device *pm) +{ + __ASSERT_NO_MSG(pm != NULL); + + K_SPINLOCK(&lock) { + pm->base.usage--; + } +} + +/* + * Release one usage reference under the global lock, unless this is the + * last reference. Last-user handling needs the per-device semaphore and + * is left to the caller. + * + * @retval true If the reference was released. + * @retval false If this is the last reference. + */ +static bool runtime_usage_put_fast(struct pm_device *pm) +{ + bool released = false; + + __ASSERT_NO_MSG(pm != NULL); + + K_SPINLOCK(&lock) { + if (pm->base.usage > 1U) { + pm->base.usage--; + released = true; + } + } + + return released; +} + +/* + * Release one usage reference under the global lock, with the per-device + * semaphore held by the caller. + * + * @retval 1 If this was the last reference and the device has to be + * suspended. + * @retval 0 If other references remain and nothing else has to be done. + * @retval -EALREADY If the usage counter was already zero. + */ +static int runtime_usage_put(struct pm_device *pm) +{ + int ret; + + __ASSERT_NO_MSG(pm != NULL); + /* Caller must hold the per-device semaphore, except in pre-kernel + * mode where it is never taken (single-threaded boot). + */ + __ASSERT(k_is_pre_kernel() || (k_sem_count_get(&pm->lock) == 0), + "pm lock not owned"); + + K_SPINLOCK(&lock) { + if (pm->base.usage == 0U) { + ret = -EALREADY; + } else { + pm->base.usage--; + ret = (pm->base.usage == 0U) ? 1 : 0; + } + } + + return ret; +} + /** * @brief Suspend a device * @@ -58,7 +140,6 @@ static int runtime_suspend(const struct device *dev, bool async, { int ret = 0; struct pm_device *pm = dev->pm; - bool early_exit = false; /* * Early return if device runtime is not enabled. @@ -67,15 +148,8 @@ static int runtime_suspend(const struct device *dev, bool async, return 0; } - K_SPINLOCK(&lock) { - /* If we are not the last user, return. */ - if (pm->base.usage > 1U) { - pm->base.usage--; - early_exit = true; - } - } - - if (early_exit == true) { + /* If we are not the last user, return. */ + if (runtime_usage_put_fast(pm)) { return 0; } @@ -88,16 +162,14 @@ static int runtime_suspend(const struct device *dev, bool async, } } - if (pm->base.usage == 0U) { - LOG_WRN("Unbalanced suspend"); - ret = -EALREADY; - goto unlock; - } - - pm->base.usage--; - if (pm->base.usage > 0U) { + ret = runtime_usage_put(pm); + if (ret <= 0) { + if (ret < 0) { + LOG_WRN("Unbalanced suspend"); + } goto unlock; } + ret = 0; if (async) { /* queue suspend */ @@ -113,7 +185,7 @@ static int runtime_suspend(const struct device *dev, bool async, /* suspend now */ ret = pm->base.action_cb(pm->dev, PM_DEVICE_ACTION_SUSPEND); if (ret < 0) { - pm->base.usage++; + runtime_usecount_inc(pm); goto unlock; } @@ -145,7 +217,7 @@ static void runtime_suspend_work(struct k_work *work) (void)k_sem_take(&pm->lock, K_FOREVER); if (ret < 0) { - pm->base.usage++; + runtime_usecount_inc(pm); pm->base.state = PM_DEVICE_STATE_ACTIVE; } else { pm->base.state = PM_DEVICE_STATE_SUSPENDED; @@ -280,7 +352,7 @@ int pm_device_runtime_get(const struct device *dev) atomic_set_bit(&pm->base.flags, PM_DEVICE_FLAG_PD_CLAIMED); } - pm->base.usage++; + runtime_usecount_inc(pm); #ifdef CONFIG_PM_DEVICE_RUNTIME_ASYNC /* @@ -316,7 +388,7 @@ int pm_device_runtime_get(const struct device *dev) ret = pm->base.action_cb(pm->dev, PM_DEVICE_ACTION_RESUME); if (ret < 0) { - pm->base.usage--; + runtime_usecount_dec(pm); if (domain != NULL) { (void)pm_device_runtime_put(domain); atomic_clear_bit(&dev->pm_base->flags, PM_DEVICE_FLAG_PD_CLAIMED); From 336267864b220c3d0f603560d433a9970a412b7f Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Wed, 21 Oct 2026 04:58:16 -0500 Subject: [PATCH 03/13] UPSTREAM-PEND: dts: arm: ti: Add copyright text to MSPM0 G110x and G310x PR: 122748 Add missing copyright text to mspm0g110x.dtsi and mspm0g310x.dtsi which were triggering check_compliance warnings. Signed-off-by: Franklin Cooper Jr --- dts/arm/ti/mspm0/g/mspm0g110x.dtsi | 6 +++++- dts/arm/ti/mspm0/g/mspm0g310x.dtsi | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dts/arm/ti/mspm0/g/mspm0g110x.dtsi b/dts/arm/ti/mspm0/g/mspm0g110x.dtsi index 7662a074329d..8d263f544ece 100644 --- a/dts/arm/ti/mspm0/g/mspm0g110x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g110x.dtsi @@ -1,3 +1,7 @@ -/* SPDX-License-Identifier: Apache-2.0 */ +/* + * SPDX-FileCopyrightText: 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ #include diff --git a/dts/arm/ti/mspm0/g/mspm0g310x.dtsi b/dts/arm/ti/mspm0/g/mspm0g310x.dtsi index ae1f984363cb..9bc1a4aac6f9 100644 --- a/dts/arm/ti/mspm0/g/mspm0g310x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g310x.dtsi @@ -1,4 +1,8 @@ -/* SPDX-License-Identifier: Apache-2.0 */ +/* + * SPDX-FileCopyrightText: 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ #include From 51a18a7f754a7c1eed64ba12d56225e747a7aaed Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Thu, 27 Aug 2026 07:41:26 -0500 Subject: [PATCH 04/13] UPSTREAM-PEND: drivers: uart: mspm0: Run clang-format on source file PR: 122748 Run clang-format on source file to avoid unexpected line changes in future commits. Signed-off-by: Franklin Cooper Jr --- drivers/serial/uart_mspm0.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/serial/uart_mspm0.c b/drivers/serial/uart_mspm0.c index f95346a4894c..ff8c1131cd67 100644 --- a/drivers/serial/uart_mspm0.c +++ b/drivers/serial/uart_mspm0.c @@ -137,10 +137,10 @@ #endif /* !CONFIG_HAS_MSPM0_SDK */ -#define UART_IFLS_TXIFLSEL_LVL_1_ENTRY FIELD_PREP(UART_IFLS_TXIFLSEL_MASK, 7) +#define UART_IFLS_TXIFLSEL_LVL_1_ENTRY FIELD_PREP(UART_IFLS_TXIFLSEL_MASK, 7) #define UART_IFLS_UNICOMM_TXIFLSEL_LVL_1_ENTRY FIELD_PREP(UART_IFLS_TXIFLSEL_MASK, 4) -#define UART_IFLS_RXIFLSEL_LVL_1_ENTRY UART_IFLS_TXIFLSEL_LVL_1_ENTRY +#define UART_IFLS_RXIFLSEL_LVL_1_ENTRY UART_IFLS_TXIFLSEL_LVL_1_ENTRY #define UART_IFLS_UNICOMM_RXIFLSEL_LVL_1_ENTRY UART_IFLS_UNICOMM_TXIFLSEL_LVL_1_ENTRY /* @@ -239,7 +239,7 @@ struct uart_mspm0_config { * Grouped here so they can be accessed as data->uart_config.parity, etc. */ struct uart_mspm0_line_config { - uint32_t parity; /* LCRH parity bits */ + uint32_t parity; /* LCRH parity bits */ uint32_t word_length; /* LCRH WLEN field */ uint32_t stop_bits; /* LCRH STP2 field */ uint32_t flow_control; /* CTL0 RTSEN/CTSEN bits */ @@ -864,15 +864,16 @@ static DEVICE_API(uart, uart_mspm0_driver_api) = { #define MSPM0_CLK_DIV_REG(n) (DT_INST_PROP(n, clk_div) - 1) +/* clang-format off */ #define MSPM0_UART_INIT_FN(index) \ - \ + \ PINCTRL_DT_INST_DEFINE(index); \ - \ + \ static const struct mspm0_sys_clock mspm0_uart_sys_clock##index = \ MSPM0_CLOCK_SUBSYS_FN(index); \ - \ + \ MSP_UART_IRQ_DEFINE(index); \ - \ + \ static const struct uart_mspm0_config uart_mspm0_cfg_##index = { \ .regs = (mspm0_uart_regs *)(DT_INST_REG_ADDR(index) - MSPM0_UART_REGS_OFFSET), \ .pinctrl = PINCTRL_DT_INST_DEV_CONFIG_GET(index), \ @@ -880,10 +881,10 @@ static DEVICE_API(uart, uart_mspm0_driver_api) = { .clk_sel = MSPM0_CLOCK_PERIPH_REG_MASK(DT_INST_CLOCKS_CELL(index, clk)), \ .clk_div = MSPM0_CLK_DIV_REG(index), \ .stat_txff_mask = COND_CODE_1(DT_INST_PROP(index, is_unicomm_uart), \ - (UART_STAT_UNICOMM_TXFF_MASK), \ + (UART_STAT_UNICOMM_TXFF_MASK), \ (UART_STAT_TXFF_MASK)), \ .stat_txfe_mask = COND_CODE_1(DT_INST_PROP(index, is_unicomm_uart), \ - (UART_STAT_UNICOMM_TXFE_MASK), \ + (UART_STAT_UNICOMM_TXFE_MASK), \ (UART_STAT_TXFE_MASK)), \ .skip_power_on = COND_CODE_1(DT_INST_PROP(index, is_unicomm_uart), \ (true), \ @@ -904,7 +905,7 @@ static DEVICE_API(uart, uart_mspm0_driver_api) = { .parity = 0, \ .word_length = UART_LCRH_WLEN_DATABIT8, \ .stop_bits = UART_LCRH_STP2_DISABLE, \ - .flow_control = (DT_INST_PROP(index, hw_flow_control) \ + .flow_control = (DT_INST_PROP(index, hw_flow_control) \ ? (UART_CTL0_RTSEN_ENABLE | \ UART_CTL0_CTSEN_ENABLE) \ : (UART_CTL0_RTSEN_DISABLE | \ @@ -915,5 +916,6 @@ static DEVICE_API(uart, uart_mspm0_driver_api) = { DEVICE_DT_INST_DEFINE(index, &uart_mspm0_init, NULL, &uart_mspm0_data_##index, \ &uart_mspm0_cfg_##index, PRE_KERNEL_1, \ CONFIG_SERIAL_INIT_PRIORITY, &uart_mspm0_driver_api); +/* clang-format on */ DT_INST_FOREACH_STATUS_OKAY(MSPM0_UART_INIT_FN) From 62be389dace95856e212af0a0b780c7d0a498983 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Thu, 27 Aug 2026 16:34:52 -0500 Subject: [PATCH 05/13] UPSTREAM-PEND: drivers: uart_mspm0 - Enable pm device runtime support PR: 122748 Add support for pm device runtime. Signed-off-by: Franklin Cooper Jr --- drivers/serial/uart_mspm0.c | 209 +++++++++++++++++++++++++++++++++--- 1 file changed, 193 insertions(+), 16 deletions(-) diff --git a/drivers/serial/uart_mspm0.c b/drivers/serial/uart_mspm0.c index ff8c1131cd67..143012f54f38 100644 --- a/drivers/serial/uart_mspm0.c +++ b/drivers/serial/uart_mspm0.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include /* * This driver uses its own native register definitions (mspm0_uart_regs and @@ -89,6 +91,7 @@ #define UART_STAT_TXFE_MASK GENMASK(6, 6) #define UART_STAT_TXFF_MASK GENMASK(7, 7) #define UART_STAT_RXFE_MASK GENMASK(2, 2) +#define UART_STAT_BUSY_MASK GENMASK(0, 0) /* * IFLS Interrupt FIFO Level Select Register @@ -143,6 +146,8 @@ #define UART_IFLS_RXIFLSEL_LVL_1_ENTRY UART_IFLS_TXIFLSEL_LVL_1_ENTRY #define UART_IFLS_UNICOMM_RXIFLSEL_LVL_1_ENTRY UART_IFLS_UNICOMM_TXIFLSEL_LVL_1_ENTRY +#define UART_MSPM0_RX_FIFO_DEPTH 4U + /* * UART register-map structs. */ @@ -250,6 +255,10 @@ struct uart_mspm0_data { uint32_t current_speed; /* UART line configuration - raw register field values */ struct uart_mspm0_line_config uart_config; +#ifdef CONFIG_PM_DEVICE + /* Maximum time needed to transmit one frame. */ + uint32_t frame_time_us; +#endif #ifdef CONFIG_UART_INTERRUPT_DRIVEN /* Callback function pointer */ uart_irq_callback_user_data_t cb; @@ -291,6 +300,15 @@ static const uint32_t uart_mspm0_tx_fifo_level[] = { }; #endif /* CONFIG_UART_INTERRUPT_DRIVEN */ +static inline int uart_mspm0_pm_is_active(const struct device *dev) +{ + enum pm_device_state state = PM_DEVICE_STATE_OFF; + int err; + + err = pm_device_state_get(dev, &state); + return state == PM_DEVICE_STATE_ACTIVE || err < 0; +} + static inline void uart_mspm0_set_clock(mspm0_uart_regs *regs, uint32_t clk_sel, uint32_t clk_div) { regs->clksel = clk_sel; @@ -356,7 +374,7 @@ static int uart_mspm0_poll_in(const struct device *dev, unsigned char *c) const struct uart_mspm0_config *config = dev->config; mspm0_uart_regs *regs = config->regs; - if ((regs->stat & UART_STAT_RXFE_MASK) != 0U) { + if (uart_mspm0_pm_is_active(dev) == 0U || (regs->stat & UART_STAT_RXFE_MASK) != 0U) { return -1; } @@ -370,12 +388,24 @@ static void uart_mspm0_poll_out(const struct device *dev, unsigned char c) mspm0_uart_regs *regs = config->regs; unsigned int key; + if (pm_device_runtime_get(dev) < 0) { + return; + } + + /* Runtime PM resumes an opted-in device. Discard transmission if the device + * was suspended directly through the device PM API, preventing data from + * being trapped in the TX FIFO and unexpectedly transmitted after resume. + */ + if (uart_mspm0_pm_is_active(dev) == 0U) { + goto out; + } + /* Skip the irq_lock()s below when in ISR context. */ if (k_is_in_isr()) { while ((regs->stat & config->stat_txff_mask) != 0U) { } regs->txdata = (uint32_t)c; - return; + goto out; } /* Wait until TX FIFO has space, then write atomically. @@ -390,12 +420,96 @@ static void uart_mspm0_poll_out(const struct device *dev, unsigned char c) if ((regs->stat & config->stat_txff_mask) == 0U) { regs->txdata = (uint32_t)c; irq_unlock(key); - return; + break; } irq_unlock(key); } while (true); + +out: + (void)pm_device_runtime_put(dev); } +#if defined(CONFIG_PM_DEVICE) + +static uint32_t uart_mspm0_frame_time_us(const struct uart_mspm0_data *data) +{ + uint32_t bits; + + bits = 1U; /* Start bit */ + bits += FIELD_GET(UART_LCRH_WLEN_MASK, data->uart_config.word_length) + 5U; + bits += (data->uart_config.parity & UART_LCRH_PEN_MASK) != 0U; + bits += (data->uart_config.stop_bits & UART_LCRH_STP2_MASK) != 0U ? 2U : 1U; + + return DIV_ROUND_UP(bits * USEC_PER_SEC, data->current_speed) + 10U; +} + +static int uart_mspm0_suspend_hw(const struct device *dev) +{ + const struct uart_mspm0_config *config = dev->config; + int err; + + /* Move pins to sleep state. */ + err = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_SLEEP); + if ((err < 0) && (err != -ENOENT)) { + return err; + } + + /* Unselect all parent clocks, effectively gating the entire peripheral. */ + uart_mspm0_set_clock(config->regs, 0U, config->clk_div); + + return 0; +} + +static int uart_mspm_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct uart_mspm0_data *data = dev->data; + int err; + const struct uart_mspm0_config *config = dev->config; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + /* Set pins to active state */ + err = pinctrl_apply_state(config->pinctrl, PINCTRL_STATE_DEFAULT); + if (err < 0) { + return err; + } + + /* Restore selection of parent clock that may been previously gated */ + uart_mspm0_set_clock(config->regs, config->clk_sel, config->clk_div); + + /* Flush the RX FIFO after the peripheral clock has been restored. */ + for (uint32_t i = 0U; (i < UART_MSPM0_RX_FIFO_DEPTH) && + ((config->regs->stat & UART_STAT_RXFE_MASK) == 0U); + i++) { + (void)config->regs->rxdata; + } + + break; + case PM_DEVICE_ACTION_SUSPEND: + /* Wait until all queued data has moved out of the TX FIFO. */ + while ((config->regs->stat & config->stat_txfe_mask) == 0U) { + } + + /* Wait until frame is fully transmitted and the TX line is idle. + * BUSY flag is also for RX so don't wait indefinitely for it. + */ + (void)WAIT_FOR((config->regs->stat & UART_STAT_BUSY_MASK) == 0U, + data->frame_time_us, k_busy_wait(1)); + + err = uart_mspm0_suspend_hw(dev); + if (err < 0) { + return err; + } + + break; + default: + return -ENOTSUP; + } + + return 0; +} +#endif + static int uart_mspm0_install_configuration(const struct device *dev) { const struct device *clk_dev = DEVICE_DT_GET(DT_NODELABEL(ckm)); @@ -432,6 +546,10 @@ static int uart_mspm0_install_configuration(const struct device *dev) /* Configure baud rate */ uart_mspm0_configure_baudrate(config->regs, uart_clk, data->current_speed); +#ifdef CONFIG_PM_DEVICE + data->frame_time_us = uart_mspm0_frame_time_us(data); +#endif + /* Re-enable FIFOs: uart_mspm0_configure_line() overwrites CTL0 entirely, * clearing the FEN bit. Always re-enable FIFOs after reconfiguring the * line control registers so that FIFO-based interrupt thresholds (IFLS) @@ -539,6 +657,11 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf uint32_t value; int ret; + ret = pm_device_runtime_get(dev); + if (ret < 0) { + return ret; + } + UART_MSPM0_DISABLE(config->regs); data->current_speed = cfg->baudrate; @@ -546,7 +669,7 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf ret = uart_mspm0_translate_in(uart_parity_to_mspm0, ARRAY_SIZE(uart_parity_to_mspm0), cfg->parity, &value); if (ret != 0) { - return ret; + goto out; } data->uart_config.parity = value; @@ -554,7 +677,7 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf ret = uart_mspm0_translate_in(uart_stop_bits_to_mspm0, ARRAY_SIZE(uart_stop_bits_to_mspm0), cfg->stop_bits, &value); if (ret != 0) { - return ret; + goto out; } data->uart_config.stop_bits = value; @@ -562,7 +685,7 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf ret = uart_mspm0_translate_in(uart_data_bits_to_mspm0, ARRAY_SIZE(uart_data_bits_to_mspm0), cfg->data_bits, &value); if (ret != 0) { - return ret; + goto out; } data->uart_config.word_length = value; @@ -571,19 +694,22 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf ARRAY_SIZE(uart_flow_control_to_mspm0), cfg->flow_ctrl, &value); if (ret != 0) { - return ret; + goto out; } data->uart_config.flow_control = value; ret = uart_mspm0_install_configuration(dev); if (ret != 0) { - return ret; + goto out; } UART_MSPM0_ENABLE(config->regs); - return 0; + ret = 0; +out: + (void)pm_device_runtime_put(dev); + return ret; } static int uart_mspm0_config_get(const struct device *dev, struct uart_config *cfg) @@ -645,6 +771,13 @@ static int uart_mspm0_fifo_fill(const struct device *dev, const uint8_t *tx_data const struct uart_mspm0_config *config = dev->config; int count = 0; + /* Discard transmission if the peripheral is inactive. This prevents data from + * getting trapped in the TX buffer and unexpectedly transmitting later upon wake-up. + */ + if (uart_mspm0_pm_is_active(dev) == 0U) { + return 0; + } + while (count < size && ((config->regs->stat & config->stat_txff_mask) == 0)) { config->regs->txdata = tx_data[count]; count++; @@ -658,6 +791,10 @@ static int uart_mspm0_fifo_read(const struct device *dev, uint8_t *rx_data, cons const struct uart_mspm0_config *config = dev->config; int count = 0; + if (uart_mspm0_pm_is_active(dev) == 0U) { + return 0; + } + while (count < size && ((config->regs->stat & UART_STAT_RXFE_MASK) == 0)) { rx_data[count] = (uint8_t)(config->regs->rxdata & 0xFF); count++; @@ -670,7 +807,12 @@ static void uart_mspm0_irq_tx_enable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask |= UART_MSPM0_TX_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_TX_INTERRUPTS) == 0U) { + if (pm_device_runtime_get(dev) < 0) { + return; + } + config->regs->cpu_int.imask |= UART_MSPM0_TX_INTERRUPTS; + } /* If the TX FIFO is already empty (below the LVL_EMPTY threshold) the * TXIFG flag may have been cleared by a prior IIDX read without a new @@ -687,7 +829,10 @@ static void uart_mspm0_irq_tx_disable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask &= ~UART_MSPM0_TX_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_TX_INTERRUPTS) != 0U) { + config->regs->cpu_int.imask &= ~UART_MSPM0_TX_INTERRUPTS; + (void)pm_device_runtime_put(dev); + } } static int uart_mspm0_irq_tx_ready(const struct device *dev) @@ -703,7 +848,12 @@ static void uart_mspm0_irq_rx_enable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask |= UART_MSPM0_RX_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_RX_INTERRUPTS) == 0U) { + if (pm_device_runtime_get(dev) < 0) { + return; + } + config->regs->cpu_int.imask |= UART_MSPM0_RX_INTERRUPTS; + } /* If the RX FIFO already contains data the RXIFG flag may have been * cleared by a prior IIDX read while the interrupt was masked, without @@ -720,7 +870,10 @@ static void uart_mspm0_irq_rx_disable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask &= ~UART_MSPM0_RX_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_RX_INTERRUPTS) != 0U) { + config->regs->cpu_int.imask &= ~UART_MSPM0_RX_INTERRUPTS; + (void)pm_device_runtime_put(dev); + } } static int uart_mspm0_irq_tx_complete(const struct device *dev) @@ -769,14 +922,22 @@ static void uart_mspm0_irq_error_enable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask |= UART_MSPM0_ERROR_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_ERROR_INTERRUPTS) == 0U) { + if (pm_device_runtime_get(dev) < 0) { + return; + } + config->regs->cpu_int.imask |= UART_MSPM0_ERROR_INTERRUPTS; + } } static void uart_mspm0_irq_error_disable(const struct device *dev) { const struct uart_mspm0_config *config = dev->config; - config->regs->cpu_int.imask &= ~UART_MSPM0_ERROR_INTERRUPTS; + if ((config->regs->cpu_int.imask & UART_MSPM0_ERROR_INTERRUPTS) != 0U) { + config->regs->cpu_int.imask &= ~UART_MSPM0_ERROR_INTERRUPTS; + (void)pm_device_runtime_put(dev); + } } static void uart_mspm0_isr(const struct device *dev) @@ -821,7 +982,21 @@ static int uart_mspm0_init(const struct device *dev) /* Enable UART */ UART_MSPM0_ENABLE(config->regs); +#if defined(CONFIG_PM_DEVICE) + /* + * pm_device_driver_init() expects runtime-managed hardware to start suspended. + * No transmission can be pending during initialization, and timer-based waits + * are not available yet because this driver initializes at PRE_KERNEL_1. + */ + ret = uart_mspm0_suspend_hw(dev); + if (ret < 0) { + return ret; + } + + return pm_device_driver_init(dev, uart_mspm_pm_action); +#else return 0; +#endif } static DEVICE_API(uart, uart_mspm0_driver_api) = { @@ -913,7 +1088,9 @@ static DEVICE_API(uart, uart_mspm0_driver_api) = { }, \ }; \ \ - DEVICE_DT_INST_DEFINE(index, &uart_mspm0_init, NULL, &uart_mspm0_data_##index, \ + PM_DEVICE_DT_INST_DEFINE(index, uart_mspm_pm_action); \ + DEVICE_DT_INST_DEFINE(index, &uart_mspm0_init, PM_DEVICE_DT_INST_GET(index), \ + &uart_mspm0_data_##index, \ &uart_mspm0_cfg_##index, PRE_KERNEL_1, \ CONFIG_SERIAL_INIT_PRIORITY, &uart_mspm0_driver_api); /* clang-format on */ From 65e4db79905f709e67584520f6a702b2396c155c Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Fri, 28 Aug 2026 10:18:37 -0500 Subject: [PATCH 06/13] UPSTREAM-PEND: dts: arm: ti: mspm0: Specify disabling power states for UART PR: 122748 Add disabling power states for uart peripheral. These low power modes will disrupt uart operations. This information came from the datasheets table called "Operating Modes". Signed-off-by: Franklin Cooper Jr --- dts/arm/ti/mspm0/g/mspm0g.dtsi | 3 +++ dts/arm/ti/mspm0/g/mspm0g110x.dtsi | 4 ++++ dts/arm/ti/mspm0/g/mspm0g150x.dtsi | 4 ++++ dts/arm/ti/mspm0/g/mspm0g310x.dtsi | 4 ++++ dts/arm/ti/mspm0/g/mspm0gx51x.dtsi | 4 ++++ dts/arm/ti/mspm0/l/mspm0l110x.dtsi | 1 + dts/arm/ti/mspm0/l/mspm0lx22x.dtsi | 4 ++++ dts/arm/ti/mspm0/mspm0.dtsi | 1 + 8 files changed, 25 insertions(+) diff --git a/dts/arm/ti/mspm0/g/mspm0g.dtsi b/dts/arm/ti/mspm0/g/mspm0g.dtsi index 1fcf7d388074..a55f1337b86e 100644 --- a/dts/arm/ti/mspm0/g/mspm0g.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g.dtsi @@ -231,6 +231,7 @@ current-speed = <115200>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; @@ -241,6 +242,7 @@ current-speed = <115200>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; @@ -251,6 +253,7 @@ current-speed = <115200>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; diff --git a/dts/arm/ti/mspm0/g/mspm0g110x.dtsi b/dts/arm/ti/mspm0/g/mspm0g110x.dtsi index 8d263f544ece..fedc9fadbc13 100644 --- a/dts/arm/ti/mspm0/g/mspm0g110x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g110x.dtsi @@ -5,3 +5,7 @@ */ #include + +&uart3 { + zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>; +}; diff --git a/dts/arm/ti/mspm0/g/mspm0g150x.dtsi b/dts/arm/ti/mspm0/g/mspm0g150x.dtsi index dd4f526a9b1f..d073c1bf4810 100644 --- a/dts/arm/ti/mspm0/g/mspm0g150x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g150x.dtsi @@ -6,6 +6,10 @@ #include +&uart3 { + zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>; +}; + / { soc { dac0: dac@40018000 { diff --git a/dts/arm/ti/mspm0/g/mspm0g310x.dtsi b/dts/arm/ti/mspm0/g/mspm0g310x.dtsi index 9bc1a4aac6f9..b2ace4b1cfcf 100644 --- a/dts/arm/ti/mspm0/g/mspm0g310x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0g310x.dtsi @@ -6,6 +6,10 @@ #include +&uart3 { + zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>; +}; + / { soc { aes: aes@40442000 { diff --git a/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi b/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi index 45062c5b0ebd..fecfb57a17dd 100644 --- a/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi +++ b/dts/arm/ti/mspm0/g/mspm0gx51x.dtsi @@ -229,3 +229,7 @@ <&gpio_none>, <&gpio_none>; }; + +&uart3 { + zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>; +}; diff --git a/dts/arm/ti/mspm0/l/mspm0l110x.dtsi b/dts/arm/ti/mspm0/l/mspm0l110x.dtsi index 258a24cbd44f..925388ab859b 100644 --- a/dts/arm/ti/mspm0/l/mspm0l110x.dtsi +++ b/dts/arm/ti/mspm0/l/mspm0l110x.dtsi @@ -59,6 +59,7 @@ current-speed = <115200>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; }; diff --git a/dts/arm/ti/mspm0/l/mspm0lx22x.dtsi b/dts/arm/ti/mspm0/l/mspm0lx22x.dtsi index 6d8588597c54..a35ef2194f52 100644 --- a/dts/arm/ti/mspm0/l/mspm0lx22x.dtsi +++ b/dts/arm/ti/mspm0/l/mspm0lx22x.dtsi @@ -173,6 +173,7 @@ current-speed = <9600>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; @@ -183,6 +184,7 @@ current-speed = <9600>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; @@ -193,6 +195,7 @@ current-speed = <9600>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; @@ -203,6 +206,7 @@ current-speed = <9600>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; diff --git a/dts/arm/ti/mspm0/mspm0.dtsi b/dts/arm/ti/mspm0/mspm0.dtsi index c6568d08b642..39247b7800d7 100644 --- a/dts/arm/ti/mspm0/mspm0.dtsi +++ b/dts/arm/ti/mspm0/mspm0.dtsi @@ -264,6 +264,7 @@ current-speed = <115200>; clocks = <&ckm MSPM0_CLOCK_ULPCLK>; clk-div = <1>; + zephyr,disabling-power-states = <&stdby1>; status = "disabled"; }; From c67500e7970dad351cde56d5715290829e055a05 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Sat, 29 Aug 2026 15:47:09 -0500 Subject: [PATCH 07/13] UPSTREAM-PEND: drivers: uart_mspm0 - Add policy locks PR: 122748 Add policy locks to prevent system from entering a low power mode that would disrupt uart operations. Signed-off-by: Franklin Cooper Jr --- drivers/serial/uart_mspm0.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/serial/uart_mspm0.c b/drivers/serial/uart_mspm0.c index 143012f54f38..91b73415012e 100644 --- a/drivers/serial/uart_mspm0.c +++ b/drivers/serial/uart_mspm0.c @@ -30,6 +30,7 @@ #include #include #include +#include /* * This driver uses its own native register definitions (mspm0_uart_regs and @@ -391,6 +392,7 @@ static void uart_mspm0_poll_out(const struct device *dev, unsigned char c) if (pm_device_runtime_get(dev) < 0) { return; } + pm_policy_device_power_lock_get(dev); /* Runtime PM resumes an opted-in device. Discard transmission if the device * was suspended directly through the device PM API, preventing data from @@ -426,6 +428,7 @@ static void uart_mspm0_poll_out(const struct device *dev, unsigned char c) } while (true); out: + pm_policy_device_power_lock_put(dev); (void)pm_device_runtime_put(dev); } @@ -811,6 +814,7 @@ static void uart_mspm0_irq_tx_enable(const struct device *dev) if (pm_device_runtime_get(dev) < 0) { return; } + pm_policy_device_power_lock_get(dev); config->regs->cpu_int.imask |= UART_MSPM0_TX_INTERRUPTS; } @@ -831,6 +835,7 @@ static void uart_mspm0_irq_tx_disable(const struct device *dev) if ((config->regs->cpu_int.imask & UART_MSPM0_TX_INTERRUPTS) != 0U) { config->regs->cpu_int.imask &= ~UART_MSPM0_TX_INTERRUPTS; + pm_policy_device_power_lock_put(dev); (void)pm_device_runtime_put(dev); } } @@ -852,6 +857,7 @@ static void uart_mspm0_irq_rx_enable(const struct device *dev) if (pm_device_runtime_get(dev) < 0) { return; } + pm_policy_device_power_lock_get(dev); config->regs->cpu_int.imask |= UART_MSPM0_RX_INTERRUPTS; } @@ -872,6 +878,7 @@ static void uart_mspm0_irq_rx_disable(const struct device *dev) if ((config->regs->cpu_int.imask & UART_MSPM0_RX_INTERRUPTS) != 0U) { config->regs->cpu_int.imask &= ~UART_MSPM0_RX_INTERRUPTS; + pm_policy_device_power_lock_put(dev); (void)pm_device_runtime_put(dev); } } @@ -926,6 +933,7 @@ static void uart_mspm0_irq_error_enable(const struct device *dev) if (pm_device_runtime_get(dev) < 0) { return; } + pm_policy_device_power_lock_get(dev); config->regs->cpu_int.imask |= UART_MSPM0_ERROR_INTERRUPTS; } } @@ -936,6 +944,7 @@ static void uart_mspm0_irq_error_disable(const struct device *dev) if ((config->regs->cpu_int.imask & UART_MSPM0_ERROR_INTERRUPTS) != 0U) { config->regs->cpu_int.imask &= ~UART_MSPM0_ERROR_INTERRUPTS; + pm_policy_device_power_lock_put(dev); (void)pm_device_runtime_put(dev); } } From 93be49609e110f30ce1cf902c46bac2f2a0cef93 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Wed, 2 Sep 2026 09:26:41 -0500 Subject: [PATCH 08/13] UPSTREAM-PEND: drivers: uart_mspm0 - Do not disable and reenable FEN PR: 122748 Disabling and reenabling FEN improperly causing the BUSY flag to indicate the UART is still busy. Since we are always using FIFO just keep it set. Signed-off-by: Franklin Cooper Jr --- drivers/serial/uart_mspm0.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/serial/uart_mspm0.c b/drivers/serial/uart_mspm0.c index 91b73415012e..93f9ba5b01ab 100644 --- a/drivers/serial/uart_mspm0.c +++ b/drivers/serial/uart_mspm0.c @@ -318,9 +318,13 @@ static inline void uart_mspm0_set_clock(mspm0_uart_regs *regs, uint32_t clk_sel, /* Configure line control: parity, word length, stop bits */ static void uart_mspm0_configure_line(mspm0_uart_regs *regs, uint32_t parity, uint32_t word_length, - uint32_t stop_bits, uint32_t flow_control) + uint32_t stop_bits, uint32_t flow_control, bool unicommuart) { + /* Standard UART instances operate in FIFO mode. Configure FEN together with + * the other CTL0 fields to avoid transitioning through character mode. + */ uint32_t ctl0 = UART_CTL0_MODE_UART | UART_CTL0_RXE_ENABLE | UART_CTL0_TXE_ENABLE | + (unicommuart ? 0U : UART_CTL0_FEN_ENABLE) | (flow_control & (UART_CTL0_RTSEN_MASK | UART_CTL0_CTSEN_MASK)); uint32_t lcrh = (parity & (UART_LCRH_PEN_MASK | UART_LCRH_EPS_MASK | UART_LCRH_SPS_MASK)) | @@ -518,11 +522,11 @@ static int uart_mspm0_install_configuration(const struct device *dev) const struct device *clk_dev = DEVICE_DT_GET(DT_NODELABEL(ckm)); const struct uart_mspm0_config *config = dev->config; struct uart_mspm0_data *data = dev->data; + const bool unicommuart = config->skip_power_on; #ifdef CONFIG_UART_INTERRUPT_DRIVEN uint32_t rx_level; uint32_t tx_level; #endif - const bool unicommuart = config->skip_power_on; uint32_t clock_rate; uint32_t uart_clk; int ret; @@ -533,7 +537,7 @@ static int uart_mspm0_install_configuration(const struct device *dev) /* Configure line control */ uart_mspm0_configure_line(config->regs, data->uart_config.parity, data->uart_config.word_length, data->uart_config.stop_bits, - data->uart_config.flow_control); + data->uart_config.flow_control, unicommuart); /* Get source clock rate */ ret = clock_control_get_rate(clk_dev, (struct mspm0_sys_clock *)config->clock_subsys, @@ -553,15 +557,6 @@ static int uart_mspm0_install_configuration(const struct device *dev) data->frame_time_us = uart_mspm0_frame_time_us(data); #endif - /* Re-enable FIFOs: uart_mspm0_configure_line() overwrites CTL0 entirely, - * clearing the FEN bit. Always re-enable FIFOs after reconfiguring the - * line control registers so that FIFO-based interrupt thresholds (IFLS) - * remain operative. The UNICOMMUART has no FEN bit. - */ - if (!unicommuart) { - config->regs->ctl0 |= UART_CTL0_FEN_ENABLE; - } - #ifdef CONFIG_UART_INTERRUPT_DRIVEN /* Restore FIFO interrupt level select thresholds. These are also wiped * when the peripheral is reset/re-initialised, so restore them here in From 45d6e1e0117219cb298a7f61f4f82980aacffa14 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Thu, 3 Sep 2026 14:53:18 -0500 Subject: [PATCH 09/13] UPSTREAM-PEND: drivers: uart_mspm0 - Don't update configuration until it... PR: 122748 Don't update configuration until we are certain its all valid. Signed-off-by: Franklin Cooper Jr --- drivers/serial/uart_mspm0.c | 54 +++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/drivers/serial/uart_mspm0.c b/drivers/serial/uart_mspm0.c index 93f9ba5b01ab..9b88723210a0 100644 --- a/drivers/serial/uart_mspm0.c +++ b/drivers/serial/uart_mspm0.c @@ -652,60 +652,62 @@ static int uart_mspm0_configure(const struct device *dev, const struct uart_conf { const struct uart_mspm0_config *config = dev->config; struct uart_mspm0_data *data = dev->data; - uint32_t value; + struct uart_mspm0_line_config new_config; + struct uart_mspm0_line_config previous_config; + uint32_t previous_speed; int ret; - ret = pm_device_runtime_get(dev); - if (ret < 0) { - return ret; + if (cfg->baudrate == 0U) { + return -EINVAL; } - UART_MSPM0_DISABLE(config->regs); - - data->current_speed = cfg->baudrate; - ret = uart_mspm0_translate_in(uart_parity_to_mspm0, ARRAY_SIZE(uart_parity_to_mspm0), - cfg->parity, &value); + cfg->parity, &new_config.parity); if (ret != 0) { - goto out; + return ret; } - data->uart_config.parity = value; - ret = uart_mspm0_translate_in(uart_stop_bits_to_mspm0, ARRAY_SIZE(uart_stop_bits_to_mspm0), - cfg->stop_bits, &value); + cfg->stop_bits, &new_config.stop_bits); if (ret != 0) { - goto out; + return ret; } - data->uart_config.stop_bits = value; - ret = uart_mspm0_translate_in(uart_data_bits_to_mspm0, ARRAY_SIZE(uart_data_bits_to_mspm0), - cfg->data_bits, &value); + cfg->data_bits, &new_config.word_length); if (ret != 0) { - goto out; + return ret; } - data->uart_config.word_length = value; - ret = uart_mspm0_translate_in(uart_flow_control_to_mspm0, ARRAY_SIZE(uart_flow_control_to_mspm0), cfg->flow_ctrl, - &value); + &new_config.flow_control); if (ret != 0) { - goto out; + return ret; } - data->uart_config.flow_control = value; + ret = pm_device_runtime_get(dev); + if (ret < 0) { + return ret; + } + + UART_MSPM0_DISABLE(config->regs); + + previous_speed = data->current_speed; + previous_config = data->uart_config; + data->current_speed = cfg->baudrate; + data->uart_config = new_config; ret = uart_mspm0_install_configuration(dev); if (ret != 0) { + data->current_speed = previous_speed; + data->uart_config = previous_config; + (void)uart_mspm0_install_configuration(dev); goto out; } - UART_MSPM0_ENABLE(config->regs); - - ret = 0; out: + UART_MSPM0_ENABLE(config->regs); (void)pm_device_runtime_put(dev); return ret; } From 234a584cbf5279686c868ff540afe80d750d3076 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Fri, 4 Sep 2026 08:37:56 -0500 Subject: [PATCH 10/13] UPSTREAM-PEND: tests: drivers: uart: uart_pm - Don't require disable_rx ... PR: 122748 If a uart peripheral driver doesn't support disable_rx then test will fail with a compile error. Instead if disable_rx is not found then default to 0/false. The test already handles when this property is 1 or 0 so no need to prevent the entire test from working. Signed-off-by: Franklin Cooper Jr --- tests/drivers/uart/uart_pm/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/drivers/uart/uart_pm/src/main.c b/tests/drivers/uart/uart_pm/src/main.c index e28d55efb331..f6eb748acf5a 100644 --- a/tests/drivers/uart/uart_pm/src/main.c +++ b/tests/drivers/uart/uart_pm/src/main.c @@ -10,7 +10,7 @@ #include #define UART_NODE DT_NODELABEL(dut) -#define DISABLED_RX DT_PROP(UART_NODE, disable_rx) +#define DISABLED_RX DT_PROP_OR(UART_NODE, disable_rx, 0) static void polling_verify(const struct device *dev, bool is_async, bool active) { From 3b396a7fe6bccac205ecf95bd2fbf1a4dbd53a73 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Mon, 7 Sep 2026 10:12:33 -0500 Subject: [PATCH 11/13] UPSTREAM-PEND: tests: uart_elementary - Support TI's G3519 and L2228 Lau... PR: 122748 Add support for TI's lp_mspm0g3519 and lp_mspm0l2228 launchpad. Signed-off-by: Franklin Cooper Jr --- tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf | 2 ++ tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.overlay | 1 + tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf | 2 ++ tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.overlay | 1 + 4 files changed, 6 insertions(+) create mode 100644 tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf create mode 100644 tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf diff --git a/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf b/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf new file mode 100644 index 000000000000..74cc8d7691e1 --- /dev/null +++ b/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf @@ -0,0 +1,2 @@ +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y diff --git a/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.overlay b/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.overlay index d0f3af819bb2..615f2380c5e5 100644 --- a/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.overlay +++ b/tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.overlay @@ -10,4 +10,5 @@ dut: &uart1 { current-speed = <115200>; hw-flow-control; status = "okay"; + zephyr,pm-device-runtime-auto; }; diff --git a/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf b/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf new file mode 100644 index 000000000000..74cc8d7691e1 --- /dev/null +++ b/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf @@ -0,0 +1,2 @@ +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y diff --git a/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.overlay b/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.overlay index b0a54564b9cd..1573ffb833de 100644 --- a/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.overlay +++ b/tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.overlay @@ -20,6 +20,7 @@ pinctrl-names = "default"; hw-flow-control; status = "okay"; + zephyr,pm-device-runtime-auto; }; }; }; From 98a23cdac3e2e325f5c1bf092273793f7a3306d8 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Mon, 7 Sep 2026 16:21:47 -0500 Subject: [PATCH 12/13] UPSTREAM-PEND: tests: uart_pm - Add support for TI G3519 & L2228 launchpad PR: 122748 Add support for TI's lp_mspm0g3519 and lp_mspm0l2228 board. Signed-off-by: Franklin Cooper Jr --- .../uart/uart_pm/boards/lp_mspm0g3519.overlay | 21 +++++++++++++++ .../uart/uart_pm/boards/lp_mspm0l2228.overlay | 26 +++++++++++++++++++ tests/drivers/uart/uart_pm/testcase.yaml | 9 +++++++ 3 files changed, 56 insertions(+) create mode 100644 tests/drivers/uart/uart_pm/boards/lp_mspm0g3519.overlay create mode 100644 tests/drivers/uart/uart_pm/boards/lp_mspm0l2228.overlay diff --git a/tests/drivers/uart/uart_pm/boards/lp_mspm0g3519.overlay b/tests/drivers/uart/uart_pm/boards/lp_mspm0g3519.overlay new file mode 100644 index 000000000000..fcfb7c704ed0 --- /dev/null +++ b/tests/drivers/uart/uart_pm/boards/lp_mspm0g3519.overlay @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* TX (PB4) and RX (PB5) must be connected together for loopback */ +/*zephyr,pm-device-runtime-auto;*/ +dut: &uart1 { + pinctrl-0 = <&uart1_tx_pb4 &uart1_rx_pb5>; + pinctrl-names = "default"; + current-speed = <9600>; + rx-fifo-threshold = ; + status = "okay"; +}; + +&cpu0 { + cpu-power-states = <&run0 &run1 &run2>; +}; diff --git a/tests/drivers/uart/uart_pm/boards/lp_mspm0l2228.overlay b/tests/drivers/uart/uart_pm/boards/lp_mspm0l2228.overlay new file mode 100644 index 000000000000..9ac0ad886292 --- /dev/null +++ b/tests/drivers/uart/uart_pm/boards/lp_mspm0l2228.overlay @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2026 Texas Instruments Incorporated + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +/* TX (PB4) and RX (PB5) must be connected together for loopback */ + +dut: &uart1 { + rx-fifo-threshold = ; + pinctrl-0 = <&uart1_tx_pb4 &uart1_rx_pb5>; + pinctrl-names = "default"; + status = "okay"; + current-speed = <9600>; + zephyr,pm-device-runtime-auto; +}; + +&timg8 { + status = "okay"; +}; + +counter_dev: &counterg8 { + status = "okay"; +}; diff --git a/tests/drivers/uart/uart_pm/testcase.yaml b/tests/drivers/uart/uart_pm/testcase.yaml index 6bb6c1bb6e46..25c07e983030 100644 --- a/tests/drivers/uart/uart_pm/testcase.yaml +++ b/tests/drivers/uart/uart_pm/testcase.yaml @@ -16,6 +16,8 @@ common: - nrf54lm20bsim/nrf54lm20a/cpuapp - nrf7120dk/nrf7120/cpuapp - ophelia4ev/nrf54l15/cpuapp + - lp_mspm0g3519 + - lp_mspm0l2228 harness_config: fixture: gpio_loopback depends_on: gpio @@ -45,6 +47,8 @@ tests: - nrf54lm20bsim/nrf54lm20a/cpuapp - nrf7120dk/nrf7120/cpuapp - ophelia4ev/nrf54l15/cpuapp + - lp_mspm0g3519 + - lp_mspm0l2228 drivers.uart.pm.enhanced_poll: extra_configs: @@ -85,6 +89,9 @@ tests: - CONFIG_UART_ASYNC_API=y - CONFIG_UART_0_ASYNC=y - CONFIG_UART_0_ENHANCED_POLL_OUT=n + platform_exclude: + - lp_mspm0g3519 + - lp_mspm0l2228 drivers.uart.pm.async.enhanced_poll: extra_configs: @@ -99,3 +106,5 @@ tests: - nrf54lm20dk/nrf54lm20b/cpuapp - nrf7120dk/nrf7120/cpuapp - ophelia4ev/nrf54l15/cpuapp + - lp_mspm0g3519 + - lp_mspm0l2228 From 91887eadadc9791f990aa11d151138cee86eac74 Mon Sep 17 00:00:00 2001 From: Franklin Cooper Jr Date: Tue, 8 Sep 2026 11:46:15 -0500 Subject: [PATCH 13/13] UPSTREAM-PEND: tests: uart_mix_fifo_poll - Add support for TI G3519 & L2... PR: 122748 Add support for TI's lp_mspm0g3519 and lp_mspm0l2228 board. Signed-off-by: Franklin Cooper Jr --- tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.conf | 2 ++ .../uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.overlay | 1 + tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.conf | 2 ++ .../uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.overlay | 1 + 4 files changed, 6 insertions(+) diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.conf b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.conf index d94b91b6981d..a731d8916433 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.conf +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.conf @@ -1,4 +1,6 @@ CONFIG_STRESS_TEST_REPS=1000 +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y # On Cortex-M0+ _IRQ_PRIO_OFFSET is 0, so SysTick shares the highest NVIC # priority with all peripherals. The k_timer expiry (uart_poll_out() spinning # on a full TX FIFO) then starves the counter ISR that drains the 4-deep RX diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.overlay index d6a6db85554f..2b451368884c 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.overlay +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0g3519.overlay @@ -14,6 +14,7 @@ dut: &uart1 { current-speed = <9600>; rx-fifo-threshold = ; status = "okay"; + zephyr,pm-device-runtime-auto; }; counter_dev: &counterg6 { diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.conf b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.conf index d94b91b6981d..a731d8916433 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.conf +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.conf @@ -1,4 +1,6 @@ CONFIG_STRESS_TEST_REPS=1000 +CONFIG_PM_DEVICE=y +CONFIG_PM_DEVICE_RUNTIME=y # On Cortex-M0+ _IRQ_PRIO_OFFSET is 0, so SysTick shares the highest NVIC # priority with all peripherals. The k_timer expiry (uart_poll_out() spinning # on a full TX FIFO) then starves the counter ISR that drains the 4-deep RX diff --git a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.overlay b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.overlay index 223808054a60..4ce21a9d3b12 100644 --- a/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.overlay +++ b/tests/drivers/uart/uart_mix_fifo_poll/boards/lp_mspm0l2228.overlay @@ -13,6 +13,7 @@ dut: &uart1 { pinctrl-0 = <&uart1_tx_pb4 &uart1_rx_pb5>; pinctrl-names = "default"; status = "okay"; + zephyr,pm-device-runtime-auto; }; &timg8 {