1 /*
2 * Copyright (c) 2022, Jamie McCrae
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT raspberrypi_pico_watchdog
8
9 #include <hardware/watchdog.h>
10 #include <hardware/ticks.h>
11 #include <hardware/structs/psm.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/watchdog.h>
14 #include <zephyr/sys_clock.h>
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(wdt_rpi_pico, CONFIG_WDT_LOG_LEVEL);
18
19 #ifdef CONFIG_SOC_RP2040
20 /* Maximum watchdog time is halved due to errata RP2040-E1 */
21 #define RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR 2
22 #else
23 #define RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR 1
24 #endif
25 #define RPI_PICO_MAX_WDT_TIME (0xffffff / RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR)
26
27 /* Watchdog requires a 1MHz clock source, divided from the crystal oscillator */
28 #define RPI_PICO_CLK_REF_FREQ_WDT_TICK_DIVISOR 1000000
29
30 struct wdt_rpi_pico_data {
31 uint8_t reset_type;
32 uint32_t load;
33 bool enabled;
34 };
35
36 struct wdt_rpi_pico_config {
37 const struct device *clk_dev;
38 clock_control_subsys_t clk_id;
39 };
40
wdt_rpi_pico_setup(const struct device * dev,uint8_t options)41 static int wdt_rpi_pico_setup(const struct device *dev, uint8_t options)
42 {
43 const struct wdt_rpi_pico_config *config = dev->config;
44 struct wdt_rpi_pico_data *data = dev->data;
45 uint32_t ref_clk;
46 int err;
47
48 if ((options & WDT_OPT_PAUSE_IN_SLEEP) == 1) {
49 return -ENOTSUP;
50 }
51
52 hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
53
54 psm_hw->wdsel = 0;
55
56 /* TODO: Handle individual core reset when SMP support for RP2040 is added */
57 if (data->reset_type == WDT_FLAG_RESET_SOC) {
58 hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_BITS);
59 } else if (data->reset_type == WDT_FLAG_RESET_CPU_CORE) {
60 hw_set_bits(&psm_hw->wdsel, PSM_WDSEL_PROC0_BITS);
61 }
62
63 if ((options & WDT_OPT_PAUSE_HALTED_BY_DBG) == 0) {
64 hw_clear_bits(&watchdog_hw->ctrl,
65 (WATCHDOG_CTRL_PAUSE_JTAG_BITS | WATCHDOG_CTRL_PAUSE_DBG0_BITS |
66 WATCHDOG_CTRL_PAUSE_DBG1_BITS));
67 } else {
68 hw_set_bits(&watchdog_hw->ctrl,
69 (WATCHDOG_CTRL_PAUSE_JTAG_BITS | WATCHDOG_CTRL_PAUSE_DBG0_BITS |
70 WATCHDOG_CTRL_PAUSE_DBG1_BITS));
71 }
72
73 watchdog_hw->load = data->load;
74
75 /* Zero out the scratch registers so that the module reboots at the
76 * default program counter
77 */
78 watchdog_hw->scratch[4] = 0;
79 watchdog_hw->scratch[5] = 0;
80 watchdog_hw->scratch[6] = 0;
81 watchdog_hw->scratch[7] = 0;
82
83 hw_set_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
84
85 data->enabled = true;
86
87 err = clock_control_on(config->clk_dev, config->clk_id);
88 if (err < 0) {
89 return err;
90 }
91
92 err = clock_control_get_rate(config->clk_dev, config->clk_id, &ref_clk);
93 if (err < 0) {
94 return err;
95 }
96
97 #ifdef CONFIG_SOC_RP2040
98 watchdog_hw->tick = (ref_clk / RPI_PICO_CLK_REF_FREQ_WDT_TICK_DIVISOR) |
99 WATCHDOG_TICK_ENABLE_BITS;
100 #else
101 ticks_hw->ticks[TICK_WATCHDOG].cycles = ref_clk / RPI_PICO_CLK_REF_FREQ_WDT_TICK_DIVISOR;
102 ticks_hw->ticks[TICK_WATCHDOG].ctrl = TICKS_WATCHDOG_CTRL_ENABLE_BITS;
103 #endif
104
105 return 0;
106 }
107
wdt_rpi_pico_disable(const struct device * dev)108 static int wdt_rpi_pico_disable(const struct device *dev)
109 {
110 struct wdt_rpi_pico_data *data = dev->data;
111
112 if (data->enabled == false) {
113 return -EFAULT;
114 }
115
116 hw_clear_bits(&watchdog_hw->ctrl, WATCHDOG_CTRL_ENABLE_BITS);
117
118 data->enabled = false;
119
120 return 0;
121 }
122
wdt_rpi_pico_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * cfg)123 static int wdt_rpi_pico_install_timeout(const struct device *dev, const struct wdt_timeout_cfg *cfg)
124 {
125 struct wdt_rpi_pico_data *data = dev->data;
126
127 if (cfg->window.min != 0U || cfg->window.max == 0U) {
128 return -EINVAL;
129 } else if (cfg->window.max * USEC_PER_MSEC > RPI_PICO_MAX_WDT_TIME) {
130 return -EINVAL;
131 } else if (cfg->callback != NULL) {
132 return -ENOTSUP;
133 } else if ((cfg->flags & WDT_FLAG_RESET_MASK) == WDT_FLAG_RESET_NONE) {
134 /* The RP2040 does technically support this mode, but requires
135 * a program counter and stack pointer value to be set,
136 * therefore do not allow configuring in this mode
137 */
138 return -EINVAL;
139 }
140
141 data->load = (cfg->window.max * USEC_PER_MSEC * RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR);
142 data->reset_type = (cfg->flags & WDT_FLAG_RESET_MASK);
143
144 return 0;
145 }
146
wdt_rpi_pico_feed(const struct device * dev,int channel_id)147 static int wdt_rpi_pico_feed(const struct device *dev, int channel_id)
148 {
149 struct wdt_rpi_pico_data *data = dev->data;
150
151 if (channel_id != 0) {
152 /* There is only one input to the watchdog */
153 return -EINVAL;
154 }
155
156 if (data->enabled == false) {
157 /* Watchdog is not running so does not need to be fed */
158 return -EINVAL;
159 }
160
161 watchdog_hw->load = data->load;
162
163 return 0;
164 }
165
wdt_rpi_pico_init(const struct device * dev)166 static int wdt_rpi_pico_init(const struct device *dev)
167 {
168 #ifndef CONFIG_WDT_DISABLE_AT_BOOT
169 return wdt_rpi_pico_setup(dev, WDT_OPT_PAUSE_HALTED_BY_DBG);
170 #endif
171
172 return 0;
173 }
174
175 static DEVICE_API(wdt, wdt_rpi_pico_driver_api) = {
176 .setup = wdt_rpi_pico_setup,
177 .disable = wdt_rpi_pico_disable,
178 .install_timeout = wdt_rpi_pico_install_timeout,
179 .feed = wdt_rpi_pico_feed,
180 };
181
182 #define WDT_RPI_PICO_WDT_DEVICE(idx) \
183 static const struct wdt_rpi_pico_config wdt_##idx##_config = { \
184 .clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
185 .clk_id = (clock_control_subsys_t)DT_INST_PHA_BY_IDX(idx, clocks, 0, clk_id), \
186 }; \
187 static struct wdt_rpi_pico_data wdt_##idx##_data = { \
188 .reset_type = WDT_FLAG_RESET_SOC, \
189 .load = (CONFIG_WDT_RPI_PICO_INITIAL_TIMEOUT * \
190 RPI_PICO_WDT_TIME_MULTIPLICATION_FACTOR), \
191 .enabled = false \
192 }; \
193 DEVICE_DT_DEFINE(DT_NODELABEL(wdt##idx), wdt_rpi_pico_init, NULL, &wdt_##idx##_data, \
194 &wdt_##idx##_config, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \
195 &wdt_rpi_pico_driver_api)
196
197 DT_INST_FOREACH_STATUS_OKAY(WDT_RPI_PICO_WDT_DEVICE);
198