1 /* $OpenBSD: wdt.c,v 1.5 2003/08/15 20:32:17 tedu Exp $ */
2
3 /*-
4 * Copyright (c) 1998,1999 Alex Nash
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30 #include <sys/types.h>
31 #include <sys/param.h>
32 #include <sys/device.h>
33 #include <sys/malloc.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/timeout.h>
37 #include <sys/proc.h>
38
39 #include <machine/bus.h>
40
41 #include <dev/pci/pcivar.h>
42 #include <dev/pci/pcireg.h>
43 #include <dev/pci/pcidevs.h>
44
45 #include <dev/pci/wdt50x.h>
46
47 struct wdt_softc {
48 /* wdt_dev must be the first item in the struct */
49 struct device wdt_dev;
50
51 /* feature set: 0 = none 1 = temp, buzzer, etc. */
52 int features;
53
54 /* unit number (unlikely more than one would be present though) */
55 int unit;
56
57 /* how many processes are in WIOCSCHED */
58 unsigned procs;
59
60 /* watchdog timeout */
61 unsigned timeout_secs;
62 struct timeout timeout;
63
64 /* device access through bus space */
65 bus_space_tag_t iot;
66 bus_space_handle_t ioh;
67 };
68
69 /* externally visible functions */
70 int wdtprobe(struct device *, void *, void *);
71 void wdtattach(struct device *, struct device *, void *);
72 int wdtopen(dev_t, int, int, struct proc *);
73 int wdtclose(dev_t, int, int, struct proc *);
74 int wdtioctl(dev_t, u_long, caddr_t, int, struct proc *);
75
76 /* static functions */
77 static int wdt_is501(struct wdt_softc *wdt);
78 static void wdt_8254_count(struct wdt_softc *wdt, int counter, u_int16_t v);
79 static void wdt_8254_mode(struct wdt_softc *wdt, int counter, int mode);
80 static void wdt_set_timeout(struct wdt_softc *wdt, unsigned seconds);
81 static void wdt_timeout(void *arg);
82 static void wdt_init_timer(struct wdt_softc *wdt);
83 static void wdt_buzzer_off(struct wdt_softc *wdt);
84 static int wdt_read_temperature(struct wdt_softc *wdt);
85 static int wdt_read_status(struct wdt_softc *wdt);
86 static void wdt_display_status(struct wdt_softc *wdt);
87 static int wdt_get_state(struct wdt_softc *wdt, struct wdt_state *state);
88 static void wdt_shutdown(void *arg);
89 static int wdt_sched(struct wdt_softc *wdt, struct proc *p);
90 static void wdt_timer_disable(struct wdt_softc *wdt);
91 static void wdt_timer_enable(struct wdt_softc *wdt, unsigned seconds);
92 #if WDT_DISABLE_BUZZER
93 static void wdt_buzzer_disable(struct wdt_softc *wdt);
94 #else
95 static void wdt_buzzer_enable(struct wdt_softc *wdt);
96 #endif
97
98 struct cfattach wdt_ca = {
99 sizeof(struct wdt_softc), wdtprobe, wdtattach
100 };
101
102 struct cfdriver wdt_cd = {
103 NULL, "wdt", DV_DULL
104 };
105
106 /*
107 * 8254 counter mappings
108 */
109 #define WDT_8254_TC_LO 0 /* low 16 bits of timeout counter */
110 #define WDT_8254_TC_HI 1 /* high 16 bits of timeout counter */
111 #define WDT_8254_BUZZER 2
112
113 /*
114 * WDT500/501 ports
115 */
116 #define WDT_8254_BASE 0
117 #define WDT_8254_CTL (WDT_8254_BASE + 3)
118 #define WDT_DISABLE_TIMER 7
119 #define WDT_ENABLE_TIMER 7
120
121 /*
122 * WDT501 specific ports
123 */
124 #define WDT_STATUS_REG 4
125 #define WDT_START_BUZZER 4
126 #define WDT_TEMPERATURE 5
127 #define WDT_STOP_BUZZER 5
128
129 #define UNIT(dev) (minor(dev))
130
131 int
wdtprobe(parent,match,aux)132 wdtprobe (parent, match, aux)
133 struct device *parent;
134 void *match, *aux;
135 {
136 struct pci_attach_args *const pa = (struct pci_attach_args *)aux;
137
138 if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INDCOMPSRC ||
139 PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_INDCOMPSRC_WDT50x)
140 return(0);
141
142 return(1);
143 }
144
145 void
wdtattach(parent,self,aux)146 wdtattach (parent, self, aux)
147 struct device *parent, *self;
148 void *aux;
149 {
150 struct wdt_softc *wdt = (struct wdt_softc *)self;
151 struct pci_attach_args *const pa = (struct pci_attach_args *)aux;
152 int unit;
153 bus_size_t iosize;
154 bus_addr_t iobase;
155
156 wdt->iot = pa->pa_iot;
157
158 unit = wdt->wdt_dev.dv_unit;
159
160 /* retrieve the I/O region (BAR2) */
161 if (pci_io_find(pa->pa_pc, pa->pa_tag, 0x18, &iobase,
162 &iosize) != 0) {
163 printf("wdt%d: couldn't find PCI I/O region\n", unit);
164 return;
165 }
166
167 /* sanity check I/O size */
168 if (iosize != (bus_size_t)16) {
169 printf("wdt%d: invalid I/O region size\n", unit);
170 return;
171 }
172
173 /* map I/O region */
174 if (bus_space_map(pa->pa_iot, iobase, iosize, 0, &wdt->ioh) != 0) {
175 printf("wdt%d: couldn't map PCI I/O region\n", unit);
176 return;
177 }
178
179 /* initialize the watchdog timer structure */
180 wdt->unit = unit;
181 wdt->procs = 0;
182
183 /* check the feature set available */
184 if (wdt_is501(wdt))
185 wdt->features = 1;
186 else
187 wdt->features = 0;
188
189 /*
190 * register a callback for system shutdown
191 * (we need to disable the watchdog timer during shutdown)
192 */
193 if (shutdownhook_establish(wdt_shutdown, wdt) == NULL)
194 return;
195
196 if (wdt->features) {
197 /*
198 * turn off the buzzer, it may have been activated
199 * by a previous timeout
200 */
201 wdt_buzzer_off(wdt);
202
203 #ifdef WDT_DISABLE_BUZZER
204 wdt_buzzer_disable(wdt);
205 #else
206 wdt_buzzer_enable(wdt);
207 #endif
208 }
209
210 /* initialize the timer modes and the lower 16-bit counter */
211 wdt_init_timer(wdt);
212
213 /*
214 * it appears the timeout queue isn't processed until the
215 * kernel has fully booted, so we set the first timeout
216 * far in advance, and subsequent timeouts at the normal
217 * 30 second interval
218 */
219 wdt_timer_enable(wdt, 90/*seconds*/);
220 wdt->timeout_secs = 30;
221
222 printf("\n");
223 wdt_display_status(wdt);
224 }
225
226 int
wdtopen(dev_t dev,int flags,int fmt,struct proc * p)227 wdtopen (dev_t dev, int flags, int fmt, struct proc *p)
228 {
229 if (UNIT(dev) >= wdt_cd.cd_ndevs)
230 return(ENXIO);
231
232 return(0);
233 }
234
235 int
wdtclose(dev_t dev,int flags,int fmt,struct proc * p)236 wdtclose (dev_t dev, int flags, int fmt, struct proc *p)
237 {
238 return(0);
239 }
240
241 int
wdtioctl(dev_t dev,u_long cmd,caddr_t arg,int flag,struct proc * p)242 wdtioctl (dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
243 {
244 struct wdt_softc *wdt = wdt_cd.cd_devs[UNIT(dev)];
245 int error;
246
247 switch (cmd) {
248 case WIOCSCHED:
249 error = wdt_sched(wdt, p);
250 break;
251
252 case WIOCGETSTATE:
253 if (wdt->features)
254 error = wdt_get_state(wdt,
255 (struct wdt_state *)arg);
256 else
257 error = ENXIO;
258 break;
259
260 default:
261 error = ENXIO;
262 break;
263 }
264
265 return(error);
266 }
267
268 /*
269 * wdt_is501
270 *
271 * Returns non-zero if the card is a 501 model.
272 */
273 static int
wdt_is501(struct wdt_softc * wdt)274 wdt_is501 (struct wdt_softc *wdt)
275 {
276 /*
277 * It makes too much sense to detect the card type
278 * by the device ID, so we have to resort to testing
279 * the presence of a register to determine the type.
280 */
281 int v = bus_space_read_1(wdt->iot, wdt->ioh, WDT_TEMPERATURE);
282
283 /* XXX may not be reliable */
284 if (v == 0 || v == 0xFF)
285 return(0);
286
287 return(1);
288 }
289
290 /*
291 * wdt_8254_count
292 *
293 * Loads the specified counter with the 16-bit value 'v'.
294 */
295 static void
wdt_8254_count(struct wdt_softc * wdt,int counter,u_int16_t v)296 wdt_8254_count (struct wdt_softc *wdt, int counter, u_int16_t v)
297 {
298 bus_space_write_1(wdt->iot, wdt->ioh,
299 WDT_8254_BASE + counter, v & 0xFF);
300 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_BASE + counter, v >> 8);
301 }
302
303 /*
304 * wdt_8254_mode
305 *
306 * Sets the mode of the specified counter.
307 */
308 static void
wdt_8254_mode(struct wdt_softc * wdt,int counter,int mode)309 wdt_8254_mode (struct wdt_softc *wdt, int counter, int mode)
310 {
311 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_CTL,
312 (counter << 6) | 0x30 | (mode << 1));
313 }
314
315 /*
316 * wdt_set_timeout
317 *
318 * Load the watchdog timer with the specified number of seconds.
319 */
320 static void
wdt_set_timeout(struct wdt_softc * wdt,unsigned seconds)321 wdt_set_timeout (struct wdt_softc *wdt, unsigned seconds)
322 {
323 /* 8254 has been programmed with a 2ms period */
324 u_int16_t v = (u_int16_t)seconds * 50;
325
326 /* disable the timer */
327 (void)bus_space_read_1(wdt->iot, wdt->ioh, WDT_DISABLE_TIMER);
328
329 /* load the new timeout count */
330 wdt_8254_count(wdt, WDT_8254_TC_HI, v);
331
332 /* enable the timer */
333 bus_space_write_1(wdt->iot, wdt->ioh, WDT_ENABLE_TIMER, 0);
334 }
335
336 /*
337 * wdt_timeout
338 *
339 * Kernel timeout handler. This function is called every
340 * wdt->timeout_secs / 2 seconds. It reloads the watchdog
341 * counters in one of two ways:
342 *
343 * - If there are one or more processes sleeping in a
344 * WIOCSCHED ioctl(), they are woken up to perform
345 * the counter reload.
346 * - If no processes are sleeping in WIOCSCHED, the
347 * counters are reloaded from here.
348 *
349 * Finally, another timeout is scheduled for wdt->timeout_secs
350 * from now.
351 */
352 static void
wdt_timeout(void * arg)353 wdt_timeout (void *arg)
354 {
355 struct wdt_softc *wdt = (struct wdt_softc *)arg;
356
357 /* reload counters from proc in WIOCSCHED ioctl()? */
358 if (wdt->procs)
359 wakeup(wdt);
360 else
361 wdt_set_timeout(wdt, wdt->timeout_secs);
362
363 /* schedule another timeout in half the countdown time */
364 timeout_add(&wdt->timeout, wdt->timeout_secs * hz / 2);
365 }
366
367 /*
368 * wdt_timer_disable
369 *
370 * Disables the watchdog timer and cancels the scheduled (if any)
371 * kernel timeout.
372 */
373 static void
wdt_timer_disable(struct wdt_softc * wdt)374 wdt_timer_disable (struct wdt_softc *wdt)
375 {
376 (void)bus_space_read_1(wdt->iot, wdt->ioh, WDT_DISABLE_TIMER);
377 timeout_del(&wdt->timeout);
378 }
379
380 /*
381 * wdt_timer_enable
382 *
383 * Enables the watchdog timer to expire in the specified number
384 * of seconds. If 'seconds' is outside the range 2-1800, it
385 * is silently clamped to be within range.
386 */
387 static void
wdt_timer_enable(struct wdt_softc * wdt,unsigned seconds)388 wdt_timer_enable (struct wdt_softc *wdt, unsigned seconds)
389 {
390 int s;
391
392 /* clamp range */
393 if (seconds < 2)
394 seconds = 2;
395
396 if (seconds > 1800)
397 seconds = 1800;
398
399 /* block out the timeout handler */
400 s = splclock();
401
402 wdt_timer_disable(wdt);
403 wdt->timeout_secs = seconds;
404
405 timeout_set(&wdt->timeout, wdt_timeout, wdt);
406 timeout_add(&wdt->timeout, hz * seconds / 2);
407 wdt_set_timeout(wdt, seconds);
408
409 /* re-enable clock interrupts */
410 splx(s);
411 }
412
413 /*
414 * wdt_init_timer
415 *
416 * Configure the modes for the watchdog counters and initialize
417 * the low 16-bits of the watchdog counter to have a period of
418 * approximately 1/50th of a second.
419 */
420 static void
wdt_init_timer(struct wdt_softc * wdt)421 wdt_init_timer (struct wdt_softc *wdt)
422 {
423 wdt_8254_mode(wdt, WDT_8254_TC_LO, 3);
424 wdt_8254_mode(wdt, WDT_8254_TC_HI, 2);
425 wdt_8254_count(wdt, WDT_8254_TC_LO, 41666);
426 }
427
428 /*******************************************************************
429 * WDT501 specific functions
430 *******************************************************************/
431
432 /*
433 * wdt_buzzer_off
434 *
435 * Turns the buzzer off.
436 */
437 static void
wdt_buzzer_off(struct wdt_softc * wdt)438 wdt_buzzer_off (struct wdt_softc *wdt)
439 {
440 bus_space_write_1(wdt->iot, wdt->ioh, WDT_STOP_BUZZER, 0);
441 }
442
443 #ifndef WDT_DISABLE_BUZZER
444 /*
445 * wdt_buzzer_enable
446 *
447 * Enables the buzzer when the watchdog counter expires.
448 */
449 static void
wdt_buzzer_enable(struct wdt_softc * wdt)450 wdt_buzzer_enable (struct wdt_softc *wdt)
451 {
452 bus_space_write_1(wdt->iot, wdt->ioh, WDT_8254_BUZZER, 1);
453 wdt_8254_mode(wdt, WDT_8254_BUZZER, 1);
454 }
455 #else
456 /*
457 * wdt_buzzer_disable
458 *
459 * Disables the buzzer from sounding when the watchdog counter
460 * expires.
461 */
462 static void
wdt_buzzer_disable(struct wdt_softc * wdt)463 wdt_buzzer_disable (struct wdt_softc *wdt)
464 {
465 wdt_8254_mode(wdt, WDT_8254_BUZZER, 0);
466 }
467 #endif
468
469 /*
470 * wdt_read_temperature
471 *
472 * Returns the temperature (in Fahrenheit) from the board.
473 */
474 static int
wdt_read_temperature(struct wdt_softc * wdt)475 wdt_read_temperature (struct wdt_softc *wdt)
476 {
477 unsigned v = bus_space_read_1(wdt->iot, wdt->ioh, WDT_TEMPERATURE);
478
479 return((v * 11) / 15 + 7);
480 }
481
482 /*
483 * wdt_read_status
484 *
485 * Returns the status register bits minus the counter refresh
486 * and IRQ generated bits.
487 */
488 static int
wdt_read_status(struct wdt_softc * wdt)489 wdt_read_status (struct wdt_softc *wdt)
490 {
491 /* mask off counter refresh & IRQ generated bits */
492 return(bus_space_read_1(wdt->iot, wdt->ioh, WDT_STATUS_REG) & 0x7E);
493 }
494
495 /*
496 * wdt_display_status
497 *
498 * Displays the current timeout, temperature, and power supply
499 * over/undervoltages to the console.
500 */
501 static void
wdt_display_status(struct wdt_softc * wdt)502 wdt_display_status (struct wdt_softc *wdt)
503 {
504 if (wdt->features) {
505 int status = wdt_read_status(wdt);
506 int temp = wdt_read_temperature(wdt);
507
508 printf("wdt%d: WDT501 timeout %d secs, temp %d F",
509 wdt->unit, wdt->timeout_secs, temp);
510
511 /* overvoltage bit is active low */
512 if ((status & WDT_SR_PS_OVER) == 0)
513 printf(" <PS overvoltage>");
514
515 /* undervoltage bit is active low */
516 if ((status & WDT_SR_PS_UNDER) == 0)
517 printf(" <PS undervoltage>");
518 } else {
519 printf("wdt%d: WDT500 timeout %d secs",
520 wdt->unit, wdt->timeout_secs);
521 }
522
523 printf("\n");
524 }
525
526 /*
527 * wdt_get_state
528 *
529 * Returns the temperature and status bits.
530 */
531 static int
wdt_get_state(struct wdt_softc * wdt,struct wdt_state * state)532 wdt_get_state (struct wdt_softc *wdt, struct wdt_state *state)
533 {
534 state->temperature = wdt_read_temperature(wdt);
535 state->status = wdt_read_status(wdt);
536
537 return(0);
538 }
539
540 /*
541 * wdt_shutdown
542 *
543 * Disables the watchdog timer at system shutdown time.
544 */
545 static void
wdt_shutdown(void * arg)546 wdt_shutdown (void *arg)
547 {
548 struct wdt_softc *wdt = (struct wdt_softc *)arg;
549
550 wdt_timer_disable(wdt);
551 }
552
553 /*
554 * wdt_sched
555 *
556 * Put the process into an infinite loop in which:
557 *
558 * - The process sleeps, waiting for a wakeup() from the tsleep()
559 * handler.
560 * - When awakened, the process reloads the watchdog counter and
561 * repeats the loop.
562 *
563 * The only way the loop can be broken is if the process is interrupted
564 * via a signal.
565 *
566 * The whole point of this is to cause a watchdog timeout to be
567 * generated if processes are no longer being scheduled.
568 */
569 static int
wdt_sched(struct wdt_softc * wdt,struct proc * p)570 wdt_sched (struct wdt_softc *wdt, struct proc *p)
571 {
572 int error;
573 int s;
574
575 /*
576 * Regardless of the device permissions, you must be
577 * root to do this -- a process which is STOPPED
578 * while in this function can cause a reboot to occur
579 * if the counters aren't reloaded within wdt->timeout_secs
580 * seconds.
581 */
582 if ((error = suser(p, 0)))
583 return(error);
584
585 /* block out the timeout handler */
586 s = splclock();
587
588 /* indicate that we are sleeping */
589 ++wdt->procs;
590
591 /* loop until the process is signaled */
592 while (1) {
593 error = tsleep(wdt, PCATCH | PSWP, "wdtsch", 0);
594
595 wdt_set_timeout(wdt, wdt->timeout_secs);
596
597 if (error != 0)
598 break;
599 }
600
601 /* remove sleeping indication */
602 --wdt->procs;
603
604 /* re-enable timeout handler */
605 splx(s);
606
607 return(error);
608 }
609