77#include <zephyr/device.h>
88#include <zephyr/init.h>
99#include <zephyr/kernel.h>
10+ #include <zephyr/pm/device.h>
11+ #include <zephyr/pm/device_runtime.h>
1012#include <zephyr/sys/poweroff.h>
1113
1214#include <zephyr/logging/log.h>
@@ -24,6 +26,63 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
2426#include <zmk/usb.h>
2527#endif
2628
29+ // Reimplement some of the device work from Zephyr PM to work with the new `sys_poweroff` API.
30+ // TODO: Tweak this to smarter runtime PM of subsystems on sleep.
31+
32+ #ifdef CONFIG_PM_DEVICE
33+ TYPE_SECTION_START_EXTERN (const struct device * , zmk_pm_device_slots );
34+
35+ #if !defined(CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE )
36+ /* Number of devices successfully suspended. */
37+ static size_t zmk_num_susp ;
38+
39+ static int zmk_pm_suspend_devices (void ) {
40+ const struct device * devs ;
41+ size_t devc ;
42+
43+ devc = z_device_get_all_static (& devs );
44+
45+ zmk_num_susp = 0 ;
46+
47+ for (const struct device * dev = devs + devc - 1 ; dev >= devs ; dev -- ) {
48+ int ret ;
49+
50+ /*
51+ * Ignore uninitialized devices, busy devices, wake up sources, and
52+ * devices with runtime PM enabled.
53+ */
54+ if (!device_is_ready (dev ) || pm_device_is_busy (dev ) || pm_device_state_is_locked (dev ) ||
55+ pm_device_wakeup_is_enabled (dev ) || pm_device_runtime_is_enabled (dev )) {
56+ continue ;
57+ }
58+
59+ ret = pm_device_action_run (dev , PM_DEVICE_ACTION_SUSPEND );
60+ /* ignore devices not supporting or already at the given state */
61+ if ((ret == - ENOSYS ) || (ret == - ENOTSUP ) || (ret == - EALREADY )) {
62+ continue ;
63+ } else if (ret < 0 ) {
64+ LOG_ERR ("Device %s did not enter %s state (%d)" , dev -> name ,
65+ pm_device_state_str (PM_DEVICE_STATE_SUSPENDED ), ret );
66+ return ret ;
67+ }
68+
69+ TYPE_SECTION_START (zmk_pm_device_slots )[zmk_num_susp ] = dev ;
70+ zmk_num_susp ++ ;
71+ }
72+
73+ return 0 ;
74+ }
75+
76+ static void zmk_pm_resume_devices (void ) {
77+ for (int i = (zmk_num_susp - 1 ); i >= 0 ; i -- ) {
78+ pm_device_action_run (TYPE_SECTION_START (zmk_pm_device_slots )[i ], PM_DEVICE_ACTION_RESUME );
79+ }
80+
81+ zmk_num_susp = 0 ;
82+ }
83+ #endif /* !CONFIG_PM_DEVICE_RUNTIME_EXCLUSIVE */
84+ #endif /* CONFIG_PM_DEVICE */
85+
2786bool is_usb_power_present (void ) {
2887#if IS_ENABLED (CONFIG_USB_DEVICE_STACK )
2988 return zmk_usb_is_powered ();
@@ -70,12 +129,19 @@ void activity_work_handler(struct k_work *work) {
70129 if (inactive_time > MAX_SLEEP_MS && !is_usb_power_present ()) {
71130 // Put devices in suspend power mode before sleeping
72131 set_state (ZMK_ACTIVITY_SLEEP );
132+
133+ if (zmk_pm_suspend_devices () < 0 ) {
134+ LOG_ERR ("Failed to suspend all the devices" );
135+ zmk_pm_resume_devices ();
136+ return ;
137+ }
138+
73139 sys_poweroff ();
74140 } else
75141#endif /* IS_ENABLED(CONFIG_ZMK_SLEEP) */
76142 if (inactive_time > MAX_IDLE_MS ) {
77- set_state (ZMK_ACTIVITY_IDLE );
78- }
143+ set_state (ZMK_ACTIVITY_IDLE );
144+ }
79145}
80146
81147K_WORK_DEFINE (activity_work , activity_work_handler );
0 commit comments