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 #include "hardware/ticks.h"
13 #include "pico/bootrom.h"
14 
15 /// \tag::watchdog_start_tick[]
watchdog_start_tick(uint cycles)16 void watchdog_start_tick(uint cycles) {
17     tick_start(TICK_WATCHDOG, cycles);
18 }
19 /// \end::watchdog_start_tick[]
20 
21 // Value to load when updating the watchdog
22 
23 // tag::watchdog_update[]
24 static uint32_t load_value;
25 
watchdog_update(void)26 void watchdog_update(void) {
27     watchdog_hw->load = load_value;
28 }
29 // end::watchdog_update[]
30 
watchdog_get_time_remaining_ms(void)31 uint32_t watchdog_get_time_remaining_ms(void) {
32     return watchdog_hw->ctrl & WATCHDOG_CTRL_TIME_BITS;
33 }
34 
35 #if PICO_RP2040
36 // Note, we have x2 here as the watchdog HW currently decrements twice per tick
37 #define WATCHDOG_XFACTOR 2
38 #else
39 #define WATCHDOG_XFACTOR 1
40 #endif
41 // tag::watchdog_enable[]
42 // Helper function used by both watchdog_enable and watchdog_reboot
_watchdog_enable(uint32_t delay_ms,bool pause_on_debug)43 void _watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
44     valid_params_if(HARDWARE_WATCHDOG, delay_ms <= WATCHDOG_LOAD_BITS / (1000 * WATCHDOG_XFACTOR));
45     hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
46 
47     // Reset everything apart from ROSC and XOSC
48     hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_BITS & ~(PSM_WDSEL_ROSC_BITS | PSM_WDSEL_XOSC_BITS));
49 
50     uint32_t dbg_bits = WATCHDOG_CTRL_PAUSE_DBG0_BITS |
51                         WATCHDOG_CTRL_PAUSE_DBG1_BITS |
52                         WATCHDOG_CTRL_PAUSE_JTAG_BITS;
53 
54     if (pause_on_debug) {
55         hw_set_bits(&watchdog_hw->ctrl, dbg_bits);
56     } else {
57         hw_clear_bits(&watchdog_hw->ctrl, dbg_bits);
58     }
59 
60     if (!delay_ms) {
61         hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_TRIGGER_BITS);
62     } else {
63         load_value = delay_ms * 1000;
64 #if PICO_RP2040
65         load_value *= 2;
66 #endif
67         if (load_value > WATCHDOG_LOAD_BITS)
68             load_value = WATCHDOG_LOAD_BITS;
69 
70         watchdog_update();
71 
72         hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
73     }
74 }
75 // end::watchdog_enable[]
76 
77 #define WATCHDOG_NON_REBOOT_MAGIC 0x6ab73121
78 
watchdog_enable(uint32_t delay_ms,bool pause_on_debug)79 void watchdog_enable(uint32_t delay_ms, bool pause_on_debug) {
80     // update scratch[4] to distinguish from magic used for reboot to specific address, or 0 used to reboot
81     // into regular flash path
82     watchdog_hw->scratch[4] = WATCHDOG_NON_REBOOT_MAGIC;
83     _watchdog_enable(delay_ms, pause_on_debug);
84 }
85 
watchdog_disable(void)86 void watchdog_disable(void) {
87     hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
88 }
89 
watchdog_reboot(uint32_t pc,uint32_t sp,uint32_t delay_ms)90 void watchdog_reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms) {
91     check_hw_layout(watchdog_hw_t, scratch[7], WATCHDOG_SCRATCH7_OFFSET);
92 
93     // Clear enable before setting up scratch registers
94     hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
95 
96     if (pc) {
97 #ifndef __riscv
98         pc |= 1u; // thumb mode
99 #endif
100         watchdog_hw->scratch[4] = 0xb007c0d3;
101         watchdog_hw->scratch[5] = pc ^ -0xb007c0d3;
102         watchdog_hw->scratch[6] = sp;
103         watchdog_hw->scratch[7] = pc;
104 //        printf("rebooting %08x/%08x in %dms...\n", (uint) pc, (uint) sp, (uint) delay_ms);
105     } else {
106         watchdog_hw->scratch[4] = 0;
107 //        printf("rebooting (regular)) in %dms...\n", (uint) delay_ms);
108     }
109 
110     // Don't pause watchdog for debug
111     _watchdog_enable(delay_ms, 0);
112 }
113 
watchdog_caused_reboot(void)114 bool watchdog_caused_reboot(void) {
115     // If any reason bits are set this is true
116 #if PICO_RP2040
117     return watchdog_hw->reason;
118 #else
119     return watchdog_hw->reason && rom_get_last_boot_type() == BOOT_TYPE_NORMAL;
120 #endif
121 }
122 
watchdog_enable_caused_reboot(void)123 bool watchdog_enable_caused_reboot(void) {
124     return watchdog_hw->reason && watchdog_hw->scratch[4] == WATCHDOG_NON_REBOOT_MAGIC;
125 }
126