1 /*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <stdio.h>
8
9 #include "hardware/watchdog.h"
10 #include "hardware/structs/watchdog.h"
11 #include "hardware/structs/psm.h"
12
13 /// \tag::watchdog_start_tick[]
watchdog_start_tick(uint cycles)14 void watchdog_start_tick(uint cycles) {
15 // Important: This function also provides a tick reference to the timer
16 watchdog_hw->tick = cycles | WATCHDOG_TICK_ENABLE_BITS;
17 }
18 /// \end::watchdog_start_tick[]
19
20 // Value to load when updating the watchdog
21
22 // tag::watchdog_update[]
23 static uint32_t load_value;
24
watchdog_update(void)25 void watchdog_update(void) {
26 watchdog_hw->load = load_value;
27 }
28 // end::watchdog_update[]
29
watchdog_get_count(void)30 uint32_t watchdog_get_count(void) {
31 return (watchdog_hw->ctrl & WATCHDOG_CTRL_TIME_BITS) / 2 ;
32 }
33
34 // tag::watchdog_enable[]
35 // Helper function used by both watchdog_enable and watchdog_reboot
_watchdog_enable(uint32_t delay_ms,bool pause_on_debug)36 void _watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
37 hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
38
39 // Reset everything apart from ROSC and XOSC
40 hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_BITS & ~(PSM_WDSEL_ROSC_BITS | PSM_WDSEL_XOSC_BITS));
41
42 uint32_t dbg_bits = WATCHDOG_CTRL_PAUSE_DBG0_BITS |
43 WATCHDOG_CTRL_PAUSE_DBG1_BITS |
44 WATCHDOG_CTRL_PAUSE_JTAG_BITS;
45
46 if (pause_on_debug) {
47 hw_set_bits(&watchdog_hw->ctrl, dbg_bits);
48 } else {
49 hw_clear_bits(&watchdog_hw->ctrl, dbg_bits);
50 }
51
52 if (!delay_ms) delay_ms = 50;
53
54 // Note, we have x2 here as the watchdog HW currently decrements twice per tick
55 load_value = delay_ms * 1000 * 2;
56
57 if (load_value > 0xffffffu)
58 load_value = 0xffffffu;
59
60
61 watchdog_update();
62
63 hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
64 }
65 // end::watchdog_enable[]
66
watchdog_enable(uint32_t delay_ms,bool pause_on_debug)67 void watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
68 // This watchdog enable doesn't reboot so clear scratch register
69 // with magic word to jump into code
70 watchdog_hw->scratch[4] = 0;
71 _watchdog_enable(delay_ms, pause_on_debug);
72 }
73
watchdog_reboot(uint32_t pc,uint32_t sp,uint32_t delay_ms)74 void watchdog_reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms) {
75 check_hw_layout(watchdog_hw_t, scratch[7], WATCHDOG_SCRATCH7_OFFSET);
76
77 // Clear enable before setting up scratch registers
78 hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
79
80 if (pc) {
81 pc |= 1u; // thumb mode
82 watchdog_hw->scratch[4] = 0xb007c0d3;
83 watchdog_hw->scratch[5] = pc ^ -0xb007c0d3;
84 watchdog_hw->scratch[6] = sp;
85 watchdog_hw->scratch[7] = pc;
86 // printf("rebooting %08x/%08x in %dms...\n", (uint) pc, (uint) sp, (uint) delay_ms);
87 } else {
88 watchdog_hw->scratch[4] = 0;
89 // printf("rebooting (regular)) in %dms...\n", (uint) delay_ms);
90 }
91
92 // Don't pause watchdog for debug
93 _watchdog_enable(delay_ms, 0);
94 }
95
watchdog_caused_reboot(void)96 bool watchdog_caused_reboot(void) {
97 // If any reason bits are set this is true
98 return watchdog_hw->reason;
99 }