1 /*
2  * Copyright (c) 2020 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <errno.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <soc.h>
11 #include <zephyr/pm/pm.h>
12 #include <zephyr/logging/log.h>
13 #define LOG_LEVEL LOG_LEVEL_DBG
14 LOG_MODULE_REGISTER(pwrmgmt_test);
15 
16 #define SLP_STATES_SUPPORTED      2ul
17 
18 /* Thread properties */
19 #define TASK_STACK_SIZE           1024ul
20 #define PRIORITY                  K_PRIO_COOP(5)
21 /* Sleep time should be lower than SUSPEND_TO_IDLE residency time */
22 #define THREAD_A_SLEEP_TIME       100ul
23 #define THREAD_B_SLEEP_TIME       1000ul
24 
25 /* Maximum latency should be less than 300 ms */
26 #define MAX_EXPECTED_MS_LATENCY   500ul
27 
28 /* Sleep some extra time than minimum residency */
29 #define DP_EXTRA_SLP_TIME         1100ul
30 #define LT_EXTRA_SLP_TIME         500ul
31 
32 #define SEC_TO_MSEC               1000ul
33 
34 K_THREAD_STACK_DEFINE(stack_a, TASK_STACK_SIZE);
35 K_THREAD_STACK_DEFINE(stack_b, TASK_STACK_SIZE);
36 
37 static struct k_thread thread_a_id;
38 static struct k_thread thread_b_id;
39 
40 struct pm_counter {
41 	uint8_t entry_cnt;
42 	uint8_t exit_cnt;
43 };
44 
45 /* Track time elapsed */
46 static int64_t trigger_time;
47 static bool checks_enabled;
48 /* Track entry/exit to sleep */
49 struct pm_counter pm_counters[SLP_STATES_SUPPORTED];
50 
51 static const struct pm_state_info residency_info[] =
52 	PM_STATE_INFO_LIST_FROM_DT_CPU(DT_NODELABEL(cpu0));
53 static size_t residency_info_len = DT_NUM_CPU_POWER_STATES(DT_NODELABEL(cpu0));
54 
55 
56 /* Instrumentation to measure latency and track entry exit via gpios
57  *
58  * In EVB set following jumpers:
59  * JP99 7-8     closed
60  * JP99 10-11   closed
61  * JP75 29-30   closed
62  * JP75 32-33   closed
63  *
64  * In EVB probe following pins:
65  * JP25.3 (GPIO012_LT) light sleep
66  * JP25.5 (GPIO013_DP) deep sleep
67  * JP25.7 (GPIO014_TRIG) trigger in app
68  * JP75.29 (GPIO60_CLK_OUT)
69  */
70 
pm_latency_check(void)71 static void pm_latency_check(void)
72 {
73 	int64_t latency;
74 	int secs;
75 	int msecs;
76 
77 	latency = k_uptime_delta(&trigger_time);
78 	secs = (int)(latency / SEC_TO_MSEC);
79 	msecs = (int)(latency % SEC_TO_MSEC);
80 	LOG_INF("PM sleep entry latency %d.%03d seconds", secs, msecs);
81 
82 	if (secs > 0) {
83 		LOG_WRN("Sleep entry latency is too high");
84 		return;
85 	}
86 
87 	if (msecs > MAX_EXPECTED_MS_LATENCY) {
88 		LOG_WRN("Sleep entry latency is higher than expected");
89 	}
90 }
91 
92 /* Hooks to count entry/exit */
notify_pm_state_entry(enum pm_state state)93 static void notify_pm_state_entry(enum pm_state state)
94 {
95 	if (!checks_enabled) {
96 		return;
97 	}
98 
99 	switch (state) {
100 	case PM_STATE_SUSPEND_TO_IDLE:
101 		GPIO_CTRL_REGS->CTRL_0012 = 0x240ul;
102 		pm_counters[0].entry_cnt++;
103 		break;
104 	case PM_STATE_SUSPEND_TO_RAM:
105 		GPIO_CTRL_REGS->CTRL_0013 = 0x240ul;
106 		pm_counters[1].entry_cnt++;
107 		pm_latency_check();
108 		break;
109 	default:
110 		break;
111 	}
112 }
113 
notify_pm_state_exit(enum pm_state state)114 static void notify_pm_state_exit(enum pm_state state)
115 {
116 	if (!checks_enabled) {
117 		return;
118 	}
119 
120 	switch (state) {
121 	case PM_STATE_SUSPEND_TO_IDLE:
122 		GPIO_CTRL_REGS->CTRL_0012 = 0x10240ul;
123 		pm_counters[0].exit_cnt++;
124 		break;
125 	case PM_STATE_SUSPEND_TO_RAM:
126 		GPIO_CTRL_REGS->CTRL_0013 = 0x10240ul;
127 		pm_counters[1].exit_cnt++;
128 		break;
129 	default:
130 		break;
131 	}
132 }
133 
pm_check_counters(uint8_t cycles)134 static void pm_check_counters(uint8_t cycles)
135 {
136 	for (int i = 0; i < SLP_STATES_SUPPORTED; i++) {
137 		LOG_INF("PM state[%d] entry counter %d", i,
138 			pm_counters[i].entry_cnt);
139 		LOG_INF("PM state[%d] exit counter %d", i,
140 			pm_counters[i].exit_cnt);
141 
142 		if (pm_counters[i].entry_cnt != pm_counters[i].exit_cnt) {
143 			LOG_WRN("PM counters entry/exit mismatch");
144 		}
145 
146 		if (pm_counters[i].entry_cnt != cycles) {
147 			LOG_WRN("PM counter mismatch expected: %d", cycles);
148 		}
149 
150 		pm_counters[i].entry_cnt = 0;
151 		pm_counters[i].exit_cnt = 0;
152 	}
153 }
154 
pm_reset_counters(void)155 static void pm_reset_counters(void)
156 {
157 	for (int i = 0; i < SLP_STATES_SUPPORTED; i++) {
158 		pm_counters[i].entry_cnt = 0;
159 		pm_counters[i].exit_cnt = 0;
160 	}
161 
162 	checks_enabled = false;
163 	GPIO_CTRL_REGS->CTRL_0014 = 0x10240UL;
164 }
165 
pm_trigger_marker(void)166 static void pm_trigger_marker(void)
167 {
168 	trigger_time = k_uptime_get();
169 
170 	/* Directly access a pin to mark sleep trigger */
171 	GPIO_CTRL_REGS->CTRL_0014 = 0x00240UL;
172 	printk("PM >\n");
173 }
174 
pm_exit_marker(void)175 static void pm_exit_marker(void)
176 {
177 	int64_t residency_delta;
178 	int secs;
179 	int msecs;
180 
181 	/* Directly access a pin */
182 	GPIO_CTRL_REGS->CTRL_0014 = 0x10240UL;
183 	printk("PM <\n");
184 
185 	if (trigger_time > 0) {
186 		residency_delta = k_uptime_delta(&trigger_time);
187 		secs = (int)(residency_delta / SEC_TO_MSEC);
188 		msecs = (int)(residency_delta % SEC_TO_MSEC);
189 		LOG_INF("PM sleep residency %d.%03d seconds", secs, msecs);
190 	}
191 }
192 
task_a_init(void)193 static int task_a_init(void)
194 {
195 	LOG_INF("Thread task A init");
196 
197 	return 0;
198 }
199 
task_b_init(void)200 static int task_b_init(void)
201 {
202 	printk("Thread task B init");
203 
204 	return 0;
205 }
206 
task_a_thread(void * p1,void * p2,void * p3)207 void task_a_thread(void *p1, void *p2, void *p3)
208 {
209 	while (true) {
210 		k_msleep(THREAD_A_SLEEP_TIME);
211 		printk("A");
212 	}
213 }
214 
task_b_thread(void * p1,void * p2,void * p3)215 static void task_b_thread(void *p1, void *p2, void *p3)
216 {
217 	while (true) {
218 		k_msleep(THREAD_B_SLEEP_TIME);
219 		printk("B");
220 	}
221 }
222 
create_tasks(void)223 static void create_tasks(void)
224 {
225 	task_a_init();
226 	task_b_init();
227 
228 	k_thread_create(&thread_a_id, stack_a, TASK_STACK_SIZE, task_a_thread,
229 		NULL, NULL, NULL, PRIORITY,  K_INHERIT_PERMS, K_FOREVER);
230 	k_thread_create(&thread_b_id, stack_b, TASK_STACK_SIZE, task_b_thread,
231 		NULL, NULL, NULL, PRIORITY,  K_INHERIT_PERMS, K_FOREVER);
232 
233 	k_thread_start(&thread_a_id);
234 	k_thread_start(&thread_b_id);
235 
236 }
237 
destroy_tasks(void)238 static void destroy_tasks(void)
239 {
240 	k_thread_abort(&thread_a_id);
241 	k_thread_abort(&thread_b_id);
242 
243 	k_thread_join(&thread_a_id, K_FOREVER);
244 	k_thread_join(&thread_b_id, K_FOREVER);
245 }
246 
suspend_all_tasks(void)247 static void suspend_all_tasks(void)
248 {
249 	k_thread_suspend(&thread_a_id);
250 	k_thread_suspend(&thread_b_id);
251 }
252 
resume_all_tasks(void)253 static void resume_all_tasks(void)
254 {
255 	k_thread_resume(&thread_a_id);
256 	k_thread_resume(&thread_b_id);
257 }
258 
259 static struct pm_notifier notifier = {
260 	.state_entry = notify_pm_state_entry,
261 	.state_exit = notify_pm_state_exit,
262 };
263 
test_pwr_mgmt_multithread(bool use_logging,uint8_t cycles)264 int test_pwr_mgmt_multithread(bool use_logging, uint8_t cycles)
265 {
266 	uint8_t iterations = cycles;
267 	/* Ensure we can enter deep sleep when stopping threads
268 	 * No UART output should occur when threads are suspended
269 	 * Test to verify Zephyr RTOS issue #20033
270 	 * https://github.com/zephyrproject-rtos/zephyr/issues/20033
271 	 */
272 
273 	pm_notifier_register(&notifier);
274 	create_tasks();
275 
276 	LOG_WRN("PM multi-thread test started for cycles: %d, logging: %d",
277 		cycles, use_logging);
278 
279 	checks_enabled = true;
280 	while (iterations-- > 0) {
281 
282 		/* Light sleep cycle */
283 		LOG_INF("Suspend...");
284 		suspend_all_tasks();
285 		LOG_INF("About to enter light sleep");
286 		k_msleep((residency_info[0].min_residency_us / 1000U) +
287 			 LT_EXTRA_SLP_TIME);
288 		k_busy_wait(100);
289 
290 		if (use_logging) {
291 			LOG_INF("Wake from Light Sleep");
292 		} else {
293 			printk("Wake from Light Sleep\n");
294 		}
295 
296 		LOG_INF("Resume");
297 		resume_all_tasks();
298 
299 		/* Deep sleep cycle */
300 		LOG_INF("Suspend...");
301 		suspend_all_tasks();
302 		LOG_INF("About to enter deep sleep");
303 
304 		/* GPIO toggle to measure latency for deep sleep */
305 		pm_trigger_marker();
306 		k_msleep(
307 		   (residency_info[residency_info_len - 1].min_residency_us /
308 		    1000U) + DP_EXTRA_SLP_TIME);
309 		k_busy_wait(100);
310 
311 		if (use_logging) {
312 			LOG_INF("Wake from Deep Sleep");
313 		} else {
314 			printk("Wake from Deep Sleep\n");
315 		}
316 
317 		pm_exit_marker();
318 		LOG_INF("Resume");
319 		resume_all_tasks();
320 	}
321 
322 	destroy_tasks();
323 
324 	LOG_INF("PM multi-thread completed");
325 	pm_check_counters(cycles);
326 	pm_reset_counters();
327 	pm_notifier_unregister(&notifier);
328 
329 	return 0;
330 }
331 
test_pwr_mgmt_singlethread(bool use_logging,uint8_t cycles)332 int test_pwr_mgmt_singlethread(bool use_logging, uint8_t cycles)
333 {
334 	uint8_t iterations = cycles;
335 
336 	LOG_WRN("PM single-thread test started for cycles: %d, logging: %d",
337 		cycles, use_logging);
338 
339 	pm_notifier_register(&notifier);
340 	checks_enabled = true;
341 	while (iterations-- > 0) {
342 
343 		/* Trigger Light Sleep 1 state. 48MHz PLL stays on */
344 		LOG_INF("About to enter light sleep");
345 		k_msleep((residency_info[0].min_residency_us / 1000U) +
346 			 LT_EXTRA_SLP_TIME);
347 		k_busy_wait(100);
348 
349 		if (use_logging) {
350 			LOG_INF("Wake from Light Sleep");
351 		} else {
352 			printk("Wake from Light Sleep\n");
353 		}
354 
355 		/* Trigger Deep Sleep 1 state. 48MHz PLL off */
356 		LOG_INF("About to enter deep Sleep");
357 
358 		/* GPIO toggle to measure latency */
359 		pm_trigger_marker();
360 		k_msleep(
361 		   (residency_info[residency_info_len - 1].min_residency_us /
362 		    1000U) + DP_EXTRA_SLP_TIME);
363 		k_busy_wait(100);
364 
365 		if (use_logging) {
366 			LOG_INF("Wake from Deep Sleep");
367 		} else {
368 			printk("Wake from Deep Sleep\n");
369 		}
370 
371 		pm_exit_marker();
372 	}
373 
374 	LOG_INF("PM single-thread completed");
375 	pm_check_counters(cycles);
376 	pm_reset_counters();
377 	pm_notifier_unregister(&notifier);
378 
379 	return 0;
380 }
381