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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 234 additions & 49 deletions drivers/serial/uart_mspm0.c

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0g.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@
current-speed = <115200>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand All @@ -241,6 +242,7 @@
current-speed = <115200>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand All @@ -251,6 +253,7 @@
current-speed = <115200>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand Down
10 changes: 9 additions & 1 deletion dts/arm/ti/mspm0/g/mspm0g110x.dtsi
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* SPDX-FileCopyrightText: 2026 Texas Instruments Incorporated
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <ti/mspm0/g/mspm0g.dtsi>

&uart3 {
zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>;
};
4 changes: 4 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0g150x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@

#include <ti/mspm0/g/mspm0g.dtsi>

&uart3 {
zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>;
};

/ {
soc {
dac0: dac@40018000 {
Expand Down
10 changes: 9 additions & 1 deletion dts/arm/ti/mspm0/g/mspm0g310x.dtsi
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
/* SPDX-License-Identifier: Apache-2.0 */
/*
* SPDX-FileCopyrightText: 2026 Texas Instruments Incorporated
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <ti/mspm0/g/mspm0g.dtsi>

&uart3 {
zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>;
};

/ {
soc {
aes: aes@40442000 {
Expand Down
4 changes: 4 additions & 0 deletions dts/arm/ti/mspm0/g/mspm0gx51x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -229,3 +229,7 @@
<&gpio_none>,
<&gpio_none>;
};

&uart3 {
zephyr,disabling-power-states = <&stop0 &stop1 &stop2 &stdby0 &stdby1>;
};
1 change: 1 addition & 0 deletions dts/arm/ti/mspm0/l/mspm0l110x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
current-speed = <115200>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};
};
Expand Down
4 changes: 4 additions & 0 deletions dts/arm/ti/mspm0/l/mspm0lx22x.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
current-speed = <9600>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand All @@ -183,6 +184,7 @@
current-speed = <9600>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand All @@ -193,6 +195,7 @@
current-speed = <9600>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand All @@ -203,6 +206,7 @@
current-speed = <9600>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand Down
1 change: 1 addition & 0 deletions dts/arm/ti/mspm0/mspm0.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
current-speed = <115200>;
clocks = <&ckm MSPM0_CLOCK_ULPCLK>;
clk-div = <1>;
zephyr,disabling-power-states = <&stdby1>;
status = "disabled";
};

Expand Down
125 changes: 113 additions & 12 deletions subsys/pm/device_runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ 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
K_THREAD_STACK_DEFINE(pm_device_runtime_stack, CONFIG_PM_DEVICE_RUNTIME_DEDICATED_WQ_STACK_SIZE);
Expand All @@ -33,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
*
Expand Down Expand Up @@ -65,6 +148,11 @@ static int runtime_suspend(const struct device *dev, bool async,
return 0;
}

/* If we are not the last user, return. */
if (runtime_usage_put_fast(pm)) {
return 0;
}

if (k_is_pre_kernel()) {
async = false;
} else {
Expand All @@ -74,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 */
Expand All @@ -99,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;
}

Expand Down Expand Up @@ -131,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;
Expand Down Expand Up @@ -219,6 +305,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;
Expand Down Expand Up @@ -251,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
/*
Expand Down Expand Up @@ -287,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);
Expand Down
2 changes: 2 additions & 0 deletions tests/drivers/uart/uart_elementary/boards/lp_mspm0g3519.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ dut: &uart1 {
current-speed = <115200>;
hw-flow-control;
status = "okay";
zephyr,pm-device-runtime-auto;
};
2 changes: 2 additions & 0 deletions tests/drivers/uart/uart_elementary/boards/lp_mspm0l2228.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
pinctrl-names = "default";
hw-flow-control;
status = "okay";
zephyr,pm-device-runtime-auto;
};
};
};
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dut: &uart1 {
current-speed = <9600>;
rx-fifo-threshold = <MSPM0_UART_RX_FIFO_LEVEL_ONE_ENTRY>;
status = "okay";
zephyr,pm-device-runtime-auto;
};

counter_dev: &counterg6 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
21 changes: 21 additions & 0 deletions tests/drivers/uart/uart_pm/boards/lp_mspm0g3519.overlay
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2026 Texas Instruments Incorporated
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/dt-bindings/uart/mspm0_uart.h>

/* 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 = <MSPM0_UART_RX_FIFO_LEVEL_ONE_ENTRY>;
status = "okay";
};

&cpu0 {
cpu-power-states = <&run0 &run1 &run2>;
};
Loading
Loading