1 /*
2 * Copyright (c) 2016 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT arm_cmsdk_watchdog
8
9 /**
10 * @brief Driver for CMSDK APB Watchdog.
11 */
12
13 #include <errno.h>
14 #include <soc.h>
15 #include <zephyr/arch/arm/nmi.h>
16 #include <zephyr/drivers/watchdog.h>
17 #include <zephyr/sys/printk.h>
18 #include <zephyr/sys/reboot.h>
19
20 struct wdog_cmsdk_apb {
21 /* offset: 0x000 (r/w) watchdog load register */
22 volatile uint32_t load;
23 /* offset: 0x004 (r/ ) watchdog value register */
24 volatile uint32_t value;
25 /* offset: 0x008 (r/w) watchdog control register */
26 volatile uint32_t ctrl;
27 /* offset: 0x00c ( /w) watchdog clear interrupt register */
28 volatile uint32_t intclr;
29 /* offset: 0x010 (r/ ) watchdog raw interrupt status register */
30 volatile uint32_t rawintstat;
31 /* offset: 0x014 (r/ ) watchdog interrupt status register */
32 volatile uint32_t maskintstat;
33 volatile uint32_t reserved0[762];
34 /* offset: 0xc00 (r/w) watchdog lock register */
35 volatile uint32_t lock;
36 volatile uint32_t reserved1[191];
37 /* offset: 0xf00 (r/w) watchdog integration test control register */
38 volatile uint32_t itcr;
39 /* offset: 0xf04 ( /w) watchdog integration test output set register */
40 volatile uint32_t itop;
41 };
42
43 #define CMSDK_APB_WDOG_LOAD (0xFFFFFFFF << 0)
44 #define CMSDK_APB_WDOG_RELOAD (0xE4E1C00 << 0)
45 #define CMSDK_APB_WDOG_VALUE (0xFFFFFFFF << 0)
46 #define CMSDK_APB_WDOG_CTRL_RESEN (0x1 << 1)
47 #define CMSDK_APB_WDOG_CTRL_INTEN (0x1 << 0)
48 #define CMSDK_APB_WDOG_INTCLR (0x1 << 0)
49 #define CMSDK_APB_WDOG_RAWINTSTAT (0x1 << 0)
50 #define CMSDK_APB_WDOG_MASKINTSTAT (0x1 << 0)
51 #define CMSDK_APB_WDOG_LOCK (0x1 << 0)
52 #define CMSDK_APB_WDOG_INTEGTESTEN (0x1 << 0)
53 #define CMSDK_APB_WDOG_INTEGTESTOUTSET (0x1 << 1)
54
55 /*
56 * Value written to the LOCK register to lock or unlock the write access
57 * to all of the registers of the watchdog (except the LOCK register)
58 */
59 #define CMSDK_APB_WDOG_UNLOCK_VALUE (0x1ACCE551)
60 #define CMSDK_APB_WDOG_LOCK_VALUE (0x2BDDF662)
61
62 #define WDOG_STRUCT \
63 ((volatile struct wdog_cmsdk_apb *)(DT_INST_REG_ADDR(0)))
64
65 /* Keep reference of the device to pass it to the callback */
66 const struct device *wdog_r;
67
68 /* watchdog reload value in clock cycles */
69 static unsigned int reload_cycles = CMSDK_APB_WDOG_RELOAD;
70 static uint8_t assigned_channels;
71 static uint8_t flags;
72 static bool enabled;
73
74 static void (*user_cb)(const struct device *dev, int channel_id);
75
wdog_cmsdk_apb_unlock(const struct device * dev)76 static void wdog_cmsdk_apb_unlock(const struct device *dev)
77 {
78 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
79
80 ARG_UNUSED(dev);
81
82 wdog->lock = CMSDK_APB_WDOG_UNLOCK_VALUE;
83 }
84
wdog_cmsdk_apb_setup(const struct device * dev,uint8_t options)85 static int wdog_cmsdk_apb_setup(const struct device *dev, uint8_t options)
86 {
87 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
88
89 ARG_UNUSED(dev);
90 ARG_UNUSED(options);
91
92 /* Check if watchdog is already running */
93 if (enabled) {
94 return -EBUSY;
95 }
96
97 /* Reset pending interrupts before starting */
98 wdog->intclr = CMSDK_APB_WDOG_INTCLR;
99 wdog->load = reload_cycles;
100
101 /* Start the watchdog counter with INTEN bit */
102 wdog->ctrl = (CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN);
103
104 enabled = true;
105 return 0;
106 }
107
wdog_cmsdk_apb_disable(const struct device * dev)108 static int wdog_cmsdk_apb_disable(const struct device *dev)
109 {
110 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
111
112 ARG_UNUSED(dev);
113
114 /* Stop the watchdog counter with INTEN bit */
115 wdog->ctrl = ~(CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN);
116
117 enabled = false;
118 assigned_channels = 0;
119
120 return 0;
121 }
122
wdog_cmsdk_apb_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)123 static int wdog_cmsdk_apb_install_timeout(const struct device *dev,
124 const struct wdt_timeout_cfg *config)
125 {
126 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
127 uint32_t clk_freq_khz = DT_INST_PROP_BY_PHANDLE(0, clocks, clock_frequency) / 1000;
128
129 ARG_UNUSED(dev);
130
131 if (config->window.max == 0) {
132 return -EINVAL;
133 }
134 if (enabled == true) {
135 return -EBUSY;
136 }
137 if (assigned_channels == 1) {
138 return -ENOMEM;
139 }
140
141 /* Reload value */
142 reload_cycles = config->window.max * clk_freq_khz;
143 flags = config->flags;
144
145 wdog->load = reload_cycles;
146
147 /* Configure only the callback */
148 user_cb = config->callback;
149
150 assigned_channels++;
151 return 0;
152 }
153
wdog_cmsdk_apb_feed(const struct device * dev,int channel_id)154 static int wdog_cmsdk_apb_feed(const struct device *dev, int channel_id)
155 {
156 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
157
158 ARG_UNUSED(dev);
159 ARG_UNUSED(channel_id);
160
161 /* Clear the interrupt */
162 wdog->intclr = CMSDK_APB_WDOG_INTCLR;
163
164 /* Reload */
165 wdog->load = reload_cycles;
166
167 return 0;
168 }
169
170 static DEVICE_API(wdt, wdog_cmsdk_apb_api) = {
171 .setup = wdog_cmsdk_apb_setup,
172 .disable = wdog_cmsdk_apb_disable,
173 .install_timeout = wdog_cmsdk_apb_install_timeout,
174 .feed = wdog_cmsdk_apb_feed,
175 };
176
177 #ifdef CONFIG_RUNTIME_NMI
178
wdog_cmsdk_apb_has_fired(void)179 static int wdog_cmsdk_apb_has_fired(void)
180 {
181 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
182
183 return (wdog->maskintstat & CMSDK_APB_WDOG_MASKINTSTAT) != 0U;
184 }
185
wdog_cmsdk_apb_isr(void)186 static void wdog_cmsdk_apb_isr(void)
187 {
188 /*
189 * Check if the watchdog was the reason of the NMI interrupt
190 * and if not, exit immediately
191 */
192 if (!wdog_cmsdk_apb_has_fired()) {
193 printk("NMI received! Rebooting...\n");
194 /* In ARM implementation sys_reboot ignores the parameter */
195 sys_reboot(0);
196 } else {
197 if (user_cb != NULL) {
198 user_cb(wdog_r, 0);
199 }
200 }
201 }
202 #endif /* CONFIG_RUNTIME_NMI */
203
wdog_cmsdk_apb_init(const struct device * dev)204 static int wdog_cmsdk_apb_init(const struct device *dev)
205 {
206 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
207
208 wdog_r = dev;
209
210 /* unlock access to configuration registers */
211 wdog_cmsdk_apb_unlock(dev);
212
213 /* set default reload value */
214 wdog->load = reload_cycles;
215
216 #ifdef CONFIG_RUNTIME_NMI
217 /* Configure the interrupts */
218 z_arm_nmi_set_handler(wdog_cmsdk_apb_isr);
219 #endif
220
221 #ifdef CONFIG_WDOG_CMSDK_APB_START_AT_BOOT
222 wdog_cmsdk_apb_setup(dev, 0);
223 #endif
224
225 return 0;
226 }
227
228 DEVICE_DT_INST_DEFINE(0,
229 wdog_cmsdk_apb_init,
230 NULL,
231 NULL, NULL,
232 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
233 &wdog_cmsdk_apb_api);
234