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