1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_WATCHDOG_H
8 #define _HARDWARE_WATCHDOG_H
9 
10 #include "pico.h"
11 #include "hardware/structs/watchdog.h"
12 
13 /** \file hardware/watchdog.h
14  *  \defgroup hardware_watchdog hardware_watchdog
15  *
16  * \brief Hardware Watchdog Timer API
17  *
18  * Supporting functions for the Pico hardware watchdog timer.
19  *
20  * The RP-series microcontrollers have a built in HW watchdog Timer. This is a countdown timer that can restart parts of the chip if it reaches zero.
21  * For example, this can be used to restart the processor if the software running on it gets stuck in an infinite loop
22  * or similar. The programmer has to periodically write a value to the watchdog to stop it reaching zero.
23  *
24  * \subsection watchdog_example Example
25  * \addtogroup hardware_watchdog
26  * \include hello_watchdog.c
27  */
28 
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 
33 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_HARDWARE_WATCHDOG, Enable/disable assertions in the hardware_watchdog module, type=bool, default=0, group=hardware_watchdog
34 #ifndef PARAM_ASSERTIONS_ENABLED_HARDWARE_WATCHDOG
35 #ifdef PARAM_ASSERTIONS_ENABLED_WATCHDOG // backwards compatibility with SDK < 2.0.0
36 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_WATCHDOG PARAM_ASSERTIONS_ENABLED_WATCHDOG
37 #else
38 #define PARAM_ASSERTIONS_ENABLED_HARDWARE_WATCHDOG 0
39 #endif
40 #endif
41 
42 /*! \brief Define actions to perform at watchdog timeout
43  *  \ingroup hardware_watchdog
44  *
45  * \note If \ref watchdog_start_tick value does not give a 1MHz clock to the watchdog system, then the \p delay_ms
46  * parameter will not be in milliseconds. See the datasheet for more details.
47  *
48  * By default the SDK assumes a 12MHz XOSC and sets the \ref watchdog_start_tick appropriately.
49  *
50  * \param pc If Zero, a standard boot will be performed, if non-zero this is the program counter to jump to on reset.
51  * \param sp If \p pc is non-zero, this will be the stack pointer used.
52  * \param delay_ms Initial load value. Maximum value 8388, approximately 8.3s.
53  */
54 void watchdog_reboot(uint32_t pc, uint32_t sp, uint32_t delay_ms);
55 
56 /*! \brief Start the watchdog tick
57  *  \ingroup hardware_watchdog
58  *
59  * \param cycles This needs to be a divider that when applied to the XOSC input, produces a 1MHz clock. So if the XOSC is
60  * 12MHz, this will need to be 12.
61  */
62 void watchdog_start_tick(uint cycles);
63 
64 /*! \brief Reload the watchdog counter with the amount of time set in watchdog_enable
65  *  \ingroup hardware_watchdog
66  *
67  */
68 void watchdog_update(void);
69 
70 /**
71  * \brief Enable the watchdog
72  * \ingroup hardware_watchdog
73  *
74  * \note If \ref watchdog_start_tick value does not give a 1MHz clock to the watchdog system, then the \p delay_ms
75  * parameter will not be in milliseconds. See the datasheet for more details.
76  *
77  * By default the SDK assumes a 12MHz XOSC and sets the \ref watchdog_start_tick appropriately.
78  *
79  * This method sets a marker in the watchdog scratch register 4 that is checked by \ref watchdog_enable_caused_reboot.
80  * If the device is subsequently reset via a call to watchdog_reboot (including for example by dragging a UF2
81  * onto the RPI-RP2), then this value will be cleared, and so \ref watchdog_enable_caused_reboot will
82  * return false.
83  *
84  * \param delay_ms Number of milliseconds before watchdog will reboot without watchdog_update being called. Maximum of 8388, which is approximately 8.3 seconds
85  * \param pause_on_debug If the watchdog should be paused when the debugger is stepping through code
86  */
87 void watchdog_enable(uint32_t delay_ms, bool pause_on_debug);
88 
89 /**
90  * \brief Disable the watchdog
91  * \ingroup hardware_watchdog
92  */
93 void watchdog_disable(void);
94 
95 /**
96  * \brief Did the watchdog cause the last reboot?
97  * \ingroup hardware_watchdog
98  *
99  * @return true If the watchdog timer or a watchdog force caused the last reboot
100  * @return false If there has been no watchdog reboot since the last power on reset. A power on reset is typically caused by a power cycle or the run pin (reset button) being toggled.
101  */
102 bool watchdog_caused_reboot(void);
103 
104 /**
105  * \brief Did watchdog_enable cause the last reboot?
106  * \ingroup hardware_watchdog
107  *
108  * Perform additional checking along with \ref watchdog_caused_reboot to determine if a watchdog timeout initiated by
109  * \ref watchdog_enable caused the last reboot.
110  *
111  * This method checks for a special value in watchdog scratch register 4 placed there by \ref watchdog_enable.
112  * This would not be present if a watchdog reset is initiated by \ref watchdog_reboot or by the RP-series microcontroller bootrom
113  * (e.g. dragging a UF2 onto the RPI-RP2 drive).
114  *
115  * @return true If the watchdog timer or a watchdog force caused (see \ref watchdog_caused_reboot) the last reboot
116  *              and the watchdog reboot happened after \ref watchdog_enable was called
117  * @return false If there has been no watchdog reboot since the last power on reset, or the watchdog reboot was not caused
118  *               by a watchdog timeout after \ref watchdog_enable was called.
119  *               A power on reset is typically caused by a power cycle or the run pin (reset button) being toggled.
120  */
121 bool watchdog_enable_caused_reboot(void);
122 
123 /**
124  * \brief Returns the number of microseconds before the watchdog will reboot the chip.
125  * \ingroup hardware_watchdog
126  *
127  * \if rp2040_specicifc
128  * On RP2040 this method returns the last value set instead of the remaining time due to a h/w bug.
129  * \endif
130  *
131  * @return The number of microseconds before the watchdog will reboot the chip.
132  */
133 uint32_t watchdog_get_time_remaining_ms(void);
134 
135 // backwards compatibility with SDK < 2.0.0
watchdog_get_count(void)136 static inline uint32_t watchdog_get_count(void) {
137     return watchdog_get_time_remaining_ms();
138 }
139 #ifdef __cplusplus
140 }
141 #endif
142 
143 #endif
144