1 /*
2 * Copyright (c) 2024 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/hwinfo.h>
8 #include <zephyr/drivers/watchdog.h>
9 #include <zephyr/sys/reboot.h>
10 #include <zephyr/cache.h>
11 #include <errno.h>
12
13 #include <zephyr/logging/log.h>
14 #include <zephyr/logging/log_ctrl.h>
15 LOG_MODULE_REGISTER(resetreason, LOG_LEVEL_INF);
16
17 static const struct device *const my_wdt_device = DEVICE_DT_GET(DT_ALIAS(watchdog0));
18 static struct wdt_timeout_cfg m_cfg_wdt0;
19 static int my_wdt_channel;
20
21 #define NOINIT_SECTION ".noinit.test_wdt"
22 volatile uint32_t machine_state __attribute__((section(NOINIT_SECTION)));
23 volatile uint32_t supported __attribute__((section(NOINIT_SECTION)));
24 volatile uint32_t wdt_status __attribute__((section(NOINIT_SECTION)));
25 volatile uint32_t reboot_status __attribute__((section(NOINIT_SECTION)));
26 volatile uint32_t cpu_lockup_status __attribute__((section(NOINIT_SECTION)));
27
28 /* Value used to indicate that the watchdog has fired. */
29 #define WDT_HAS_FIRED (0x12345678U)
30 #define REBOOT_WAS_DONE (0x87654321U)
31 #define CPU_LOCKUP_WAS_DONE (0x19283746U)
32
33 /* Highest value in the switch statement in the main() */
34 #define LAST_STATE (3)
35
wdt_int_cb(const struct device * wdt_dev,int channel_id)36 static void wdt_int_cb(const struct device *wdt_dev, int channel_id)
37 {
38 ARG_UNUSED(wdt_dev);
39 ARG_UNUSED(channel_id);
40 wdt_status = WDT_HAS_FIRED;
41
42 /* Flush cache as reboot may invalidate all lines. */
43 sys_cache_data_flush_range((void *) &wdt_status, sizeof(wdt_status));
44 }
45
46 /* Print LOG delimiter. */
print_bar(void)47 static void print_bar(void)
48 {
49 LOG_INF("===================================================================");
50 }
51
print_supported_reset_cause(void)52 static void print_supported_reset_cause(void)
53 {
54 int32_t ret;
55
56 /* Store supported reset causes in global variable placed at NOINIT_SECTION. */
57 ret = hwinfo_get_supported_reset_cause((uint32_t *) &supported);
58
59 /* Print which reset causes are supported. */
60 if (ret == 0) {
61 LOG_INF("Supported reset causes are:");
62 if (supported & RESET_PIN) {
63 LOG_INF(" 0: RESET_PIN is supported");
64 } else {
65 LOG_INF(" 0: no support for RESET_PIN");
66 }
67 if (supported & RESET_SOFTWARE) {
68 LOG_INF(" 1: RESET_SOFTWARE is supported");
69 } else {
70 LOG_INF(" 1: no support for RESET_SOFTWARE");
71 }
72 if (supported & RESET_BROWNOUT) {
73 LOG_INF(" 2: RESET_BROWNOUT is supported");
74 } else {
75 LOG_INF(" 2: no support for RESET_BROWNOUT");
76 }
77 if (supported & RESET_POR) {
78 LOG_INF(" 3: RESET_POR is supported");
79 } else {
80 LOG_INF(" 3: no support for RESET_POR");
81 }
82 if (supported & RESET_WATCHDOG) {
83 LOG_INF(" 4: RESET_WATCHDOG is supported");
84 } else {
85 LOG_INF(" 4: no support for RESET_WATCHDOG");
86 }
87 if (supported & RESET_DEBUG) {
88 LOG_INF(" 5: RESET_DEBUG is supported");
89 } else {
90 LOG_INF(" 5: no support for RESET_DEBUG");
91 }
92 if (supported & RESET_SECURITY) {
93 LOG_INF(" 6: RESET_SECURITY is supported");
94 } else {
95 LOG_INF(" 6: no support for RESET_SECURITY");
96 }
97 if (supported & RESET_LOW_POWER_WAKE) {
98 LOG_INF(" 7: RESET_LOW_POWER_WAKE is supported");
99 } else {
100 LOG_INF(" 7: no support for RESET_LOW_POWER_WAKE");
101 }
102 if (supported & RESET_CPU_LOCKUP) {
103 LOG_INF(" 8: RESET_CPU_LOCKUP is supported");
104 } else {
105 LOG_INF(" 8: no support for RESET_CPU_LOCKUP");
106 }
107 if (supported & RESET_PARITY) {
108 LOG_INF(" 9: RESET_PARITY is supported");
109 } else {
110 LOG_INF(" 9: no support for RESET_PARITY");
111 }
112 if (supported & RESET_PLL) {
113 LOG_INF("10: RESET_PLL is supported");
114 } else {
115 LOG_INF("10: no support for RESET_PLL");
116 }
117 if (supported & RESET_CLOCK) {
118 LOG_INF("11: RESET_CLOCK is supported");
119 } else {
120 LOG_INF("11: no support for RESET_CLOCK");
121 }
122 if (supported & RESET_HARDWARE) {
123 LOG_INF("12: RESET_HARDWARE is supported");
124 } else {
125 LOG_INF("12: no support for RESET_HARDWARE");
126 }
127 if (supported & RESET_USER) {
128 LOG_INF("13: RESET_USER is supported");
129 } else {
130 LOG_INF("13: no support for RESET_USER");
131 }
132 if (supported & RESET_TEMPERATURE) {
133 LOG_INF("14: RESET_TEMPERATURE is supported");
134 } else {
135 LOG_INF("14: no support for RESET_TEMPERATURE");
136 }
137 } else if (ret == -ENOSYS) {
138 LOG_INF("hwinfo_get_supported_reset_cause() is NOT supported");
139 supported = 0;
140 } else {
141 LOG_ERR("hwinfo_get_supported_reset_cause() failed (ret = %d)", ret);
142 }
143 sys_cache_data_flush_range((void *) &supported, sizeof(supported));
144 print_bar();
145 }
146
147 /* Print current value of reset cause. */
print_current_reset_cause(uint32_t * cause)148 static void print_current_reset_cause(uint32_t *cause)
149 {
150 int32_t ret;
151
152 ret = hwinfo_get_reset_cause(cause);
153 if (ret == 0) {
154 LOG_INF("Current reset cause is:");
155 if (*cause & RESET_PIN) {
156 LOG_INF(" 0: reset due to RESET_PIN");
157 }
158 if (*cause & RESET_SOFTWARE) {
159 LOG_INF(" 1: reset due to RESET_SOFTWARE");
160 }
161 if (*cause & RESET_BROWNOUT) {
162 LOG_INF(" 2: reset due to RESET_BROWNOUT");
163 }
164 if (*cause & RESET_POR) {
165 LOG_INF(" 3: reset due to RESET_POR");
166 }
167 if (*cause & RESET_WATCHDOG) {
168 LOG_INF(" 4: reset due to RESET_WATCHDOG");
169 }
170 if (*cause & RESET_DEBUG) {
171 LOG_INF(" 5: reset due to RESET_DEBUG");
172 }
173 if (*cause & RESET_SECURITY) {
174 LOG_INF(" 6: reset due to RESET_SECURITY");
175 }
176 if (*cause & RESET_LOW_POWER_WAKE) {
177 LOG_INF(" 7: reset due to RESET_LOW_POWER_WAKE");
178 }
179 if (*cause & RESET_CPU_LOCKUP) {
180 LOG_INF(" 8: reset due to RESET_CPU_LOCKUP");
181 }
182 if (*cause & RESET_PARITY) {
183 LOG_INF(" 9: reset due to RESET_PARITY");
184 }
185 if (*cause & RESET_PLL) {
186 LOG_INF("10: reset due to RESET_PLL");
187 }
188 if (*cause & RESET_CLOCK) {
189 LOG_INF("11: reset due to RESET_CLOCK");
190 }
191 if (*cause & RESET_HARDWARE) {
192 LOG_INF("12: reset due to RESET_HARDWARE");
193 }
194 if (*cause & RESET_USER) {
195 LOG_INF("13: reset due to RESET_USER");
196 }
197 if (*cause & RESET_TEMPERATURE) {
198 LOG_INF("14: reset due to RESET_TEMPERATURE");
199 }
200 } else if (ret == -ENOSYS) {
201 LOG_INF("hwinfo_get_reset_cause() is NOT supported");
202 *cause = 0;
203 } else {
204 LOG_ERR("hwinfo_get_reset_cause() failed (ret = %d)", ret);
205 }
206 print_bar();
207 }
208
209 /* Clear reset cause. */
test_clear_reset_cause(void)210 static void test_clear_reset_cause(void)
211 {
212 int32_t ret, temp;
213
214 ret = hwinfo_clear_reset_cause();
215 if (ret == 0) {
216 LOG_INF("hwinfo_clear_reset_cause() was executed");
217 } else if (ret == -ENOSYS) {
218 LOG_INF("hwinfo_get_reset_cause() is NOT supported");
219 } else {
220 LOG_ERR("hwinfo_get_reset_cause() failed (ret = %d)", ret);
221 }
222 print_bar();
223
224 /* Print current value of reset causes -> expected 0 */
225 print_current_reset_cause(&temp);
226 LOG_INF("TEST that all reset causes were cleared");
227 if (temp == 0) {
228 LOG_INF("PASS: reset causes were cleared");
229 } else {
230 LOG_ERR("FAIL: reset case = %u while expected is 0", temp);
231 }
232 print_bar();
233 }
234
test_reset_software(uint32_t cause)235 void test_reset_software(uint32_t cause)
236 {
237 /* Check that reset cause from sys_reboot is detected. */
238 if (supported & RESET_SOFTWARE) {
239 if (reboot_status != REBOOT_WAS_DONE) {
240 /* If software reset hasn't happen yet, do it. */
241 reboot_status = REBOOT_WAS_DONE;
242 LOG_INF("Test RESET_SOFTWARE");
243
244 /* Flush cache as reboot may invalidate all lines. */
245 sys_cache_data_flush_range((void *) &machine_state, sizeof(machine_state));
246 sys_cache_data_flush_range((void *) &reboot_status, sizeof(reboot_status));
247 sys_reboot(SYS_REBOOT_COLD);
248 } else {
249 /* Software reset was done */
250 LOG_INF("TEST that RESET_SOFTWARE was detected");
251 if (cause & RESET_SOFTWARE) {
252 LOG_INF("PASS: RESET_SOFTWARE detected");
253 print_bar();
254 /* Check RESET_SOFTWARE can be cleared */
255 test_clear_reset_cause();
256 } else {
257 LOG_ERR("FAIL: RESET_SOFTWARE not set");
258 print_bar();
259 }
260 /* Cleanup */
261 reboot_status = 0;
262 sys_cache_data_flush_range((void *) &reboot_status, sizeof(reboot_status));
263 }
264 }
265 }
266
test_reset_watchdog(uint32_t cause)267 void test_reset_watchdog(uint32_t cause)
268 {
269 int32_t ret;
270
271 /* Check that reset cause from expired watchdog is detected. */
272 if (supported & RESET_WATCHDOG) {
273 if (wdt_status != WDT_HAS_FIRED) {
274 /* If watchdog hasn't fired yet, configure it do so. */
275 uint32_t watchdog_window = 2000U;
276
277 if (!device_is_ready(my_wdt_device)) {
278 LOG_ERR("WDT device %s is not ready", my_wdt_device->name);
279 return;
280 }
281
282 m_cfg_wdt0.callback = wdt_int_cb;
283 m_cfg_wdt0.flags = WDT_FLAG_RESET_SOC;
284 m_cfg_wdt0.window.max = watchdog_window;
285 m_cfg_wdt0.window.min = 0U;
286 my_wdt_channel = wdt_install_timeout(my_wdt_device, &m_cfg_wdt0);
287 if (my_wdt_channel < 0) {
288 LOG_ERR("wdt_install_timeout() returned %d", my_wdt_channel);
289 return;
290 }
291
292 ret = wdt_setup(my_wdt_device, WDT_OPT_PAUSE_HALTED_BY_DBG);
293 if (ret < 0) {
294 LOG_ERR("wdt_setup() returned %d", ret);
295 return;
296 }
297
298 /* Flush cache as reboot may invalidate all lines. */
299 sys_cache_data_flush_range((void *) &machine_state, sizeof(machine_state));
300 LOG_INF("Watchdog shall fire in ~%u miliseconds", watchdog_window);
301 print_bar();
302 k_sleep(K_FOREVER);
303 } else {
304 /* Watchdog has fired. */
305 LOG_INF("TEST that RESET_WATCHDOG was detected");
306 if (cause & RESET_WATCHDOG) {
307 LOG_INF("PASS: RESET_WATCHDOG detected");
308 print_bar();
309 /* Check RESET_WATCHDOG can be cleared */
310 test_clear_reset_cause();
311 } else {
312 LOG_ERR("FAIL: RESET_WATCHDOG not set");
313 print_bar();
314 }
315 /* Cleanup */
316 wdt_status = 0;
317 sys_cache_data_flush_range((void *) &wdt_status, sizeof(wdt_status));
318 }
319 }
320 }
321
k_sys_fatal_error_handler(unsigned int reason,const struct arch_esf * pEsf)322 void k_sys_fatal_error_handler(unsigned int reason, const struct arch_esf *pEsf)
323 {
324 LOG_INF("%s(%d)", __func__, reason);
325 LOG_PANIC();
326
327 /* Assert inside Assert handler - shall result in reset due to cpu lockup. */
328 __ASSERT(0, "Intentionally failed assert inside kernel panic");
329 }
330
test_reset_cpu_lockup(uint32_t cause)331 void test_reset_cpu_lockup(uint32_t cause)
332 {
333 /* Check that reset cause from cpu lockup is detected. */
334 if (supported & RESET_CPU_LOCKUP) {
335 if (cpu_lockup_status != CPU_LOCKUP_WAS_DONE) {
336 /* If reset due to cpu lockup hasn't happen yet, do it. */
337 cpu_lockup_status = CPU_LOCKUP_WAS_DONE;
338 LOG_INF("Test RESET_CPU_LOCKUP");
339
340 /* Flush cache as reboot may invalidate all lines. */
341 sys_cache_data_flush_range((void *) &machine_state, sizeof(machine_state));
342 sys_cache_data_flush_range((void *) &cpu_lockup_status,
343 sizeof(cpu_lockup_status));
344 __ASSERT(0, "Intentionally failed assertion");
345 } else {
346 /* Reset due to CPU Lockup was done */
347 LOG_INF("TEST that RESET_CPU_LOCKUP was detected");
348 if (cause & RESET_CPU_LOCKUP) {
349 LOG_INF("PASS: RESET_CPU_LOCKUP detected");
350 print_bar();
351 /* Check RESET_SOFTWARE can be cleared */
352 test_clear_reset_cause();
353 } else {
354 LOG_ERR("FAIL: RESET_CPU_LOCKUP not set");
355 print_bar();
356 }
357 /* Cleanup */
358 cpu_lockup_status = 0;
359 sys_cache_data_flush_range((void *) &cpu_lockup_status,
360 sizeof(cpu_lockup_status));
361 }
362 }
363 }
364
main(void)365 int main(void)
366 {
367 uint32_t cause;
368
369 LOG_INF("HW Info reset reason test on %s", CONFIG_BOARD_TARGET);
370 if (wdt_status == WDT_HAS_FIRED) {
371 LOG_INF("This boot is due to expected watchdog reset");
372 }
373 if (reboot_status == REBOOT_WAS_DONE) {
374 LOG_INF("This boot is due to expected software reset");
375 }
376 if (cpu_lockup_status == CPU_LOCKUP_WAS_DONE) {
377 LOG_INF("This boot is due to expected cpu lockup reset");
378 }
379 print_bar();
380
381 /* Test relies on REST_PIN to correctly start. */
382 print_current_reset_cause(&cause);
383 if (cause & RESET_PIN) {
384 LOG_INF("TEST that RESET_PIN was detected");
385 LOG_INF("PASS: RESET_PIN detected");
386 print_bar();
387 /* Check RESET_PIN can be cleared */
388 test_clear_reset_cause();
389 machine_state = 0;
390 reboot_status = 0;
391 wdt_status = 0;
392 cpu_lockup_status = 0;
393 }
394
395 while (machine_state <= LAST_STATE) {
396 LOG_DBG("machine_state = %u", machine_state);
397 LOG_DBG("reboot_status = %u", reboot_status);
398 LOG_DBG("wdt_status = %u", wdt_status);
399 LOG_DBG("cpu_lockup_status = %u", cpu_lockup_status);
400
401 switch (machine_state) {
402 case 0: /* Print (an store) which reset causes are supported. */
403 print_supported_reset_cause();
404 machine_state++;
405 break;
406 case 1: /* Test RESET_SOFTWARE. */
407 test_reset_software(cause);
408 machine_state++;
409 case 2: /* Test RESET_WATCHDOG. */
410 test_reset_watchdog(cause);
411 machine_state++;
412 case 3: /* Test CPU_LOCKUP. */
413 test_reset_cpu_lockup(cause);
414 machine_state++;
415 }
416 }
417
418 LOG_INF("All tests done");
419 return 0;
420 }
421