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