xref: /openbsd-src/sys/dev/pci/wdt.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: wdt.c,v 1.3 2001/02/03 06:19:13 mickey 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 __P((struct device *, void *, void *));
71 void wdtattach __P((struct device *, struct device *, void *));
72 int wdtopen __P((dev_t, int, int, struct proc *));
73 int wdtclose __P((dev_t, int, int, struct proc *));
74 int wdtioctl __P((dev_t, u_long, caddr_t, int, struct proc *));
75 
76 /* static functions */
77 static int wdt_is501 __P((struct wdt_softc *wdt));
78 static void wdt_8254_count __P((struct wdt_softc *wdt, int counter, u_int16_t v));
79 static void wdt_8254_mode __P((struct wdt_softc *wdt, int counter, int mode));
80 static void wdt_set_timeout __P((struct wdt_softc *wdt, unsigned seconds));
81 static void wdt_timeout __P((void *arg));
82 static void wdt_init_timer __P((struct wdt_softc *wdt));
83 static void wdt_buzzer_off __P((struct wdt_softc *wdt));
84 static int wdt_read_temperature __P((struct wdt_softc *wdt));
85 static int wdt_read_status __P((struct wdt_softc *wdt));
86 static void wdt_display_status __P((struct wdt_softc *wdt));
87 static int wdt_get_state __P((struct wdt_softc *wdt, struct wdt_state *state));
88 static void wdt_shutdown __P((void *arg));
89 static int wdt_sched __P((struct wdt_softc *wdt, struct proc *p));
90 static void wdt_timer_disable __P((struct wdt_softc *wdt));
91 static void wdt_timer_enable __P((struct wdt_softc *wdt, unsigned seconds));
92 #if WDT_DISABLE_BUZZER
93 static void wdt_buzzer_disable __P((struct wdt_softc *wdt));
94 #else
95 static void wdt_buzzer_enable __P((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
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
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
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
236 wdtclose (dev_t dev, int flags, int fmt, struct proc *p)
237 {
238 	return(0);
239 }
240 
241 int
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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->p_ucred, &p->p_acflag)))
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