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
73 static void (*user_cb)(const struct device *dev, int channel_id);
74
wdog_cmsdk_apb_unlock(const struct device * dev)75 static void wdog_cmsdk_apb_unlock(const struct device *dev)
76 {
77 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
78
79 ARG_UNUSED(dev);
80
81 wdog->lock = CMSDK_APB_WDOG_UNLOCK_VALUE;
82 }
83
wdog_cmsdk_apb_setup(const struct device * dev,uint8_t options)84 static int wdog_cmsdk_apb_setup(const struct device *dev, uint8_t options)
85 {
86 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
87
88 ARG_UNUSED(dev);
89 ARG_UNUSED(options);
90
91 /* Start the watchdog counter with INTEN bit */
92 wdog->ctrl = (CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN);
93
94 return 0;
95 }
96
wdog_cmsdk_apb_disable(const struct device * dev)97 static int wdog_cmsdk_apb_disable(const struct device *dev)
98 {
99 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
100
101 ARG_UNUSED(dev);
102
103 /* Stop the watchdog counter with INTEN bit */
104 wdog->ctrl = ~(CMSDK_APB_WDOG_CTRL_RESEN | CMSDK_APB_WDOG_CTRL_INTEN);
105
106 assigned_channels = 0;
107
108 return 0;
109 }
110
wdog_cmsdk_apb_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)111 static int wdog_cmsdk_apb_install_timeout(const struct device *dev,
112 const struct wdt_timeout_cfg *config)
113 {
114 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
115 uint32_t clk_freq_khz = DT_INST_PROP_BY_PHANDLE(0, clocks, clock_frequency) / 1000;
116
117 ARG_UNUSED(dev);
118
119 if (config->window.max == 0) {
120 return -EINVAL;
121 }
122 if (assigned_channels == 1) {
123 return -ENOMEM;
124 }
125
126 /* Reload value */
127 reload_cycles = config->window.max * clk_freq_khz;
128 flags = config->flags;
129
130 wdog->load = reload_cycles;
131
132 /* Configure only the callback */
133 user_cb = config->callback;
134
135 assigned_channels++;
136 return 0;
137 }
138
wdog_cmsdk_apb_feed(const struct device * dev,int channel_id)139 static int wdog_cmsdk_apb_feed(const struct device *dev, int channel_id)
140 {
141 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
142
143 ARG_UNUSED(dev);
144 ARG_UNUSED(channel_id);
145
146 /* Clear the interrupt */
147 wdog->intclr = CMSDK_APB_WDOG_INTCLR;
148
149 /* Reload */
150 wdog->load = reload_cycles;
151
152 return 0;
153 }
154
155 static DEVICE_API(wdt, wdog_cmsdk_apb_api) = {
156 .setup = wdog_cmsdk_apb_setup,
157 .disable = wdog_cmsdk_apb_disable,
158 .install_timeout = wdog_cmsdk_apb_install_timeout,
159 .feed = wdog_cmsdk_apb_feed,
160 };
161
162 #ifdef CONFIG_RUNTIME_NMI
163
wdog_cmsdk_apb_has_fired(void)164 static int wdog_cmsdk_apb_has_fired(void)
165 {
166 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
167
168 return (wdog->maskintstat & CMSDK_APB_WDOG_MASKINTSTAT) != 0U;
169 }
170
wdog_cmsdk_apb_isr(void)171 static void wdog_cmsdk_apb_isr(void)
172 {
173 /*
174 * Check if the watchdog was the reason of the NMI interrupt
175 * and if not, exit immediately
176 */
177 if (!wdog_cmsdk_apb_has_fired()) {
178 printk("NMI received! Rebooting...\n");
179 /* In ARM implementation sys_reboot ignores the parameter */
180 sys_reboot(0);
181 } else {
182 if (user_cb != NULL) {
183 user_cb(wdog_r, 0);
184 }
185 }
186 }
187 #endif /* CONFIG_RUNTIME_NMI */
188
wdog_cmsdk_apb_init(const struct device * dev)189 static int wdog_cmsdk_apb_init(const struct device *dev)
190 {
191 volatile struct wdog_cmsdk_apb *wdog = WDOG_STRUCT;
192
193 wdog_r = dev;
194
195 /* unlock access to configuration registers */
196 wdog_cmsdk_apb_unlock(dev);
197
198 /* set default reload value */
199 wdog->load = reload_cycles;
200
201 #ifdef CONFIG_RUNTIME_NMI
202 /* Configure the interrupts */
203 z_arm_nmi_set_handler(wdog_cmsdk_apb_isr);
204 #endif
205
206 #ifdef CONFIG_WDOG_CMSDK_APB_START_AT_BOOT
207 wdog_cmsdk_apb_setup(dev, 0);
208 #endif
209
210 return 0;
211 }
212
213 DEVICE_DT_INST_DEFINE(0,
214 wdog_cmsdk_apb_init,
215 NULL,
216 NULL, NULL,
217 PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
218 &wdog_cmsdk_apb_api);
219