1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2020 BusyTech
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/param.h>
29 #include <sys/eventhandler.h>
30 #include <sys/kernel.h>
31 #include <sys/types.h>
32 #include <sys/bus.h>
33 #include <sys/module.h>
34 #include <sys/systm.h>
35 #include <sys/sysctl.h>
36 #include <sys/rman.h>
37 #include <sys/resource.h>
38 #include <sys/watchdog.h>
39
40 #include <machine/bus.h>
41 #include <machine/resource.h>
42
43 #include <dev/extres/clk/clk.h>
44 #include <dev/fdt/fdt_common.h>
45 #include <dev/ofw/openfirm.h>
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48
49 /* Registers */
50 #define DWWDT_CR 0x00
51 #define DWWDT_CR_WDT_EN (1 << 0)
52 #define DWWDT_CR_RESP_MODE (1 << 1)
53 #define DWWDT_TORR 0x04
54 #define DWWDT_CCVR 0x08
55 #define DWWDT_CRR 0x0C
56 #define DWWDT_CRR_KICK 0x76
57 #define DWWDT_STAT 0x10
58 #define DWWDT_STAT_STATUS 0x01
59 #define DWWDT_EOI 0x14
60
61 #define DWWDT_READ4(sc, reg) bus_read_4((sc)->sc_mem_res, (reg))
62 #define DWWDT_WRITE4(sc, reg, val) \
63 bus_write_4((sc)->sc_mem_res, (reg), (val))
64
65 /*
66 * 47 = 16 (timeout shift of dwwdt) + 30 (1s ~= 2 ** 30ns) + 1
67 * (pre-restart delay)
68 */
69 #define DWWDT_EXP_OFFSET 47
70
71 struct dwwdt_softc {
72 device_t sc_dev;
73 struct resource *sc_mem_res;
74 struct resource *sc_irq_res;
75 void *sc_intr_cookie;
76 clk_t sc_clk;
77 uint64_t sc_clk_freq;
78 eventhandler_tag sc_evtag;
79 int sc_mem_rid;
80 int sc_irq_rid;
81 enum {
82 DWWDT_STOPPED,
83 DWWDT_RUNNING,
84 } sc_status;
85 };
86
87 static devclass_t dwwdt_devclass;
88
89 static struct ofw_compat_data compat_data[] = {
90 { "snps,dw-wdt", 1 },
91 { NULL, 0 }
92 };
93
94 SYSCTL_NODE(_dev, OID_AUTO, dwwdt, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
95 "Synopsys Designware watchdog timer");
96
97 /* Setting this to true disables full restart mode. */
98 static bool dwwdt_prevent_restart = false;
99 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, prevent_restart, CTLFLAG_RW | CTLFLAG_MPSAFE,
100 &dwwdt_prevent_restart, 0, "Disable system reset on timeout");
101
102 static bool dwwdt_debug_enabled = false;
103 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_MPSAFE,
104 &dwwdt_debug_enabled, 0, "Enable debug mode");
105
106 static bool dwwdt_panic_first = true;
107 SYSCTL_BOOL(_dev_dwwdt, OID_AUTO, panic_first, CTLFLAG_RW | CTLFLAG_MPSAFE,
108 &dwwdt_panic_first, 0,
109 "Try to panic on timeout, reset on another timeout");
110
111 static int dwwdt_probe(device_t);
112 static int dwwdt_attach(device_t);
113 static int dwwdt_detach(device_t);
114 static int dwwdt_shutdown(device_t);
115
116 static void dwwdt_intr(void *);
117 static void dwwdt_event(void *, unsigned int, int *);
118
119 /* Helpers */
120 static inline void dwwdt_start(struct dwwdt_softc *sc);
121 static inline bool dwwdt_started(const struct dwwdt_softc *sc);
122 static inline void dwwdt_stop(struct dwwdt_softc *sc);
123 static inline void dwwdt_set_timeout(const struct dwwdt_softc *sc, int val);
124
125 static void dwwdt_debug(device_t);
126
127 static void
dwwdt_debug(device_t dev)128 dwwdt_debug(device_t dev)
129 {
130 /*
131 * Reading from EOI may clear interrupt flag.
132 */
133 const struct dwwdt_softc *sc = device_get_softc(dev);
134
135 device_printf(dev, "Registers dump: \n");
136 device_printf(dev, " CR: %08x\n", DWWDT_READ4(sc, DWWDT_CR));
137 device_printf(dev, " CCVR: %08x\n", DWWDT_READ4(sc, DWWDT_CCVR));
138 device_printf(dev, " CRR: %08x\n", DWWDT_READ4(sc, DWWDT_CRR));
139 device_printf(dev, " STAT: %08x\n", DWWDT_READ4(sc, DWWDT_STAT));
140
141 device_printf(dev, "Clock: %s\n", clk_get_name(sc->sc_clk));
142 device_printf(dev, " FREQ: %lu\n", sc->sc_clk_freq);
143 }
144
145 static inline bool
dwwdt_started(const struct dwwdt_softc * sc)146 dwwdt_started(const struct dwwdt_softc *sc)
147 {
148
149 /* CR_WDT_E bit can be clear only by full CPU reset. */
150 return ((DWWDT_READ4(sc, DWWDT_CR) & DWWDT_CR_WDT_EN) != 0);
151 }
152
153 static void inline
dwwdt_start(struct dwwdt_softc * sc)154 dwwdt_start(struct dwwdt_softc *sc)
155 {
156 uint32_t val;
157
158 /* Enable watchdog */
159 val = DWWDT_READ4(sc, DWWDT_CR);
160 val |= DWWDT_CR_WDT_EN | DWWDT_CR_RESP_MODE;
161 DWWDT_WRITE4(sc, DWWDT_CR, val);
162 sc->sc_status = DWWDT_RUNNING;
163 }
164
165 static void inline
dwwdt_stop(struct dwwdt_softc * sc)166 dwwdt_stop(struct dwwdt_softc *sc)
167 {
168
169 sc->sc_status = DWWDT_STOPPED;
170 dwwdt_set_timeout(sc, 0x0f);
171 }
172
173 static void inline
dwwdt_set_timeout(const struct dwwdt_softc * sc,int val)174 dwwdt_set_timeout(const struct dwwdt_softc *sc, int val)
175 {
176
177 DWWDT_WRITE4(sc, DWWDT_TORR, val);
178 DWWDT_WRITE4(sc, DWWDT_CRR, DWWDT_CRR_KICK);
179 }
180
181 static void
dwwdt_intr(void * arg)182 dwwdt_intr(void *arg)
183 {
184 struct dwwdt_softc *sc = arg;
185
186 KASSERT((DWWDT_READ4(sc, DWWDT_STAT) & DWWDT_STAT_STATUS) != 0,
187 ("Missing interrupt status bit?"));
188
189 if (dwwdt_prevent_restart || sc->sc_status == DWWDT_STOPPED) {
190 /*
191 * Confirm interrupt reception. Restart counter.
192 * This also emulates stopping watchdog.
193 */
194 (void)DWWDT_READ4(sc, DWWDT_EOI);
195 return;
196 }
197
198 if (dwwdt_panic_first)
199 panic("dwwdt pre-timeout interrupt");
200 }
201
202 static void
dwwdt_event(void * arg,unsigned int cmd,int * error)203 dwwdt_event(void *arg, unsigned int cmd, int *error)
204 {
205 struct dwwdt_softc *sc = arg;
206 const int exponent = flsl(sc->sc_clk_freq);
207 int timeout;
208 int val;
209
210 timeout = cmd & WD_INTERVAL;
211 val = MAX(0, timeout + exponent - DWWDT_EXP_OFFSET + 1);
212
213 dwwdt_stop(sc);
214 if (cmd == 0 || val > 0x0f) {
215 /*
216 * Set maximum time between interrupts and Leave watchdog
217 * disabled.
218 */
219 return;
220 }
221
222 dwwdt_set_timeout(sc, val);
223 dwwdt_start(sc);
224 *error = 0;
225
226 if (dwwdt_debug_enabled)
227 dwwdt_debug(sc->sc_dev);
228 }
229
230 static int
dwwdt_probe(device_t dev)231 dwwdt_probe(device_t dev)
232 {
233 if (!ofw_bus_status_okay(dev))
234 return (ENXIO);
235
236 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
237 return (ENXIO);
238
239 device_set_desc(dev, "Synopsys Designware watchdog timer");
240 return (BUS_PROBE_DEFAULT);
241 }
242
243 static int
dwwdt_attach(device_t dev)244 dwwdt_attach(device_t dev)
245 {
246 struct dwwdt_softc *sc;
247
248 sc = device_get_softc(dev);
249 sc->sc_dev = dev;
250
251 sc->sc_mem_rid = 0;
252 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
253 &sc->sc_mem_rid, RF_ACTIVE);
254 if (sc->sc_mem_res == NULL) {
255 device_printf(dev, "cannot allocate memory resource\n");
256 goto err_no_mem;
257 }
258
259 sc->sc_irq_rid = 0;
260 sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
261 &sc->sc_irq_rid, RF_ACTIVE);
262 if (sc->sc_irq_res == NULL) {
263 device_printf(dev, "cannot allocate ireq resource\n");
264 goto err_no_irq;
265 }
266
267 sc->sc_intr_cookie = NULL;
268 if (bus_setup_intr(dev, sc->sc_irq_res, INTR_MPSAFE | INTR_TYPE_MISC,
269 NULL, dwwdt_intr, sc, &sc->sc_intr_cookie) != 0) {
270 device_printf(dev, "cannot setup interrupt routine\n");
271 goto err_no_intr;
272 }
273
274 if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) {
275 device_printf(dev, "cannot find clock\n");
276 goto err_no_clock;
277 }
278
279 if (clk_enable(sc->sc_clk) != 0) {
280 device_printf(dev, "cannot enable clock\n");
281 goto err_no_freq;
282 }
283
284 if (clk_get_freq(sc->sc_clk, &sc->sc_clk_freq) != 0) {
285 device_printf(dev, "cannot get clock frequency\n");
286 goto err_no_freq;
287 }
288
289 if (sc->sc_clk_freq == 0UL)
290 goto err_no_freq;
291
292 sc->sc_evtag = EVENTHANDLER_REGISTER(watchdog_list, dwwdt_event, sc, 0);
293 sc->sc_status = DWWDT_STOPPED;
294
295 return (bus_generic_attach(dev));
296
297 err_no_freq:
298 clk_release(sc->sc_clk);
299 err_no_clock:
300 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
301 err_no_intr:
302 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid, sc->sc_irq_res);
303 err_no_irq:
304 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
305 sc->sc_mem_res);
306 err_no_mem:
307 return (ENXIO);
308 }
309
310 static int
dwwdt_detach(device_t dev)311 dwwdt_detach(device_t dev)
312 {
313 struct dwwdt_softc *sc = device_get_softc(dev);
314
315 if (dwwdt_started(sc)) {
316 /*
317 * Once started it cannot be stopped. Prevent module unload
318 * instead.
319 */
320 return (EBUSY);
321 }
322
323 EVENTHANDLER_DEREGISTER(watchdog_list, sc->sc_evtag);
324 sc->sc_evtag = NULL;
325
326 if (sc->sc_clk != NULL)
327 clk_release(sc->sc_clk);
328
329 if (sc->sc_intr_cookie != NULL)
330 bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intr_cookie);
331
332 if (sc->sc_irq_res) {
333 bus_release_resource(dev, SYS_RES_IRQ, sc->sc_irq_rid,
334 sc->sc_irq_res);
335 }
336
337 if (sc->sc_mem_res) {
338 bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
339 sc->sc_mem_res);
340 }
341
342 return (bus_generic_detach(dev));
343 }
344
345 static int
dwwdt_shutdown(device_t dev)346 dwwdt_shutdown(device_t dev)
347 {
348 struct dwwdt_softc *sc;
349
350 sc = device_get_softc(dev);
351
352 /* Prevent restarts during shutdown. */
353 dwwdt_prevent_restart = true;
354 dwwdt_stop(sc);
355 return (bus_generic_shutdown(dev));
356 }
357
358 static device_method_t dwwdt_methods[] = {
359 DEVMETHOD(device_probe, dwwdt_probe),
360 DEVMETHOD(device_attach, dwwdt_attach),
361 DEVMETHOD(device_detach, dwwdt_detach),
362 DEVMETHOD(device_shutdown, dwwdt_shutdown),
363
364 {0, 0}
365 };
366
367 static driver_t dwwdt_driver = {
368 "dwwdt",
369 dwwdt_methods,
370 sizeof(struct dwwdt_softc),
371 };
372
373 DRIVER_MODULE(dwwdt, simplebus, dwwdt_driver, dwwdt_devclass, NULL, NULL);
374 MODULE_VERSION(dwwdt, 1);
375 OFWBUS_PNP_INFO(compat_data);
376