root/drivers/watchdog/smsc37b787_wdt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. open_io_config
  2. close_io_config
  3. select_io_device
  4. write_io_cr
  5. read_io_cr
  6. gpio_bit12
  7. gpio_bit13
  8. wdt_timer_units
  9. wdt_timeout_value
  10. wdt_timer_conf
  11. wdt_timer_ctrl
  12. wb_smsc_wdt_initialize
  13. wb_smsc_wdt_shutdown
  14. wb_smsc_wdt_set_timeout
  15. wb_smsc_wdt_get_timeout
  16. wb_smsc_wdt_disable
  17. wb_smsc_wdt_enable
  18. wb_smsc_wdt_reset_timer
  19. wb_smsc_wdt_status
  20. wb_smsc_wdt_open
  21. wb_smsc_wdt_release
  22. wb_smsc_wdt_write
  23. wb_smsc_wdt_ioctl
  24. wb_smsc_wdt_notify_sys
  25. wb_smsc_wdt_init
  26. wb_smsc_wdt_exit

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  *      SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x
   4  *
   5  *      Based on acquirewdt.c by Alan Cox <alan@lxorguk.ukuu.org.uk>
   6  *      and some other existing drivers
   7  *
   8  *      The authors do NOT admit liability nor provide warranty for
   9  *      any of this software. This material is provided "AS-IS" in
  10  *      the hope that it may be useful for others.
  11  *
  12  *      (C) Copyright 2003-2006  Sven Anders <anders@anduras.de>
  13  *
  14  *  History:
  15  *      2003 - Created version 1.0 for Linux 2.4.x.
  16  *      2006 - Ported to Linux 2.6, added nowayout and MAGICCLOSE
  17  *             features. Released version 1.1
  18  *
  19  *  Theory of operation:
  20  *
  21  *      A Watchdog Timer (WDT) is a hardware circuit that can
  22  *      reset the computer system in case of a software fault.
  23  *      You probably knew that already.
  24  *
  25  *      Usually a userspace daemon will notify the kernel WDT driver
  26  *      via the /dev/watchdog special device file that userspace is
  27  *      still alive, at regular intervals.  When such a notification
  28  *      occurs, the driver will usually tell the hardware watchdog
  29  *      that everything is in order, and that the watchdog should wait
  30  *      for yet another little while to reset the system.
  31  *      If userspace fails (RAM error, kernel bug, whatever), the
  32  *      notifications cease to occur, and the hardware watchdog will
  33  *      reset the system (causing a reboot) after the timeout occurs.
  34  *
  35  * Create device with:
  36  *  mknod /dev/watchdog c 10 130
  37  *
  38  * For an example userspace keep-alive daemon, see:
  39  *   Documentation/watchdog/wdt.rst
  40  */
  41 
  42 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  43 
  44 #include <linux/module.h>
  45 #include <linux/moduleparam.h>
  46 #include <linux/types.h>
  47 #include <linux/miscdevice.h>
  48 #include <linux/watchdog.h>
  49 #include <linux/delay.h>
  50 #include <linux/fs.h>
  51 #include <linux/ioport.h>
  52 #include <linux/notifier.h>
  53 #include <linux/reboot.h>
  54 #include <linux/init.h>
  55 #include <linux/spinlock.h>
  56 #include <linux/io.h>
  57 #include <linux/uaccess.h>
  58 
  59 
  60 /* enable support for minutes as units? */
  61 /* (does not always work correctly, so disabled by default!) */
  62 #define SMSC_SUPPORT_MINUTES
  63 #undef SMSC_SUPPORT_MINUTES
  64 
  65 #define MAX_TIMEOUT     255
  66 
  67 #define UNIT_SECOND     0
  68 #define UNIT_MINUTE     1
  69 
  70 #define VERSION         "1.1"
  71 
  72 #define IOPORT          0x3F0
  73 #define IOPORT_SIZE     2
  74 #define IODEV_NO        8
  75 
  76 static int unit = UNIT_SECOND;  /* timer's unit */
  77 static int timeout = 60;        /* timeout value: default is 60 "units" */
  78 static unsigned long timer_enabled;   /* is the timer enabled? */
  79 
  80 static char expect_close;       /* is the close expected? */
  81 
  82 static DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */
  83 
  84 static bool nowayout = WATCHDOG_NOWAYOUT;
  85 
  86 /* -- Low level function ----------------------------------------*/
  87 
  88 /* unlock the IO chip */
  89 
  90 static inline void open_io_config(void)
  91 {
  92         outb(0x55, IOPORT);
  93         mdelay(1);
  94         outb(0x55, IOPORT);
  95 }
  96 
  97 /* lock the IO chip */
  98 static inline void close_io_config(void)
  99 {
 100         outb(0xAA, IOPORT);
 101 }
 102 
 103 /* select the IO device */
 104 static inline void select_io_device(unsigned char devno)
 105 {
 106         outb(0x07, IOPORT);
 107         outb(devno, IOPORT+1);
 108 }
 109 
 110 /* write to the control register */
 111 static inline void write_io_cr(unsigned char reg, unsigned char data)
 112 {
 113         outb(reg, IOPORT);
 114         outb(data, IOPORT+1);
 115 }
 116 
 117 /* read from the control register */
 118 static inline char read_io_cr(unsigned char reg)
 119 {
 120         outb(reg, IOPORT);
 121         return inb(IOPORT+1);
 122 }
 123 
 124 /* -- Medium level functions ------------------------------------*/
 125 
 126 static inline void gpio_bit12(unsigned char reg)
 127 {
 128         /* -- General Purpose I/O Bit 1.2 --
 129          * Bit 0,   In/Out: 0 = Output, 1 = Input
 130          * Bit 1,   Polarity: 0 = No Invert, 1 = Invert
 131          * Bit 2,   Group Enable Intr.: 0 = Disable, 1 = Enable
 132          * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,
 133          *                           11 = Either Edge Triggered Intr. 2
 134          * Bit 5/6  (Reserved)
 135          * Bit 7,   Output Type: 0 = Push Pull Bit, 1 = Open Drain
 136          */
 137         write_io_cr(0xE2, reg);
 138 }
 139 
 140 static inline void gpio_bit13(unsigned char reg)
 141 {
 142         /* -- General Purpose I/O Bit 1.3 --
 143          * Bit 0,  In/Out: 0 = Output, 1 = Input
 144          * Bit 1,  Polarity: 0 = No Invert, 1 = Invert
 145          * Bit 2,  Group Enable Intr.: 0 = Disable, 1 = Enable
 146          * Bit 3,  Function select: 0 = GPI/O, 1 = LED
 147          * Bit 4-6 (Reserved)
 148          * Bit 7,  Output Type: 0 = Push Pull Bit, 1 = Open Drain
 149          */
 150         write_io_cr(0xE3, reg);
 151 }
 152 
 153 static inline void wdt_timer_units(unsigned char new_units)
 154 {
 155         /* -- Watchdog timer units --
 156          * Bit 0-6 (Reserved)
 157          * Bit 7,  WDT Time-out Value Units Select
 158          *         (0 = Minutes, 1 = Seconds)
 159          */
 160         write_io_cr(0xF1, new_units);
 161 }
 162 
 163 static inline void wdt_timeout_value(unsigned char new_timeout)
 164 {
 165         /* -- Watchdog Timer Time-out Value --
 166          * Bit 0-7 Binary coded units (0=Disabled, 1..255)
 167          */
 168         write_io_cr(0xF2, new_timeout);
 169 }
 170 
 171 static inline void wdt_timer_conf(unsigned char conf)
 172 {
 173         /* -- Watchdog timer configuration --
 174          * Bit 0   Joystick enable: 0* = No Reset, 1 = Reset WDT upon
 175          *                                                      Gameport I/O
 176          * Bit 1   Keyboard enable: 0* = No Reset, 1 = Reset WDT upon KBD Intr.
 177          * Bit 2   Mouse enable: 0* = No Reset, 1 = Reset WDT upon Mouse Intr
 178          * Bit 3   Reset the timer
 179          *         (Wrong in SMsC documentation? Given as: PowerLED Timout
 180          *                                                      Enabled)
 181          * Bit 4-7 WDT Interrupt Mapping: (0000* = Disabled,
 182          *            0001=IRQ1, 0010=(Invalid), 0011=IRQ3 to 1111=IRQ15)
 183          */
 184         write_io_cr(0xF3, conf);
 185 }
 186 
 187 static inline void wdt_timer_ctrl(unsigned char reg)
 188 {
 189         /* -- Watchdog timer control --
 190          * Bit 0   Status Bit: 0 = Timer counting, 1 = Timeout occurred
 191          * Bit 1   Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz
 192          * Bit 2   Force Timeout: 1 = Forces WD timeout event (self-cleaning)
 193          * Bit 3   P20 Force Timeout enabled:
 194          *          0 = P20 activity does not generate the WD timeout event
 195          *          1 = P20 Allows rising edge of P20, from the keyboard
 196          *              controller, to force the WD timeout event.
 197          * Bit 4   (Reserved)
 198          * -- Soft power management --
 199          * Bit 5   Stop Counter: 1 = Stop software power down counter
 200          *            set via register 0xB8, (self-cleaning)
 201          *            (Upon read: 0 = Counter running, 1 = Counter stopped)
 202          * Bit 6   Restart Counter: 1 = Restart software power down counter
 203          *            set via register 0xB8, (self-cleaning)
 204          * Bit 7   SPOFF: 1 = Force software power down (self-cleaning)
 205          */
 206         write_io_cr(0xF4, reg);
 207 }
 208 
 209 /* -- Higher level functions ------------------------------------*/
 210 
 211 /* initialize watchdog */
 212 
 213 static void wb_smsc_wdt_initialize(void)
 214 {
 215         unsigned char old;
 216 
 217         spin_lock(&io_lock);
 218         open_io_config();
 219         select_io_device(IODEV_NO);
 220 
 221         /* enable the watchdog */
 222         gpio_bit13(0x08);  /* Select pin 80 = LED not GPIO */
 223         gpio_bit12(0x0A);  /* Set pin 79 = WDT not
 224                               GPIO/Output/Polarity=Invert */
 225         /* disable the timeout */
 226         wdt_timeout_value(0);
 227 
 228         /* reset control register */
 229         wdt_timer_ctrl(0x00);
 230 
 231         /* reset configuration register */
 232         wdt_timer_conf(0x00);
 233 
 234         /* read old (timer units) register */
 235         old = read_io_cr(0xF1) & 0x7F;
 236         if (unit == UNIT_SECOND)
 237                 old |= 0x80;    /* set to seconds */
 238 
 239         /* set the watchdog timer units */
 240         wdt_timer_units(old);
 241 
 242         close_io_config();
 243         spin_unlock(&io_lock);
 244 }
 245 
 246 /* shutdown the watchdog */
 247 
 248 static void wb_smsc_wdt_shutdown(void)
 249 {
 250         spin_lock(&io_lock);
 251         open_io_config();
 252         select_io_device(IODEV_NO);
 253 
 254         /* disable the watchdog */
 255         gpio_bit13(0x09);
 256         gpio_bit12(0x09);
 257 
 258         /* reset watchdog config register */
 259         wdt_timer_conf(0x00);
 260 
 261         /* reset watchdog control register */
 262         wdt_timer_ctrl(0x00);
 263 
 264         /* disable timeout */
 265         wdt_timeout_value(0x00);
 266 
 267         close_io_config();
 268         spin_unlock(&io_lock);
 269 }
 270 
 271 /* set timeout => enable watchdog */
 272 
 273 static void wb_smsc_wdt_set_timeout(unsigned char new_timeout)
 274 {
 275         spin_lock(&io_lock);
 276         open_io_config();
 277         select_io_device(IODEV_NO);
 278 
 279         /* set Power LED to blink, if we enable the timeout */
 280         wdt_timer_ctrl((new_timeout == 0) ? 0x00 : 0x02);
 281 
 282         /* set timeout value */
 283         wdt_timeout_value(new_timeout);
 284 
 285         close_io_config();
 286         spin_unlock(&io_lock);
 287 }
 288 
 289 /* get timeout */
 290 
 291 static unsigned char wb_smsc_wdt_get_timeout(void)
 292 {
 293         unsigned char set_timeout;
 294 
 295         spin_lock(&io_lock);
 296         open_io_config();
 297         select_io_device(IODEV_NO);
 298         set_timeout = read_io_cr(0xF2);
 299         close_io_config();
 300         spin_unlock(&io_lock);
 301 
 302         return set_timeout;
 303 }
 304 
 305 /* disable watchdog */
 306 
 307 static void wb_smsc_wdt_disable(void)
 308 {
 309         /* set the timeout to 0 to disable the watchdog */
 310         wb_smsc_wdt_set_timeout(0);
 311 }
 312 
 313 /* enable watchdog by setting the current timeout */
 314 
 315 static void wb_smsc_wdt_enable(void)
 316 {
 317         /* set the current timeout... */
 318         wb_smsc_wdt_set_timeout(timeout);
 319 }
 320 
 321 /* reset the timer */
 322 
 323 static void wb_smsc_wdt_reset_timer(void)
 324 {
 325         spin_lock(&io_lock);
 326         open_io_config();
 327         select_io_device(IODEV_NO);
 328 
 329         /* reset the timer */
 330         wdt_timeout_value(timeout);
 331         wdt_timer_conf(0x08);
 332 
 333         close_io_config();
 334         spin_unlock(&io_lock);
 335 }
 336 
 337 /* return, if the watchdog is enabled (timeout is set...) */
 338 
 339 static int wb_smsc_wdt_status(void)
 340 {
 341         return (wb_smsc_wdt_get_timeout() == 0) ? 0 : WDIOF_KEEPALIVEPING;
 342 }
 343 
 344 
 345 /* -- File operations -------------------------------------------*/
 346 
 347 /* open => enable watchdog and set initial timeout */
 348 
 349 static int wb_smsc_wdt_open(struct inode *inode, struct file *file)
 350 {
 351         /* /dev/watchdog can only be opened once */
 352 
 353         if (test_and_set_bit(0, &timer_enabled))
 354                 return -EBUSY;
 355 
 356         if (nowayout)
 357                 __module_get(THIS_MODULE);
 358 
 359         /* Reload and activate timer */
 360         wb_smsc_wdt_enable();
 361 
 362         pr_info("Watchdog enabled. Timeout set to %d %s\n",
 363                 timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
 364 
 365         return stream_open(inode, file);
 366 }
 367 
 368 /* close => shut off the timer */
 369 
 370 static int wb_smsc_wdt_release(struct inode *inode, struct file *file)
 371 {
 372         /* Shut off the timer. */
 373 
 374         if (expect_close == 42) {
 375                 wb_smsc_wdt_disable();
 376                 pr_info("Watchdog disabled, sleeping again...\n");
 377         } else {
 378                 pr_crit("Unexpected close, not stopping watchdog!\n");
 379                 wb_smsc_wdt_reset_timer();
 380         }
 381 
 382         clear_bit(0, &timer_enabled);
 383         expect_close = 0;
 384         return 0;
 385 }
 386 
 387 /* write => update the timer to keep the machine alive */
 388 
 389 static ssize_t wb_smsc_wdt_write(struct file *file, const char __user *data,
 390                                  size_t len, loff_t *ppos)
 391 {
 392         /* See if we got the magic character 'V' and reload the timer */
 393         if (len) {
 394                 if (!nowayout) {
 395                         size_t i;
 396 
 397                         /* reset expect flag */
 398                         expect_close = 0;
 399 
 400                         /* scan to see whether or not we got the
 401                            magic character */
 402                         for (i = 0; i != len; i++) {
 403                                 char c;
 404                                 if (get_user(c, data + i))
 405                                         return -EFAULT;
 406                                 if (c == 'V')
 407                                         expect_close = 42;
 408                         }
 409                 }
 410 
 411                 /* someone wrote to us, we should reload the timer */
 412                 wb_smsc_wdt_reset_timer();
 413         }
 414         return len;
 415 }
 416 
 417 /* ioctl => control interface */
 418 
 419 static long wb_smsc_wdt_ioctl(struct file *file,
 420                                         unsigned int cmd, unsigned long arg)
 421 {
 422         int new_timeout;
 423 
 424         union {
 425                 struct watchdog_info __user *ident;
 426                 int __user *i;
 427         } uarg;
 428 
 429         static const struct watchdog_info ident = {
 430                 .options =              WDIOF_KEEPALIVEPING |
 431                                         WDIOF_SETTIMEOUT |
 432                                         WDIOF_MAGICCLOSE,
 433                 .firmware_version =     0,
 434                 .identity =             "SMsC 37B787 Watchdog",
 435         };
 436 
 437         uarg.i = (int __user *)arg;
 438 
 439         switch (cmd) {
 440         case WDIOC_GETSUPPORT:
 441                 return copy_to_user(uarg.ident, &ident, sizeof(ident))
 442                                                                 ? -EFAULT : 0;
 443         case WDIOC_GETSTATUS:
 444                 return put_user(wb_smsc_wdt_status(), uarg.i);
 445         case WDIOC_GETBOOTSTATUS:
 446                 return put_user(0, uarg.i);
 447         case WDIOC_SETOPTIONS:
 448         {
 449                 int options, retval = -EINVAL;
 450 
 451                 if (get_user(options, uarg.i))
 452                         return -EFAULT;
 453 
 454                 if (options & WDIOS_DISABLECARD) {
 455                         wb_smsc_wdt_disable();
 456                         retval = 0;
 457                 }
 458                 if (options & WDIOS_ENABLECARD) {
 459                         wb_smsc_wdt_enable();
 460                         retval = 0;
 461                 }
 462                 return retval;
 463         }
 464         case WDIOC_KEEPALIVE:
 465                 wb_smsc_wdt_reset_timer();
 466                 return 0;
 467         case WDIOC_SETTIMEOUT:
 468                 if (get_user(new_timeout, uarg.i))
 469                         return -EFAULT;
 470                 /* the API states this is given in secs */
 471                 if (unit == UNIT_MINUTE)
 472                         new_timeout /= 60;
 473                 if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)
 474                         return -EINVAL;
 475                 timeout = new_timeout;
 476                 wb_smsc_wdt_set_timeout(timeout);
 477                 /* fall through - and return the new timeout... */
 478         case WDIOC_GETTIMEOUT:
 479                 new_timeout = timeout;
 480                 if (unit == UNIT_MINUTE)
 481                         new_timeout *= 60;
 482                 return put_user(new_timeout, uarg.i);
 483         default:
 484                 return -ENOTTY;
 485         }
 486 }
 487 
 488 /* -- Notifier funtions -----------------------------------------*/
 489 
 490 static int wb_smsc_wdt_notify_sys(struct notifier_block *this,
 491                                         unsigned long code, void *unused)
 492 {
 493         if (code == SYS_DOWN || code == SYS_HALT) {
 494                 /* set timeout to 0, to avoid possible race-condition */
 495                 timeout = 0;
 496                 wb_smsc_wdt_disable();
 497         }
 498         return NOTIFY_DONE;
 499 }
 500 
 501 /* -- Module's structures ---------------------------------------*/
 502 
 503 static const struct file_operations wb_smsc_wdt_fops = {
 504         .owner    = THIS_MODULE,
 505         .llseek         = no_llseek,
 506         .write          = wb_smsc_wdt_write,
 507         .unlocked_ioctl = wb_smsc_wdt_ioctl,
 508         .open           = wb_smsc_wdt_open,
 509         .release        = wb_smsc_wdt_release,
 510 };
 511 
 512 static struct notifier_block wb_smsc_wdt_notifier = {
 513         .notifier_call  = wb_smsc_wdt_notify_sys,
 514 };
 515 
 516 static struct miscdevice wb_smsc_wdt_miscdev = {
 517         .minor          = WATCHDOG_MINOR,
 518         .name           = "watchdog",
 519         .fops           = &wb_smsc_wdt_fops,
 520 };
 521 
 522 /* -- Module init functions -------------------------------------*/
 523 
 524 /* module's "constructor" */
 525 
 526 static int __init wb_smsc_wdt_init(void)
 527 {
 528         int ret;
 529 
 530         pr_info("SMsC 37B787 watchdog component driver "
 531                 VERSION " initialising...\n");
 532 
 533         if (!request_region(IOPORT, IOPORT_SIZE, "SMsC 37B787 watchdog")) {
 534                 pr_err("Unable to register IO port %#x\n", IOPORT);
 535                 ret = -EBUSY;
 536                 goto out_pnp;
 537         }
 538 
 539         /* set new maximum, if it's too big */
 540         if (timeout > MAX_TIMEOUT)
 541                 timeout = MAX_TIMEOUT;
 542 
 543         /* init the watchdog timer */
 544         wb_smsc_wdt_initialize();
 545 
 546         ret = register_reboot_notifier(&wb_smsc_wdt_notifier);
 547         if (ret) {
 548                 pr_err("Unable to register reboot notifier err = %d\n", ret);
 549                 goto out_io;
 550         }
 551 
 552         ret = misc_register(&wb_smsc_wdt_miscdev);
 553         if (ret) {
 554                 pr_err("Unable to register miscdev on minor %d\n",
 555                        WATCHDOG_MINOR);
 556                 goto out_rbt;
 557         }
 558 
 559         /* output info */
 560         pr_info("Timeout set to %d %s\n",
 561                 timeout, (unit == UNIT_SECOND) ? "second(s)" : "minute(s)");
 562         pr_info("Watchdog initialized and sleeping (nowayout=%d)...\n",
 563                 nowayout);
 564 out_clean:
 565         return ret;
 566 
 567 out_rbt:
 568         unregister_reboot_notifier(&wb_smsc_wdt_notifier);
 569 
 570 out_io:
 571         release_region(IOPORT, IOPORT_SIZE);
 572 
 573 out_pnp:
 574         goto out_clean;
 575 }
 576 
 577 /* module's "destructor" */
 578 
 579 static void __exit wb_smsc_wdt_exit(void)
 580 {
 581         /* Stop the timer before we leave */
 582         if (!nowayout) {
 583                 wb_smsc_wdt_shutdown();
 584                 pr_info("Watchdog disabled\n");
 585         }
 586 
 587         misc_deregister(&wb_smsc_wdt_miscdev);
 588         unregister_reboot_notifier(&wb_smsc_wdt_notifier);
 589         release_region(IOPORT, IOPORT_SIZE);
 590 
 591         pr_info("SMsC 37B787 watchdog component driver removed\n");
 592 }
 593 
 594 module_init(wb_smsc_wdt_init);
 595 module_exit(wb_smsc_wdt_exit);
 596 
 597 MODULE_AUTHOR("Sven Anders <anders@anduras.de>");
 598 MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "
 599                                                                 VERSION ")");
 600 MODULE_LICENSE("GPL");
 601 
 602 #ifdef SMSC_SUPPORT_MINUTES
 603 module_param(unit, int, 0);
 604 MODULE_PARM_DESC(unit,
 605                 "set unit to use, 0=seconds or 1=minutes, default is 0");
 606 #endif
 607 
 608 module_param(timeout, int, 0);
 609 MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");
 610 
 611 module_param(nowayout, bool, 0);
 612 MODULE_PARM_DESC(nowayout,
 613                 "Watchdog cannot be stopped once started (default="
 614                                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

/* [<][>][^][v][top][bottom][index][help] */