615 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * SMsC 37B787 Watchdog Timer driver for Linux 2.6.x.x4 *5 * Based on acquirewdt.c by Alan Cox <alan@lxorguk.ukuu.org.uk>6 * and some other existing drivers7 *8 * The authors do NOT admit liability nor provide warranty for9 * any of this software. This material is provided "AS-IS" in10 * 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 MAGICCLOSE17 * features. Released version 1.118 *19 * Theory of operation:20 *21 * A Watchdog Timer (WDT) is a hardware circuit that can22 * 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 driver26 * via the /dev/watchdog special device file that userspace is27 * still alive, at regular intervals. When such a notification28 * occurs, the driver will usually tell the hardware watchdog29 * that everything is in order, and that the watchdog should wait30 * for yet another little while to reset the system.31 * If userspace fails (RAM error, kernel bug, whatever), the32 * notifications cease to occur, and the hardware watchdog will33 * reset the system (causing a reboot) after the timeout occurs.34 *35 * Create device with:36 * mknod /dev/watchdog c 10 13037 *38 * For an example userspace keep-alive daemon, see:39 * Documentation/watchdog/wdt.rst40 */41 42#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt43 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_MINUTES63#undef SMSC_SUPPORT_MINUTES64 65#define MAX_TIMEOUT 25566 67#define UNIT_SECOND 068#define UNIT_MINUTE 169 70#define VERSION "1.1"71 72#define IOPORT 0x3F073#define IOPORT_SIZE 274#define IODEV_NO 875 76static int unit = UNIT_SECOND; /* timer's unit */77static int timeout = 60; /* timeout value: default is 60 "units" */78static unsigned long timer_enabled; /* is the timer enabled? */79 80static char expect_close; /* is the close expected? */81 82static DEFINE_SPINLOCK(io_lock);/* to guard the watchdog from io races */83 84static bool nowayout = WATCHDOG_NOWAYOUT;85 86/* -- Low level function ----------------------------------------*/87 88/* unlock the IO chip */89 90static 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 */98static inline void close_io_config(void)99{100 outb(0xAA, IOPORT);101}102 103/* select the IO device */104static 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 */111static 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 */118static 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 126static inline void gpio_bit12(unsigned char reg)127{128 /* -- General Purpose I/O Bit 1.2 --129 * Bit 0, In/Out: 0 = Output, 1 = Input130 * Bit 1, Polarity: 0 = No Invert, 1 = Invert131 * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable132 * Bit 3/4, Function select: 00 = GPI/O, 01 = WDT, 10 = P17,133 * 11 = Either Edge Triggered Intr. 2134 * Bit 5/6 (Reserved)135 * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain136 */137 write_io_cr(0xE2, reg);138}139 140static inline void gpio_bit13(unsigned char reg)141{142 /* -- General Purpose I/O Bit 1.3 --143 * Bit 0, In/Out: 0 = Output, 1 = Input144 * Bit 1, Polarity: 0 = No Invert, 1 = Invert145 * Bit 2, Group Enable Intr.: 0 = Disable, 1 = Enable146 * Bit 3, Function select: 0 = GPI/O, 1 = LED147 * Bit 4-6 (Reserved)148 * Bit 7, Output Type: 0 = Push Pull Bit, 1 = Open Drain149 */150 write_io_cr(0xE3, reg);151}152 153static 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 Select158 * (0 = Minutes, 1 = Seconds)159 */160 write_io_cr(0xF1, new_units);161}162 163static 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 171static inline void wdt_timer_conf(unsigned char conf)172{173 /* -- Watchdog timer configuration --174 * Bit 0 Joystick enable: 0* = No Reset, 1 = Reset WDT upon175 * Gameport I/O176 * 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 Intr178 * Bit 3 Reset the timer179 * (Wrong in SMsC documentation? Given as: PowerLED Timout180 * 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 187static inline void wdt_timer_ctrl(unsigned char reg)188{189 /* -- Watchdog timer control --190 * Bit 0 Status Bit: 0 = Timer counting, 1 = Timeout occurred191 * Bit 1 Power LED Toggle: 0 = Disable Toggle, 1 = Toggle at 1 Hz192 * 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 event195 * 1 = P20 Allows rising edge of P20, from the keyboard196 * 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 counter200 * 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 counter203 * 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 213static 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 not224 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 248static 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 273static 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 291static 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 307static 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 315static 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 323static 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 339static 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 349static 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 370static 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 389static 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 the401 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 419static 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 fallthrough; /* 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 490static 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 503static const struct file_operations wb_smsc_wdt_fops = {504 .owner = THIS_MODULE,505 .write = wb_smsc_wdt_write,506 .unlocked_ioctl = wb_smsc_wdt_ioctl,507 .compat_ioctl = compat_ptr_ioctl,508 .open = wb_smsc_wdt_open,509 .release = wb_smsc_wdt_release,510};511 512static struct notifier_block wb_smsc_wdt_notifier = {513 .notifier_call = wb_smsc_wdt_notify_sys,514};515 516static 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 526static 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);564out_clean:565 return ret;566 567out_rbt:568 unregister_reboot_notifier(&wb_smsc_wdt_notifier);569 570out_io:571 release_region(IOPORT, IOPORT_SIZE);572 573out_pnp:574 goto out_clean;575}576 577/* module's "destructor" */578 579static 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 594module_init(wb_smsc_wdt_init);595module_exit(wb_smsc_wdt_exit);596 597MODULE_AUTHOR("Sven Anders <anders@anduras.de>");598MODULE_DESCRIPTION("Driver for SMsC 37B787 watchdog component (Version "599 VERSION ")");600MODULE_LICENSE("GPL");601 602#ifdef SMSC_SUPPORT_MINUTES603module_param(unit, int, 0);604MODULE_PARM_DESC(unit,605 "set unit to use, 0=seconds or 1=minutes, default is 0");606#endif607 608module_param(timeout, int, 0);609MODULE_PARM_DESC(timeout, "range is 1-255 units, default is 60");610 611module_param(nowayout, bool, 0);612MODULE_PARM_DESC(nowayout,613 "Watchdog cannot be stopped once started (default="614 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");615