zephyrproject-rtos / zephyr

Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
https://docs.zephyrproject.org
Apache License 2.0
10.6k stars 6.49k forks source link

Support of uart power management on serial logging #67266

Open mjm987 opened 8 months ago

mjm987 commented 8 months ago

Is your enhancement proposal related to a problem? Please describe. Normally when using serial uart logging, the uart drains battery in mA range during cpu sleep because of active uart clocking. Beside of disabling console logging completely, a workaround is to set the uart to PM_DEVICE_ACTION_SUSPEND before any application k_sleep() and performing uart PM_DEVICE_ACTION_RESUME therafter, but this way any logging from driver ISRs, callbacks and threads are lost.

Describe the solution you'd like Add power management hooks in the system logging thread or alternatively a CONFIG_ option which activates execution of pm_device_action_run(console_uart, PM_DEVICE_ACTION_SUSPEND/RESUME) in the system logging thread before/after log output.

mjm987 commented 8 months ago

In case the console is a real uart device and used for logging only following hack seems to work fine for nrf devices:

diff --git a/subsys/logging/log_core.c b/subsys/logging/log_core.c
index 8e7f739d0c..f3b6de9816 100644
--- a/subsys/logging/log_core.c
+++ b/subsys/logging/log_core.c
@@ -22,6 +22,7 @@
 #include <zephyr/logging/log_output_dict.h>
 #include <zephyr/logging/log_output_custom.h>
 #include <zephyr/linker/utils.h>
+#include <zephyr/pm/device.h>

 LOG_MODULE_REGISTER(log);

@@ -895,7 +896,10 @@ static void log_process_thread_func(void *dummy1, void *dummy2, void *dummy3)
                                processed_any = false;
                                log_backend_notify_all(LOG_BACKEND_EVT_PROCESS_THREAD_DONE, NULL);
                        }
+                      const struct device *console_uart = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+                      pm_device_action_run(console_uart, PM_DEVICE_ACTION_SUSPEND);
                        (void)k_sem_take(&log_process_thread_sem, timeout);
+                      pm_device_action_run(console_uart, PM_DEVICE_ACTION_RESUME);
                } else {
                        processed_any = true;
                }
~

Of course a clean solution should be configurable via a CONFIG_ option and consistancy checked.