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 }