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);
Cache object: 4b831bc649ff605461371af72e6b2b8c
|