1 /*
2  * Copyright 2025 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/pm/pm.h>
10 #include <zephyr/pm/policy.h>
11 #include <zephyr/logging/log.h>
12 
13 LOG_MODULE_REGISTER(pm_cpu_shell, CONFIG_PM_LOG_LEVEL);
14 
15 /* Supported states info from devicetree (CPU0) */
16 static const struct pm_state_info residency_info[] =
17 	PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
18 
cmd_cpu_states(const struct shell * sh,size_t argc,char ** argv)19 static int cmd_cpu_states(const struct shell *sh, size_t argc, char **argv)
20 {
21 	ARG_UNUSED(argc);
22 	ARG_UNUSED(argv);
23 
24 	/* sanity check for first */
25 	if (ARRAY_SIZE(residency_info) == 0U) {
26 		shell_warn(sh, "No pm states");
27 		return -EINVAL;
28 	}
29 
30 	shell_print(sh, "Supported Low Power States:");
31 
32 	ARRAY_FOR_EACH_PTR(residency_info, state_info) {
33 		shell_print(sh,
34 		"  - State: %s, Substate: %d, Residency: %dus, Latency: %dus, PM Device Disabled: %s",
35 			pm_state_to_str(state_info->state),
36 			state_info->substate_id,
37 			state_info->min_residency_us,
38 			state_info->exit_latency_us,
39 			state_info->pm_device_disabled ? "Yes" : "No");
40 	}
41 
42 	return 0;
43 }
44 
cmd_cpu_available(const struct shell * sh,size_t argc,char ** argv)45 static int cmd_cpu_available(const struct shell *sh, size_t argc, char **argv)
46 {
47 	ARG_UNUSED(argc);
48 	ARG_UNUSED(argv);
49 
50 	/* sanity check for first */
51 	if (ARRAY_SIZE(residency_info) == 0U) {
52 		shell_warn(sh, "No pm states");
53 		return -EINVAL;
54 	}
55 
56 	bool available;
57 	bool locked;
58 
59 	shell_print(sh, "Check whether the low power states of the current core are supported:");
60 
61 	ARRAY_FOR_EACH_PTR(residency_info, state_info) {
62 		available = pm_policy_state_is_available(state_info->state,
63 				state_info->substate_id);
64 		locked = pm_policy_state_lock_is_active(state_info->state,
65 				state_info->substate_id);
66 
67 		shell_print(sh, " - %-16s sub=%-3u avail=%c lock=%c",
68 				pm_state_to_str(state_info->state),
69 				state_info->substate_id,
70 				available ? 'Y' : 'N',
71 				locked ? 'Y' : 'N');
72 	}
73 
74 	return 0;
75 }
76 
cmd_cpu_lock(const struct shell * sh,size_t argc,char ** argv)77 static int cmd_cpu_lock(const struct shell *sh, size_t argc, char **argv)
78 {
79 	ARG_UNUSED(argc);
80 
81 	enum pm_state st;
82 	uint8_t sub;
83 	int err;
84 
85 	if (pm_state_from_str(argv[1], &st) < 0) {
86 		shell_error(sh, "Unknown state: %s", argv[1]);
87 		return -EINVAL;
88 	}
89 
90 	sub = shell_strtoul(argv[2], 0, &err);
91 	if (err < 0) {
92 		shell_error(sh, "Unable to parse input (err %d), substate", err);
93 		return err;
94 	}
95 
96 	pm_policy_state_lock_get(st, sub);
97 
98 	shell_print(sh, "Locked %s sub=%u", argv[1], sub);
99 
100 	return 0;
101 }
102 
cmd_cpu_unlock(const struct shell * sh,size_t argc,char ** argv)103 static int cmd_cpu_unlock(const struct shell *sh, size_t argc, char **argv)
104 {
105 	ARG_UNUSED(argc);
106 
107 	enum pm_state st;
108 	uint8_t sub;
109 	int err = 0;
110 
111 	if (pm_state_from_str(argv[1], &st) < 0) {
112 		shell_error(sh, "Unknown state: %s", argv[1]);
113 		return -EINVAL;
114 	}
115 
116 	sub = shell_strtoul(argv[2], 0, &err);
117 	if (err < 0) {
118 		shell_error(sh, "Unable to parse input (err %d), substate", err);
119 		return err;
120 	}
121 
122 	pm_policy_state_lock_put(st, sub);
123 
124 	shell_print(sh, "Unlocked %s sub=%u", argv[1], sub);
125 
126 	return 0;
127 }
128 
cmd_cpu_idle(const struct shell * sh,size_t argc,char ** argv)129 static int cmd_cpu_idle(const struct shell *sh, size_t argc, char **argv)
130 {
131 	ARG_UNUSED(argc);
132 
133 	int err;
134 	uint32_t ms;
135 
136 	ms = shell_strtoul(argv[1], 0, &err);
137 	if (err < 0) {
138 		shell_error(sh, "Unable to parse input (err %d), times", err);
139 		return err;
140 	}
141 
142 	k_msleep(ms);
143 
144 	shell_print(sh, "Woke up");
145 
146 	return 0;
147 }
148 
149 SHELL_STATIC_SUBCMD_SET_CREATE(
150 	cpu_cmds,
151 	SHELL_CMD_ARG(states,    NULL,
152 		SHELL_HELP("List supported CPU low power states", ""), cmd_cpu_states, 1, 0),
153 	SHELL_CMD_ARG(available, NULL,
154 		SHELL_HELP("Show availability/locks for each state", ""), cmd_cpu_available, 1, 0),
155 	SHELL_CMD_ARG(lock,      NULL,
156 		SHELL_HELP("Lock a state", "<state> <substate>"), cmd_cpu_lock, 2, 1),
157 	SHELL_CMD_ARG(unlock,    NULL,
158 		SHELL_HELP("Unlock a state", "<state> <substate>"), cmd_cpu_unlock, 2, 1),
159 	SHELL_CMD_ARG(idle,      NULL,
160 		SHELL_HELP("Sleep current thread to let PM work", "<ms>"), cmd_cpu_idle, 2, 0),
161 	SHELL_SUBCMD_SET_END
162 );
163 
164 SHELL_CMD_REGISTER(cpu, &cpu_cmds, "CPU core and power state commands", NULL);
165