xref: /freebsd/sys/dev/wdatwd/wdatwd.c (revision 685dc743)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2022 Tetsuya Uemura <t_uemura@macome.co.jp>
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/cdefs.h>
29 #include "opt_acpi.h"
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/callout.h>
35 #include <sys/eventhandler.h>
36 #include <sys/interrupt.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.h>
39 #include <sys/module.h>
40 #include <sys/queue.h>
41 #include <sys/rman.h>
42 #include <sys/sysctl.h>
43 #include <sys/watchdog.h>
44 #include <vm/vm.h>
45 #include <vm/pmap.h>
46 
47 #include <contrib/dev/acpica/include/acpi.h>
48 #include <contrib/dev/acpica/include/accommon.h>
49 #include <contrib/dev/acpica/include/aclocal.h>
50 #include <contrib/dev/acpica/include/actables.h>
51 
52 #include <dev/acpica/acpivar.h>
53 
54 /*
55  * Resource entry. Every instruction has the corresponding ACPI GAS but two or
56  * more instructions may access the same or adjacent register region(s). So we
57  * need to merge all the specified resources.
58  *
59  * res   Resource when allocated.
60  * start Region start address.
61  * end   Region end address + 1.
62  * rid   Resource rid assigned when allocated.
63  * type  ACPI resource type, SYS_RES_IOPORT or SYS_RES_MEMORY.
64  * link  Next/previous resource entry.
65  */
66 struct wdat_res {
67 	struct resource		*res;
68 	uint64_t		start;
69 	uint64_t		end;
70 	int			rid;
71 	int			type;
72 	TAILQ_ENTRY(wdat_res)	link;
73 };
74 
75 /*
76  * Instruction entry. Every instruction itself is actually a single register
77  * read or write (and subsequent bit operation(s)).
78  * 0 or more instructions are tied to every watchdog action and once an action
79  * is kicked, the corresponding entries run sequentially.
80  *
81  * entry Permanent copy of ACPI_WDAT_ENTRY entry (sub-table).
82  * next  Next instruction entry.
83  */
84 struct wdat_instr {
85 	ACPI_WDAT_ENTRY		entry;
86 	STAILQ_ENTRY(wdat_instr) next;
87 };
88 
89 /*
90  * dev             Watchdog device.
91  * wdat            ACPI WDAT table, can be accessed until AcpiPutTable().
92  * default_timeout BIOS configured watchdog ticks to fire.
93  * timeout         User configured timeout in millisecond or 0 if isn't set.
94  * max             Max. supported watchdog ticks to be set.
95  * min             Min. supported watchdog ticks to be set.
96  * period          Milliseconds per watchdog tick.
97  * running         True if this watchdog is running or false if stopped.
98  * stop_in_sleep   False if this watchdog keeps counting down during sleep.
99  * ev_tag          Tag for EVENTHANDLER_*().
100  * action          Array of watchdog instruction sets, each indexed by action.
101  */
102 struct wdatwd_softc {
103 	device_t		dev;
104 	ACPI_TABLE_WDAT		*wdat;
105 	uint64_t		default_timeout;
106 	uint64_t		timeout;
107 	u_int			max;
108 	u_int			min;
109 	u_int			period;
110 	bool			running;
111 	bool			stop_in_sleep;
112 	eventhandler_tag	ev_tag;
113 	STAILQ_HEAD(, wdat_instr) action[ACPI_WDAT_ACTION_RESERVED];
114 	TAILQ_HEAD(res_head, wdat_res) res;
115 };
116 
117 #define WDATWD_VERBOSE_PRINTF(dev, ...)					\
118 	do {								\
119 		if (bootverbose)					\
120 			device_printf(dev, __VA_ARGS__);		\
121 	} while (0)
122 
123 /*
124  * Do requested action.
125  */
126 static int
wdatwd_action(const struct wdatwd_softc * sc,const u_int action,const uint64_t val,uint64_t * ret)127 wdatwd_action(const struct wdatwd_softc *sc, const u_int action, const uint64_t val, uint64_t *ret)
128 {
129 	struct wdat_instr	*wdat;
130 	const char		*rw = NULL;
131 	ACPI_STATUS		status;
132 
133 	if (STAILQ_EMPTY(&sc->action[action])) {
134 		WDATWD_VERBOSE_PRINTF(sc->dev,
135 		    "action not supported: 0x%02x\n", action);
136 		return (EOPNOTSUPP);
137 	}
138 
139 	STAILQ_FOREACH(wdat, &sc->action[action], next) {
140 		ACPI_GENERIC_ADDRESS	*gas = &wdat->entry.RegisterRegion;
141 		uint64_t		x, y;
142 
143 		switch (wdat->entry.Instruction
144 		    & ~ACPI_WDAT_PRESERVE_REGISTER) {
145 		    case ACPI_WDAT_READ_VALUE:
146 			status = AcpiRead(&x, gas);
147 			if (ACPI_FAILURE(status)) {
148 				rw = "AcpiRead";
149 				goto fail;
150 			}
151 			x >>= gas->BitOffset;
152 			x &= wdat->entry.Mask;
153 			*ret = (x == wdat->entry.Value) ? 1 : 0;
154 			break;
155 		    case ACPI_WDAT_READ_COUNTDOWN:
156 			status = AcpiRead(&x, gas);
157 			if (ACPI_FAILURE(status)) {
158 				rw = "AcpiRead";
159 				goto fail;
160 			}
161 			x >>= gas->BitOffset;
162 			x &= wdat->entry.Mask;
163 			*ret = x;
164 			break;
165 		    case ACPI_WDAT_WRITE_VALUE:
166 			x = wdat->entry.Value & wdat->entry.Mask;
167 			x <<= gas->BitOffset;
168 			if (wdat->entry.Instruction
169 			    & ACPI_WDAT_PRESERVE_REGISTER) {
170 				status = AcpiRead(&y, gas);
171 				if (ACPI_FAILURE(status)) {
172 					rw = "AcpiRead";
173 					goto fail;
174 				}
175 				y &= ~(wdat->entry.Mask << gas->BitOffset);
176 				x |= y;
177 			}
178 			status = AcpiWrite(x, gas);
179 			if (ACPI_FAILURE(status)) {
180 				rw = "AcpiWrite";
181 				goto fail;
182 			}
183 			break;
184 		    case ACPI_WDAT_WRITE_COUNTDOWN:
185 			x = val & wdat->entry.Mask;
186 			x <<= gas->BitOffset;
187 			if (wdat->entry.Instruction
188 			    & ACPI_WDAT_PRESERVE_REGISTER) {
189 				status = AcpiRead(&y, gas);
190 				if (ACPI_FAILURE(status)) {
191 					rw = "AcpiRead";
192 					goto fail;
193 				}
194 				y &= ~(wdat->entry.Mask << gas->BitOffset);
195 				x |= y;
196 			}
197 			status = AcpiWrite(x, gas);
198 			if (ACPI_FAILURE(status)) {
199 				rw = "AcpiWrite";
200 				goto fail;
201 			}
202 			break;
203 		    default:
204 			return (EINVAL);
205 		}
206 	}
207 
208 	return (0);
209 
210 fail:
211 	device_printf(sc->dev, "action: 0x%02x, %s() returned: %d\n",
212 	    action, rw, status);
213 	return (ENXIO);
214 }
215 
216 /*
217  * Reset the watchdog countdown.
218  */
219 static int
wdatwd_reset_countdown(const struct wdatwd_softc * sc)220 wdatwd_reset_countdown(const struct wdatwd_softc *sc)
221 {
222 	return wdatwd_action(sc, ACPI_WDAT_RESET, 0, NULL);
223 }
224 
225 /*
226  * Set the watchdog countdown value. In WDAT specification, this is optional.
227  */
228 static int
wdatwd_set_countdown(struct wdatwd_softc * sc,u_int cmd)229 wdatwd_set_countdown(struct wdatwd_softc *sc, u_int cmd)
230 {
231 	uint64_t		timeout;
232 	int			e;
233 
234 	cmd &= WD_INTERVAL;
235 	timeout = ((uint64_t) 1 << cmd) / 1000000 / sc->period;
236 	if (timeout > sc->max)
237 		timeout = sc->max;
238 	else if (timeout < sc->min)
239 		timeout = sc->min;
240 
241 	e = wdatwd_action(sc, ACPI_WDAT_SET_COUNTDOWN, timeout, NULL);
242 	if (e == 0)
243 		sc->timeout = timeout * sc->period;
244 
245 	return (e);
246 }
247 
248 /*
249  * Get the watchdog current countdown value.
250  */
251 static int
wdatwd_get_current_countdown(const struct wdatwd_softc * sc,uint64_t * timeout)252 wdatwd_get_current_countdown(const struct wdatwd_softc *sc, uint64_t *timeout)
253 {
254 	return wdatwd_action(sc, ACPI_WDAT_GET_CURRENT_COUNTDOWN, 0, timeout);
255 }
256 
257 /*
258  * Get the watchdog countdown value the watchdog is configured to fire.
259  */
260 static int
wdatwd_get_countdown(const struct wdatwd_softc * sc,uint64_t * timeout)261 wdatwd_get_countdown(const struct wdatwd_softc *sc, uint64_t *timeout)
262 {
263 	return wdatwd_action(sc, ACPI_WDAT_GET_COUNTDOWN, 0, timeout);
264 }
265 
266 /*
267  * Set the watchdog to running state.
268  */
269 static int
wdatwd_set_running(struct wdatwd_softc * sc)270 wdatwd_set_running(struct wdatwd_softc *sc)
271 {
272 	int			e;
273 
274 	e = wdatwd_action(sc, ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
275 	if (e == 0)
276 		sc->running = true;
277 	return (e);
278 }
279 
280 /*
281  * Set the watchdog to stopped state.
282  */
283 static int
wdatwd_set_stop(struct wdatwd_softc * sc)284 wdatwd_set_stop(struct wdatwd_softc *sc)
285 {
286 	int			e;
287 
288 	e = wdatwd_action(sc, ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
289 	if (e == 0)
290 		sc->running = false;
291 	return (e);
292 }
293 
294 /*
295  * Clear the watchdog's boot status if the current boot was caused by the
296  * watchdog firing.
297  */
298 static int
wdatwd_clear_status(const struct wdatwd_softc * sc)299 wdatwd_clear_status(const struct wdatwd_softc *sc)
300 {
301 	return wdatwd_action(sc, ACPI_WDAT_SET_STATUS, 0, NULL);
302 }
303 
304 /*
305  * Set the watchdog to reboot when it is fired.
306  */
307 static int
wdatwd_set_reboot(const struct wdatwd_softc * sc)308 wdatwd_set_reboot(const struct wdatwd_softc *sc)
309 {
310 	return wdatwd_action(sc, ACPI_WDAT_SET_REBOOT, 0, NULL);
311 }
312 
313 /*
314  * Watchdog event handler.
315  */
316 static void
wdatwd_event(void * private,u_int cmd,int * error)317 wdatwd_event(void *private, u_int cmd, int *error)
318 {
319 	struct wdatwd_softc	*sc = private;
320 	uint64_t		cur[2], cnt[2];
321 	bool			run[2];
322 
323 	if (bootverbose) {
324 		run[0] = sc->running;
325 		if (wdatwd_get_countdown(sc, &cnt[0]) != 0)
326 			cnt[0] = 0;
327 		if (wdatwd_get_current_countdown(sc, &cur[0]) != 0)
328 			cur[0] = 0;
329 	}
330 
331 	if ((cmd & WD_INTERVAL) == 0)
332 		wdatwd_set_stop(sc);
333 	else {
334 		if (!sc->running) {
335 			/* ACPI_WDAT_SET_COUNTDOWN may not be implemented. */
336 			wdatwd_set_countdown(sc, cmd);
337 			wdatwd_set_running(sc);
338 			/*
339 			 * In the first wdatwd_event() call, it sets the
340 			 * watchdog timeout to a considerably larger value such
341 			 * as 137 seconds, then kicks the watchdog to start
342 			 * counting down. Weirdly though, on a Dell R210 BIOS
343 			 * 1.12.0, a supplemental reset action must be
344 			 * triggered for the newly set timeout value to take
345 			 * effect. Without it, the watchdog fires 2.4 seconds
346 			 * after starting, where 2.4 seconds is its initially
347 			 * set timeout. This failure scenario is seen by first
348 			 * starting watchdogd(8) without wdatwd registered then
349 			 * kldload it. In steady state, watchdogd pats the
350 			 * watchdog every 10 or so seconds which is much longer
351 			 * than 2.4 seconds timeout.
352 			 */
353 		}
354 		wdatwd_reset_countdown(sc);
355 	}
356 
357 	if (bootverbose) {
358 		run[1] = sc->running;
359 		if (wdatwd_get_countdown(sc, &cnt[1]) != 0)
360 			cnt[1] = 0;
361 		if (wdatwd_get_current_countdown(sc, &cur[1]) != 0)
362 			cur[1] = 0;
363 		WDATWD_VERBOSE_PRINTF(sc->dev, "cmd: %u, sc->running: "
364 		    "%d -> %d, cnt: %llu -> %llu, cur: %llu -> %llu\n", cmd,
365 				      run[0], run[1],
366 				      (unsigned long long) cnt[0],
367 				      (unsigned long long) cnt[1],
368 				      (unsigned long long)cur[0],
369 				      (unsigned long long)cur[1]);
370 	}
371 
372 	return;
373 }
374 
375 static ssize_t
wdat_set_action(struct wdatwd_softc * sc,ACPI_WDAT_ENTRY * addr,ssize_t remaining)376 wdat_set_action(struct wdatwd_softc *sc, ACPI_WDAT_ENTRY *addr, ssize_t remaining)
377 {
378 	ACPI_WDAT_ENTRY		*entry = addr;
379 	struct wdat_instr	*wdat;
380 
381 	if (remaining < sizeof(ACPI_WDAT_ENTRY))
382 		return (-EINVAL);
383 
384 	/* Skip actions beyond specification. */
385 	if (entry->Action < nitems(sc->action)) {
386 		wdat = malloc(sizeof(*wdat), M_DEVBUF, M_WAITOK | M_ZERO);
387 		wdat->entry = *entry;
388 		STAILQ_INSERT_TAIL(&sc->action[entry->Action], wdat, next);
389 	}
390 	return sizeof(ACPI_WDAT_ENTRY);
391 }
392 
393 /*
394  * Transform every ACPI_WDAT_ENTRY to wdat_instr by calling wdat_set_action().
395  */
396 static void
wdat_parse_action_table(struct wdatwd_softc * sc)397 wdat_parse_action_table(struct wdatwd_softc *sc)
398 {
399 	ACPI_TABLE_WDAT		*wdat = sc->wdat;
400 	ssize_t			remaining, consumed;
401 	char			*cp;
402 
403 	remaining = wdat->Header.Length - sizeof(ACPI_TABLE_WDAT);
404 	while (remaining > 0) {
405 		cp = (char *)wdat + wdat->Header.Length - remaining;
406 		consumed = wdat_set_action(sc, (ACPI_WDAT_ENTRY *)cp,
407 		    remaining);
408 		if (consumed < 0) {
409 			device_printf(sc->dev, "inconsistent WDAT table.\n");
410 			break;
411 		}
412 			remaining -= consumed;
413 	}
414 }
415 
416 /*
417  * Decode the given GAS rr and set its type, start and end (actually end + 1)
418  * in the newly malloc()'ed res.
419  */
420 static struct wdat_res *
wdat_alloc_region(ACPI_GENERIC_ADDRESS * rr)421 wdat_alloc_region(ACPI_GENERIC_ADDRESS *rr)
422 {
423 	struct wdat_res *res;
424 
425 	if (rr->AccessWidth < 1 || rr->AccessWidth > 4)
426 		return (NULL);
427 
428 	res = malloc(sizeof(*res),
429 	    M_DEVBUF, M_WAITOK | M_ZERO);
430 	if (res != NULL) {
431 		res->start = rr->Address;
432 		res->end   = res->start + (1 << (rr->AccessWidth - 1));
433 		res->type  = rr->SpaceId;
434 	}
435 	return (res);
436 }
437 
438 #define OVERLAP_NONE	0x0 // no overlap.
439 #define OVERLAP_SUBSET	0x1 // res2 is fully covered by res1.
440 #define OVERLAP_START	0x2 // the start of res2 is overlaped.
441 #define OVERLAP_END	0x4 // the end of res2 is overlapped.
442 
443 /*
444  * Compare the given res1 and res2, and one of the above OVERLAP_* constant, or
445  * in case res2 is larger than res1 at both the start and the end,
446  * OVERLAP_START | OVERLAP_END, is returned.
447  */
448 static int
wdat_compare_region(const struct wdat_res * res1,const struct wdat_res * res2)449 wdat_compare_region(const struct wdat_res *res1, const struct wdat_res *res2)
450 {
451 	int overlap;
452 
453 	/*
454 	 * a) both have different resource type. == OVERLAP_NONE
455 	 * b) res2 and res1 have no overlap.     == OVERLAP_NONE
456 	 * c) res2 is fully covered by res1.     == OVERLAP_SUBSET
457 	 * d) res2 and res1 overlap partially.   == OVERLAP_START or
458 	 * 					    OVERLAP_END
459 	 * e) res2 fully covers res1.            == OVERLAP_START | OVERLAP_END
460 	 */
461 	overlap = 0;
462 
463 	if (res1->type != res2->type || res1->start > res2->end
464 	    || res1->end < res2->start)
465 		overlap |= OVERLAP_NONE;
466 	else {
467 		if (res1->start <= res2->start && res1->end >= res2->end)
468 			overlap |= OVERLAP_SUBSET;
469 		if (res1->start > res2->start)
470 			overlap |= OVERLAP_START;
471 		if (res1->end < res2->end)
472 			overlap |= OVERLAP_END;
473 	}
474 
475 	return (overlap);
476 }
477 
478 /*
479  * Try to merge the given newres with the existing sc->res.
480  */
481 static void
wdat_merge_region(struct wdatwd_softc * sc,struct wdat_res * newres)482 wdat_merge_region(struct wdatwd_softc *sc, struct wdat_res *newres)
483 {
484 	struct wdat_res		*res1, *res2, *res_safe, *res_itr;
485 	int			overlap;
486 
487 	if (TAILQ_EMPTY(&sc->res)) {
488 		TAILQ_INSERT_HEAD(&sc->res, newres, link);
489 		return;
490 	}
491 
492 	overlap = OVERLAP_NONE;
493 
494 	TAILQ_FOREACH_SAFE(res1, &sc->res, link, res_safe) {
495 		overlap = wdat_compare_region(res1, newres);
496 
497 		/* Try next res if newres isn't mergeable. */
498 		if (overlap == OVERLAP_NONE)
499 			continue;
500 
501 		/* This res fully covers newres. */
502 		if (overlap == OVERLAP_SUBSET)
503 			break;
504 
505 		/* Newres extends the existing res res1 to lower. */
506 		if ((overlap & OVERLAP_START)) {
507 			res1->start = newres->start;
508 			res_itr = res1;
509 			/* Try to merge more res if possible. */
510 			while ((res2 = TAILQ_PREV(res_itr, res_head, link))) {
511 				if (res1->type != res2->type) {
512 					res_itr = res2;
513 					continue;
514 				} else if (res1->start <= res2->end) {
515 					res1->start = res2->start;
516 					TAILQ_REMOVE(&sc->res, res2, link);
517 					free(res2, M_DEVBUF);
518 				} else
519 					break;
520 			}
521 		}
522 		/* Newres extends the existing res res1 to upper. */
523 		if ((overlap & OVERLAP_END)) {
524 			res1->end = newres->end;
525 			res_itr = res1;
526 			/* Try to merge more res if possible. */
527 			while ((res2 = TAILQ_NEXT(res_itr, link))) {
528 				if (res1->type != res2->type) {
529 					res_itr = res2;
530 					continue;
531 				} else if (res1->end >= res2->start) {
532 					res1->end = res2->end;
533 					TAILQ_REMOVE(&sc->res, res2, link);
534 					free(res2, M_DEVBUF);
535 				} else
536 					break;
537 			}
538 		}
539 		break;
540 	}
541 
542 	/*
543 	 * If newres extends the existing res, newres must be free()'ed.
544 	 * Otherwise insert newres into sc->res at appropriate position
545 	 * (the lowest address region appears first).
546 	 */
547 	if (overlap > OVERLAP_NONE)
548 		free(newres, M_DEVBUF);
549 	else {
550 		TAILQ_FOREACH(res1, &sc->res, link) {
551 			if (newres->type != res1->type)
552 				continue;
553 			if (newres->start < res1->start) {
554 				TAILQ_INSERT_BEFORE(res1, newres, link);
555 				break;
556 			}
557 		}
558 		if (res1 == NULL)
559 			TAILQ_INSERT_TAIL(&sc->res, newres, link);
560 	}
561 }
562 
563 /*
564  * Release the already allocated resource.
565  */
566 static void
wdat_release_resource(device_t dev)567 wdat_release_resource(device_t dev)
568 {
569 	struct wdatwd_softc	*sc;
570 	struct wdat_instr	*wdat;
571 	struct wdat_res		*res;
572 	int			i;
573 
574 	sc = device_get_softc(dev);
575 
576 	TAILQ_FOREACH(res, &sc->res, link)
577 		if (res->res != NULL) {
578 			bus_release_resource(dev, res->type,
579 			    res->rid, res->res);
580 			bus_delete_resource(dev, res->type, res->rid);
581 			res->res = NULL;
582 		}
583 
584 	for (i = 0; i < nitems(sc->action); ++i)
585 		while (!STAILQ_EMPTY(&sc->action[i])) {
586 			wdat = STAILQ_FIRST(&sc->action[i]);
587 			STAILQ_REMOVE_HEAD(&sc->action[i], next);
588 			free(wdat, M_DEVBUF);
589 		}
590 
591 	while (!TAILQ_EMPTY(&sc->res)) {
592 		res = TAILQ_FIRST(&sc->res);
593 		TAILQ_REMOVE(&sc->res, res, link);
594 		free(res, M_DEVBUF);
595 	}
596 }
597 
598 static int
wdatwd_probe(device_t dev)599 wdatwd_probe(device_t dev)
600 {
601 	ACPI_TABLE_WDAT		*wdat;
602 	ACPI_STATUS		status;
603 
604 	/* Without WDAT table we have nothing to do. */
605 	status = AcpiGetTable(ACPI_SIG_WDAT, 0, (ACPI_TABLE_HEADER **)&wdat);
606 	if (ACPI_FAILURE(status))
607 		return (ENXIO);
608 
609 	/* Try to allocate one resource and assume wdatwd is already attached
610 	 * if it fails. */
611 	{
612 		int		type, rid = 0;
613 		struct resource *res;
614 
615 		if (acpi_bus_alloc_gas(dev, &type, &rid,
616 		    &((ACPI_WDAT_ENTRY *)(wdat + 1))->RegisterRegion,
617 		    &res, 0))
618 			return (ENXIO);
619 		bus_release_resource(dev, type, rid, res);
620 		bus_delete_resource(dev, type, rid);
621 	}
622 
623 	WDATWD_VERBOSE_PRINTF(dev, "Flags: 0x%x, TimerPeriod: %d ms/cnt, "
624 	    "MaxCount: %d cnt (%d ms), MinCount: %d cnt (%d ms)\n",
625 	    (int)wdat->Flags, (int)wdat->TimerPeriod,
626 	    (int)wdat->MaxCount, (int)(wdat->MaxCount * wdat->TimerPeriod),
627 	    (int)wdat->MinCount, (int)(wdat->MinCount * wdat->TimerPeriod));
628 	/* WDAT timer consistency. */
629 	if ((wdat->TimerPeriod < 1) || (wdat->MinCount > wdat->MaxCount)) {
630 		device_printf(dev, "inconsistent timer variables.\n");
631 		return (EINVAL);
632 	}
633 
634 	AcpiPutTable((ACPI_TABLE_HEADER *)wdat);
635 
636 	device_set_desc(dev, "ACPI WDAT Watchdog Interface");
637 	return (BUS_PROBE_DEFAULT);
638 }
639 
640 static int
wdatwd_attach(device_t dev)641 wdatwd_attach(device_t dev)
642 {
643 	struct wdatwd_softc	*sc;
644 	struct wdat_instr	*wdat;
645 	struct wdat_res		*res;
646 	struct sysctl_ctx_list	*sctx;
647 	struct sysctl_oid	*soid;
648 	ACPI_STATUS		status;
649 	int			e, i, rid;
650 
651 	sc = device_get_softc(dev);
652 	sc->dev = dev;
653 
654 	for (i = 0; i < nitems(sc->action); ++i)
655 		STAILQ_INIT(&sc->action[i]);
656 
657 	/* Search and parse WDAT table. */
658 	status = AcpiGetTable(ACPI_SIG_WDAT, 0,
659 	    (ACPI_TABLE_HEADER **)&sc->wdat);
660 	if (ACPI_FAILURE(status))
661 		return (ENXIO);
662 
663 	/* Parse watchdog variables. */
664 	sc->period = sc->wdat->TimerPeriod;
665 	sc->max = sc->wdat->MaxCount;
666 	sc->min = sc->wdat->MinCount;
667 	sc->stop_in_sleep = (sc->wdat->Flags & ACPI_WDAT_STOPPED)
668 	    ? true : false;
669 	/* Parse defined watchdog actions. */
670 	wdat_parse_action_table(sc);
671 
672 	AcpiPutTable((ACPI_TABLE_HEADER *)sc->wdat);
673 
674 	/* Verbose logging. */
675 	if (bootverbose) {
676 		for (i = 0; i < nitems(sc->action); ++i)
677 			STAILQ_FOREACH(wdat, &sc->action[i], next) {
678 				WDATWD_VERBOSE_PRINTF(dev, "action: 0x%02x, "
679 				    "%s %s at 0x%llx (%d bit(s), offset %d bit(s))\n",
680 				    i,
681 				    wdat->entry.RegisterRegion.SpaceId
682 					== ACPI_ADR_SPACE_SYSTEM_MEMORY
683 					? "mem"
684 					: wdat->entry.RegisterRegion.SpaceId
685 					    == ACPI_ADR_SPACE_SYSTEM_IO
686 					    ? "io "
687 					    : "???",
688 				    wdat->entry.RegisterRegion.AccessWidth == 1
689 					? "byte "
690 					: wdat->entry.RegisterRegion.AccessWidth == 2
691 					    ? "word "
692 					    : wdat->entry.RegisterRegion.AccessWidth == 3
693 						? "dword"
694 						: wdat->entry.RegisterRegion.AccessWidth == 4
695 						    ? "qword"
696 						    : "undef",
697 				    (unsigned long long )
698 				    wdat->entry.RegisterRegion.Address,
699 				    wdat->entry.RegisterRegion.BitWidth,
700 				    wdat->entry.RegisterRegion.BitOffset);
701 		}
702 	}
703 
704 	/* Canonicalize the requested resources. */
705 	TAILQ_INIT(&sc->res);
706 	for (i = 0; i < nitems(sc->action); ++i)
707 		STAILQ_FOREACH(wdat, &sc->action[i], next) {
708 			res = wdat_alloc_region(&wdat->entry.RegisterRegion);
709 			if (res == NULL)
710 				goto fail;
711 			wdat_merge_region(sc, res);
712 		}
713 
714 	/* Resource allocation. */
715 	rid = 0;
716 	TAILQ_FOREACH(res, &sc->res, link) {
717 		switch (res->type) {
718 		    case ACPI_ADR_SPACE_SYSTEM_MEMORY:
719 			res->type = SYS_RES_MEMORY;
720 			break;
721 		    case ACPI_ADR_SPACE_SYSTEM_IO:
722 			res->type = SYS_RES_IOPORT;
723 			break;
724 		    default:
725 			goto fail;
726 		}
727 
728 		res->rid = rid++;
729 		bus_set_resource(dev, res->type, res->rid,
730 		    res->start, res->end - res->start);
731 		res->res = bus_alloc_resource_any(
732 		    dev, res->type, &res->rid, RF_ACTIVE);
733 		if (res->res == NULL) {
734 			bus_delete_resource(dev, res->type, res->rid);
735 			device_printf(dev, "%s at 0x%llx (%lld byte(s)): "
736 			    "alloc' failed\n",
737 			    res->type == SYS_RES_MEMORY ? "mem" : "io ",
738 			    (unsigned long long )res->start,
739 			    (unsigned long long )(res->end - res->start));
740 			goto fail;
741 		}
742 		WDATWD_VERBOSE_PRINTF(dev, "%s at 0x%llx (%lld byte(s)): "
743 		    "alloc'ed\n",
744 		    res->type == SYS_RES_MEMORY ? "mem" : "io ",
745 		    (unsigned long long )res->start,
746 		    (unsigned long long) (res->end - res->start));
747 	}
748 
749 	/* Initialize the watchdog hardware. */
750 	if (wdatwd_set_stop(sc) != 0)
751 		goto fail;
752 	if ((e = wdatwd_clear_status(sc)) && e != EOPNOTSUPP)
753 		goto fail;
754 	if ((e = wdatwd_set_reboot(sc)) && e != EOPNOTSUPP)
755 		goto fail;
756 	if ((e = wdatwd_get_countdown(sc, &sc->default_timeout))
757 	    && e != EOPNOTSUPP)
758 		goto fail;
759 	WDATWD_VERBOSE_PRINTF(dev, "initialized.\n");
760 
761 	/* Some sysctls. Most of them should go to WDATWD_VERBOSE_PRINTF(). */
762 	sctx = device_get_sysctl_ctx(dev);
763 	soid = device_get_sysctl_tree(dev);
764 	SYSCTL_ADD_U64(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
765 	    "timeout_default", CTLFLAG_RD, SYSCTL_NULL_U64_PTR,
766 	    sc->default_timeout * sc->period,
767 	    "The default watchdog timeout in millisecond.");
768 	SYSCTL_ADD_BOOL(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
769 	    "timeout_configurable", CTLFLAG_RD, SYSCTL_NULL_BOOL_PTR,
770 	    STAILQ_EMPTY(&sc->action[ACPI_WDAT_SET_COUNTDOWN]) ? false : true,
771 	    "Whether the watchdog timeout is configurable or not.");
772 	SYSCTL_ADD_U64(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
773 	    "timeout", CTLFLAG_RD, &sc->timeout, 0,
774 	    "The current watchdog timeout in millisecond. "
775 	    "If 0, the default timeout is used.");
776 	SYSCTL_ADD_BOOL(sctx, SYSCTL_CHILDREN(soid), OID_AUTO,
777 	    "running", CTLFLAG_RD, &sc->running, 0,
778 	    "Whether the watchdog timer is running or not.");
779 
780 	sc->ev_tag = EVENTHANDLER_REGISTER(watchdog_list, wdatwd_event, sc,
781 	    EVENTHANDLER_PRI_ANY);
782 	WDATWD_VERBOSE_PRINTF(dev, "watchdog registered.\n");
783 
784 	return (0);
785 
786 fail:
787 	wdat_release_resource(dev);
788 
789 	return (ENXIO);
790 }
791 
792 static int
wdatwd_detach(device_t dev)793 wdatwd_detach(device_t dev)
794 {
795 	struct wdatwd_softc	*sc;
796 	int			e;
797 
798 	sc = device_get_softc(dev);
799 
800 	EVENTHANDLER_DEREGISTER(watchdog_list, sc->ev_tag);
801 	e = wdatwd_set_stop(sc);
802 	wdat_release_resource(dev);
803 
804 	return (e);
805 }
806 
807 static int
wdatwd_suspend(device_t dev)808 wdatwd_suspend(device_t dev)
809 {
810 	struct wdatwd_softc	*sc;
811 
812 	sc = device_get_softc(dev);
813 
814 	if (!sc->stop_in_sleep)
815 		return (0);
816 
817 	return wdatwd_set_stop(sc);
818 }
819 
820 static int
wdatwd_resume(device_t dev)821 wdatwd_resume(device_t dev)
822 {
823 	struct wdatwd_softc	*sc;
824 
825 	sc = device_get_softc(dev);
826 
827 	if (!sc->stop_in_sleep)
828 		return (0);
829 
830 	return (wdatwd_reset_countdown(sc) || wdatwd_set_running(sc));
831 }
832 
833 static device_method_t wdatwd_methods[] = {
834 	/* Device interface */
835 	DEVMETHOD(device_probe, wdatwd_probe),
836 	DEVMETHOD(device_attach, wdatwd_attach),
837 	DEVMETHOD(device_detach, wdatwd_detach),
838 	DEVMETHOD(device_shutdown, wdatwd_detach),
839 	DEVMETHOD(device_suspend, wdatwd_suspend),
840 	DEVMETHOD(device_resume, wdatwd_resume),
841 	DEVMETHOD_END
842 };
843 
844 static driver_t	wdatwd_driver = {
845 	"wdatwd",
846 	wdatwd_methods,
847 	sizeof(struct wdatwd_softc),
848 };
849 
850 DRIVER_MODULE(wdatwd, acpi, wdatwd_driver, 0, 0);
851 MODULE_DEPEND(wdatwd, acpi, 1, 1, 1);
852