1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/ztest.h>
9 #include <zephyr/pm/pm.h>
10 #include <zephyr/logging/log.h>
11 #define LOG_LEVEL LOG_LEVEL_DBG
12 LOG_MODULE_REGISTER(pwrmgmt_test);
13 
14 /* Thread properties */
15 #undef TASK_STACK_SIZE
16 #define TASK_STACK_SIZE           1024ul
17 #define PRIORITY                  K_PRIO_COOP(5)
18 
19 /* Sleep time should be lower than SUSPEND_TO_IDLE */
20 #define THREAD_A_SLEEP_TIME       100ul
21 #define THREAD_B_SLEEP_TIME       1000ul
22 
23 /* Maximum latency should be less than 500 ms */
24 #define MAX_EXPECTED_MS_LATENCY   500ul
25 
26 /* Sleep some extra time than minimum residency:
27  * - for light it should be very little so that we get only into light sleep
28  *   and not accidentally into a deep sleep.
29  * - for deep sleep it can be very long as we want to ensure that we enter
30  *   the deepest sleep state possible.
31  */
32 #define LT_EXTRA_SLP_TIME_US      50ul
33 #define DP_EXTRA_SLP_TIME_US      1100000ul
34 
35 #define SEC_TO_MSEC               1000ul
36 
37 K_THREAD_STACK_DEFINE(stack_a, TASK_STACK_SIZE);
38 K_THREAD_STACK_DEFINE(stack_b, TASK_STACK_SIZE);
39 
40 static struct k_thread thread_a_id;
41 static struct k_thread thread_b_id;
42 
43 struct pm_counter {
44 	uint8_t entry_cnt;
45 	uint8_t exit_cnt;
46 };
47 
48 /* Track time elapsed */
49 static int64_t trigger_time;
50 static bool checks_enabled;
51 static bool measure_entry_latency;
52 /* Track entry/exit to sleep */
53 struct pm_counter pm_counters[PM_STATE_COUNT];
54 
55 static const struct pm_state_info residency_info[] =
56 	PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
57 static size_t residency_info_len = DT_NUM_CPU_POWER_STATES(DT_NODELABEL(cpu0));
58 
pm_latency_check(void)59 static void pm_latency_check(void)
60 {
61 	int64_t latency;
62 	int secs;
63 	int msecs;
64 
65 	latency = k_uptime_get() - trigger_time;
66 	secs = (int)(latency / SEC_TO_MSEC);
67 	msecs = (int)(latency % SEC_TO_MSEC);
68 
69 	zassert_false(secs > 0 || msecs > MAX_EXPECTED_MS_LATENCY,
70 			"Sleep entry latency is too high: %d.%03d s.",
71 			secs, msecs);
72 }
73 
notify_pm_state_entry(enum pm_state state)74 static void notify_pm_state_entry(enum pm_state state)
75 {
76 	if (!checks_enabled) {
77 		return;
78 	}
79 
80 	pm_counters[(int)state].entry_cnt++;
81 
82 	if (measure_entry_latency) {
83 		pm_latency_check();
84 		/* After first PM entry disable further checks since there can
85 		 * be multiple PM entry-exit events e.g. due to spurious
86 		 * wakeup
87 		 */
88 		measure_entry_latency = false;
89 	}
90 }
91 
notify_pm_state_exit(enum pm_state state)92 static void notify_pm_state_exit(enum pm_state state)
93 {
94 	if (!checks_enabled) {
95 		return;
96 	}
97 
98 	pm_counters[(int)state].exit_cnt++;
99 }
100 
101 static struct pm_notifier notifier = {
102 	.state_entry = notify_pm_state_entry,
103 	.state_exit = notify_pm_state_exit,
104 };
105 
pm_check_counters(uint8_t cycles)106 static void pm_check_counters(uint8_t cycles)
107 {
108 	for (int i = 0; i < PM_STATE_COUNT; i++) {
109 
110 		LOG_INF("PM state[%d] entry counter %d\n", i,
111 			pm_counters[i].entry_cnt);
112 		LOG_INF("PM state[%d] exit counter %d\n", i,
113 			pm_counters[i].exit_cnt);
114 
115 		zassert_equal(pm_counters[i].entry_cnt,
116 				pm_counters[i].exit_cnt,
117 				"PM counters entry/exit mismatch");
118 
119 		pm_counters[i].entry_cnt = 0;
120 		pm_counters[i].exit_cnt = 0;
121 	}
122 }
123 
pm_reset_counters(void)124 static void pm_reset_counters(void)
125 {
126 	for (int i = 0; i < PM_STATE_COUNT; i++) {
127 		pm_counters[i].entry_cnt = 0;
128 		pm_counters[i].exit_cnt = 0;
129 	}
130 
131 	checks_enabled = false;
132 	measure_entry_latency = false;
133 }
134 
pm_trigger_marker(void)135 static void pm_trigger_marker(void)
136 {
137 	trigger_time = k_uptime_get();
138 
139 	printk("PM >\n");
140 }
141 
pm_exit_marker(void)142 static void pm_exit_marker(void)
143 {
144 	int64_t residency_delta;
145 	int secs;
146 	int msecs;
147 
148 	printk("PM <\n");
149 
150 	if (trigger_time > 0) {
151 		residency_delta = k_uptime_get() - trigger_time;
152 		secs = (int)(residency_delta / SEC_TO_MSEC);
153 		msecs = (int)(residency_delta % SEC_TO_MSEC);
154 		LOG_INF("PM sleep residency %d.%03d seconds", secs, msecs);
155 	}
156 }
157 
task_a_init(void)158 static int task_a_init(void)
159 {
160 	LOG_INF("Thread task A init");
161 
162 	return 0;
163 }
164 
task_b_init(void)165 static int task_b_init(void)
166 {
167 	LOG_INF("Thread task B init");
168 
169 	return 0;
170 }
171 
task_a_thread(void * p1,void * p2,void * p3)172 void task_a_thread(void *p1, void *p2, void *p3)
173 {
174 	while (true) {
175 		k_msleep(THREAD_A_SLEEP_TIME);
176 		printk("A");
177 	}
178 }
179 
task_b_thread(void * p1,void * p2,void * p3)180 static void task_b_thread(void *p1, void *p2, void *p3)
181 {
182 	while (true) {
183 		k_msleep(THREAD_B_SLEEP_TIME);
184 		printk("B");
185 	}
186 }
187 
create_tasks(void)188 static void create_tasks(void)
189 {
190 	task_a_init();
191 	task_b_init();
192 
193 	k_thread_create(&thread_a_id, stack_a, TASK_STACK_SIZE, task_a_thread,
194 		NULL, NULL, NULL, PRIORITY,  K_INHERIT_PERMS, K_FOREVER);
195 	k_thread_create(&thread_b_id, stack_b, TASK_STACK_SIZE, task_b_thread,
196 		NULL, NULL, NULL, PRIORITY,  K_INHERIT_PERMS, K_FOREVER);
197 
198 	k_thread_start(&thread_a_id);
199 	k_thread_start(&thread_b_id);
200 
201 }
202 
destroy_tasks(void)203 static void destroy_tasks(void)
204 {
205 	k_thread_abort(&thread_a_id);
206 	k_thread_abort(&thread_b_id);
207 
208 	k_thread_join(&thread_a_id, K_FOREVER);
209 	k_thread_join(&thread_b_id, K_FOREVER);
210 }
211 
suspend_all_tasks(void)212 static void suspend_all_tasks(void)
213 {
214 	k_thread_suspend(&thread_a_id);
215 	k_thread_suspend(&thread_b_id);
216 }
217 
resume_all_tasks(void)218 static void resume_all_tasks(void)
219 {
220 	k_thread_resume(&thread_a_id);
221 	k_thread_resume(&thread_b_id);
222 }
223 
test_pwr_mgmt_multithread(uint8_t cycles)224 int test_pwr_mgmt_multithread(uint8_t cycles)
225 {
226 	uint8_t iterations = cycles;
227 
228 	pm_notifier_register(&notifier);
229 	create_tasks();
230 
231 	LOG_INF("PM multi-thread test started for cycles: %d", cycles);
232 
233 	checks_enabled = true;
234 	while (iterations-- > 0) {
235 
236 		/* Light sleep cycle */
237 		LOG_INF("Suspend...");
238 		suspend_all_tasks();
239 		LOG_INF("About to enter light sleep");
240 		measure_entry_latency = true;
241 		pm_trigger_marker();
242 		k_usleep(residency_info[0].min_residency_us +
243 				LT_EXTRA_SLP_TIME_US);
244 
245 		LOG_INF("Wake from Light Sleep");
246 		pm_exit_marker();
247 		LOG_INF("Resume");
248 		resume_all_tasks();
249 
250 		/* Deep sleep cycle */
251 		/* Platforms that do not automatically enter deep sleep */
252 		/* states in its residency policy will simply enter light */
253 		/* sleep states instead */
254 		LOG_INF("Suspend...");
255 		suspend_all_tasks();
256 		LOG_INF("About to enter deep sleep");
257 
258 		measure_entry_latency = true;
259 		pm_trigger_marker();
260 		k_usleep(
261 			residency_info[residency_info_len - 1].min_residency_us +
262 			DP_EXTRA_SLP_TIME_US);
263 
264 		LOG_INF("Wake from Deep Sleep");
265 		pm_exit_marker();
266 		LOG_INF("Resume");
267 		resume_all_tasks();
268 	}
269 
270 	destroy_tasks();
271 	pm_notifier_unregister(&notifier);
272 
273 	LOG_INF("PM multi-thread completed");
274 	pm_check_counters(cycles);
275 	pm_reset_counters();
276 
277 	return 0;
278 }
279 
test_pwr_mgmt_singlethread(uint8_t cycles)280 int test_pwr_mgmt_singlethread(uint8_t cycles)
281 {
282 	uint8_t iterations = cycles;
283 
284 	LOG_INF("PM single-thread test started for cycles: %d", cycles);
285 
286 	pm_notifier_register(&notifier);
287 	checks_enabled = true;
288 	while (iterations-- > 0) {
289 
290 		/* Trigger Light Sleep 1 state. 48MHz PLL stays on */
291 		LOG_INF("About to enter light sleep");
292 		measure_entry_latency = true;
293 		pm_trigger_marker();
294 		k_usleep(residency_info[0].min_residency_us +
295 				LT_EXTRA_SLP_TIME_US);
296 		LOG_INF("Wake from Light Sleep");
297 		pm_exit_marker();
298 
299 		/* Trigger Deep Sleep 1 state. 48MHz PLL off */
300 		/* Platforms that do not automatically enter deep sleep */
301 		/* states in its residency policy will simply enter light */
302 		/* sleep states instead */
303 		LOG_INF("About to enter deep Sleep");
304 		measure_entry_latency = true;
305 		pm_trigger_marker();
306 		k_usleep(
307 			residency_info[residency_info_len - 1].min_residency_us +
308 			DP_EXTRA_SLP_TIME_US);
309 		LOG_INF("Wake from Deep Sleep");
310 		pm_exit_marker();
311 	}
312 
313 	pm_notifier_unregister(&notifier);
314 	LOG_INF("PM single-thread completed");
315 	pm_check_counters(cycles);
316 	pm_reset_counters();
317 
318 	return 0;
319 }
320 
test_dummy_init(void)321 int test_dummy_init(void)
322 {
323 	uint8_t iterations = 1;
324 
325 	LOG_INF("PM dummy single-thread test started for one cycle");
326 
327 	checks_enabled = true;
328 	while (iterations-- > 0) {
329 		LOG_INF("About to enter light sleep");
330 		measure_entry_latency = true;
331 		pm_trigger_marker();
332 		k_usleep(residency_info[0].min_residency_us +
333 				LT_EXTRA_SLP_TIME_US);
334 		LOG_INF("Wake from Light Sleep");
335 		pm_exit_marker();
336 
337 		LOG_INF("About to enter deep Sleep");
338 		measure_entry_latency = true;
339 		pm_trigger_marker();
340 		k_usleep(
341 			residency_info[residency_info_len - 1].min_residency_us +
342 			DP_EXTRA_SLP_TIME_US);
343 		LOG_INF("Wake from Deep Sleep");
344 		pm_exit_marker();
345 	}
346 
347 	LOG_INF("PM dummy single-thread completed");
348 	pm_reset_counters();
349 	return 0;
350 }
351