475 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver4 * (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>,5 * All Rights Reserved.6 * Based on wdt.c and wdt977.c by Alan Cox and Woody Suwalski respectively.7 *8 * The author(s) of this software shall not be held liable for damages9 * of any nature resulting due to the use of this software. This10 * software is provided AS-IS with no warranties.11 *12 * Changelog:13 * 20020220 Zwane Mwaikambo Code based on datasheet, no hardware.14 * 20020221 Zwane Mwaikambo Cleanups as suggested by Jeff Garzik15 * and Alan Cox.16 * 20020222 Zwane Mwaikambo Added probing.17 * 20020225 Zwane Mwaikambo Added ISAPNP support.18 * 20020412 Rob Radez Broke out start/stop functions19 * <rob@osinvestor.com> Return proper status instead of20 * temperature warning21 * Add WDIOC_GETBOOTSTATUS and22 * WDIOC_SETOPTIONS ioctls23 * Fix CONFIG_WATCHDOG_NOWAYOUT24 * 20020530 Joel Becker Add Matt Domsch's nowayout module25 * option26 * 20030116 Adam Belay Updated to the latest pnp code27 */28 29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt30 31#include <linux/module.h>32#include <linux/moduleparam.h>33#include <linux/miscdevice.h>34#include <linux/watchdog.h>35#include <linux/ioport.h>36#include <linux/spinlock.h>37#include <linux/notifier.h>38#include <linux/reboot.h>39#include <linux/init.h>40#include <linux/pnp.h>41#include <linux/fs.h>42#include <linux/semaphore.h>43#include <linux/io.h>44#include <linux/uaccess.h>45 46#define SC1200_MODULE_VER "build 20020303"47#define SC1200_MODULE_NAME "sc1200wdt"48 49#define MAX_TIMEOUT 255 /* 255 minutes */50#define PMIR (io) /* Power Management Index Register */51#define PMDR (io+1) /* Power Management Data Register */52 53/* Data Register indexes */54#define FER1 0x00 /* Function enable register 1 */55#define FER2 0x01 /* Function enable register 2 */56#define PMC1 0x02 /* Power Management Ctrl 1 */57#define PMC2 0x03 /* Power Management Ctrl 2 */58#define PMC3 0x04 /* Power Management Ctrl 3 */59#define WDTO 0x05 /* Watchdog timeout register */60#define WDCF 0x06 /* Watchdog config register */61#define WDST 0x07 /* Watchdog status register */62 63/* WDCF bitfields - which devices assert WDO */64#define KBC_IRQ 0x01 /* Keyboard Controller */65#define MSE_IRQ 0x02 /* Mouse */66#define UART1_IRQ 0x03 /* Serial0 */67#define UART2_IRQ 0x04 /* Serial1 */68/* 5 -7 are reserved */69 70static int timeout = 1;71static int io = -1;72static int io_len = 2; /* for non plug and play */73static unsigned long open_flag;74static char expect_close;75static DEFINE_SPINLOCK(sc1200wdt_lock); /* io port access serialisation */76 77#if defined CONFIG_PNP78static int isapnp = 1;79static struct pnp_dev *wdt_dev;80 81module_param(isapnp, int, 0);82MODULE_PARM_DESC(isapnp,83 "When set to 0 driver ISA PnP support will be disabled");84#endif85 86module_param_hw(io, int, ioport, 0);87MODULE_PARM_DESC(io, "io port");88module_param(timeout, int, 0);89MODULE_PARM_DESC(timeout, "range is 0-255 minutes, default is 1");90 91static bool nowayout = WATCHDOG_NOWAYOUT;92module_param(nowayout, bool, 0);93MODULE_PARM_DESC(nowayout,94 "Watchdog cannot be stopped once started (default="95 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");96 97 98 99/* Read from Data Register */100static inline void __sc1200wdt_read_data(unsigned char index,101 unsigned char *data)102{103 outb_p(index, PMIR);104 *data = inb(PMDR);105}106 107static void sc1200wdt_read_data(unsigned char index, unsigned char *data)108{109 spin_lock(&sc1200wdt_lock);110 __sc1200wdt_read_data(index, data);111 spin_unlock(&sc1200wdt_lock);112}113 114/* Write to Data Register */115static inline void __sc1200wdt_write_data(unsigned char index,116 unsigned char data)117{118 outb_p(index, PMIR);119 outb(data, PMDR);120}121 122static inline void sc1200wdt_write_data(unsigned char index,123 unsigned char data)124{125 spin_lock(&sc1200wdt_lock);126 __sc1200wdt_write_data(index, data);127 spin_unlock(&sc1200wdt_lock);128}129 130 131static void sc1200wdt_start(void)132{133 unsigned char reg;134 spin_lock(&sc1200wdt_lock);135 136 __sc1200wdt_read_data(WDCF, ®);137 /* assert WDO when any of the following interrupts are triggered too */138 reg |= (KBC_IRQ | MSE_IRQ | UART1_IRQ | UART2_IRQ);139 __sc1200wdt_write_data(WDCF, reg);140 /* set the timeout and get the ball rolling */141 __sc1200wdt_write_data(WDTO, timeout);142 143 spin_unlock(&sc1200wdt_lock);144}145 146static void sc1200wdt_stop(void)147{148 sc1200wdt_write_data(WDTO, 0);149}150 151/* This returns the status of the WDO signal, inactive high. */152static inline int sc1200wdt_status(void)153{154 unsigned char ret;155 156 sc1200wdt_read_data(WDST, &ret);157 /* If the bit is inactive, the watchdog is enabled, so return158 * KEEPALIVEPING which is a bit of a kludge because there's nothing159 * else for enabled/disabled status160 */161 return (ret & 0x01) ? 0 : WDIOF_KEEPALIVEPING;162}163 164static int sc1200wdt_open(struct inode *inode, struct file *file)165{166 /* allow one at a time */167 if (test_and_set_bit(0, &open_flag))168 return -EBUSY;169 170 if (timeout > MAX_TIMEOUT)171 timeout = MAX_TIMEOUT;172 173 sc1200wdt_start();174 pr_info("Watchdog enabled, timeout = %d min(s)", timeout);175 176 return stream_open(inode, file);177}178 179 180static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,181 unsigned long arg)182{183 int new_timeout;184 void __user *argp = (void __user *)arg;185 int __user *p = argp;186 static const struct watchdog_info ident = {187 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |188 WDIOF_MAGICCLOSE,189 .firmware_version = 0,190 .identity = "PC87307/PC97307",191 };192 193 switch (cmd) {194 case WDIOC_GETSUPPORT:195 if (copy_to_user(argp, &ident, sizeof(ident)))196 return -EFAULT;197 return 0;198 199 case WDIOC_GETSTATUS:200 return put_user(sc1200wdt_status(), p);201 202 case WDIOC_GETBOOTSTATUS:203 return put_user(0, p);204 205 case WDIOC_SETOPTIONS:206 {207 int options, retval = -EINVAL;208 209 if (get_user(options, p))210 return -EFAULT;211 212 if (options & WDIOS_DISABLECARD) {213 sc1200wdt_stop();214 retval = 0;215 }216 217 if (options & WDIOS_ENABLECARD) {218 sc1200wdt_start();219 retval = 0;220 }221 222 return retval;223 }224 case WDIOC_KEEPALIVE:225 sc1200wdt_write_data(WDTO, timeout);226 return 0;227 228 case WDIOC_SETTIMEOUT:229 if (get_user(new_timeout, p))230 return -EFAULT;231 /* the API states this is given in secs */232 new_timeout /= 60;233 if (new_timeout < 0 || new_timeout > MAX_TIMEOUT)234 return -EINVAL;235 timeout = new_timeout;236 sc1200wdt_write_data(WDTO, timeout);237 fallthrough; /* and return the new timeout */238 239 case WDIOC_GETTIMEOUT:240 return put_user(timeout * 60, p);241 242 default:243 return -ENOTTY;244 }245}246 247 248static int sc1200wdt_release(struct inode *inode, struct file *file)249{250 if (expect_close == 42) {251 sc1200wdt_stop();252 pr_info("Watchdog disabled\n");253 } else {254 sc1200wdt_write_data(WDTO, timeout);255 pr_crit("Unexpected close!, timeout = %d min(s)\n", timeout);256 }257 clear_bit(0, &open_flag);258 expect_close = 0;259 260 return 0;261}262 263 264static ssize_t sc1200wdt_write(struct file *file, const char __user *data,265 size_t len, loff_t *ppos)266{267 if (len) {268 if (!nowayout) {269 size_t i;270 271 expect_close = 0;272 273 for (i = 0; i != len; i++) {274 char c;275 276 if (get_user(c, data + i))277 return -EFAULT;278 if (c == 'V')279 expect_close = 42;280 }281 }282 283 sc1200wdt_write_data(WDTO, timeout);284 return len;285 }286 287 return 0;288}289 290 291static int sc1200wdt_notify_sys(struct notifier_block *this,292 unsigned long code, void *unused)293{294 if (code == SYS_DOWN || code == SYS_HALT)295 sc1200wdt_stop();296 297 return NOTIFY_DONE;298}299 300 301static struct notifier_block sc1200wdt_notifier = {302 .notifier_call = sc1200wdt_notify_sys,303};304 305static const struct file_operations sc1200wdt_fops = {306 .owner = THIS_MODULE,307 .write = sc1200wdt_write,308 .unlocked_ioctl = sc1200wdt_ioctl,309 .compat_ioctl = compat_ptr_ioctl,310 .open = sc1200wdt_open,311 .release = sc1200wdt_release,312};313 314static struct miscdevice sc1200wdt_miscdev = {315 .minor = WATCHDOG_MINOR,316 .name = "watchdog",317 .fops = &sc1200wdt_fops,318};319 320 321static int __init sc1200wdt_probe(void)322{323 /* The probe works by reading the PMC3 register's default value of 0x0e324 * there is one caveat, if the device disables the parallel port or any325 * of the UARTs we won't be able to detect it.326 * NB. This could be done with accuracy by reading the SID registers,327 * but we don't have access to those io regions.328 */329 330 unsigned char reg;331 332 sc1200wdt_read_data(PMC3, ®);333 reg &= 0x0f; /* we don't want the UART busy bits */334 return (reg == 0x0e) ? 0 : -ENODEV;335}336 337 338#if defined CONFIG_PNP339 340static const struct pnp_device_id scl200wdt_pnp_devices[] = {341 /* National Semiconductor PC87307/PC97307 watchdog component */342 {.id = "NSC0800", .driver_data = 0},343 {.id = ""},344};345 346static int scl200wdt_pnp_probe(struct pnp_dev *dev,347 const struct pnp_device_id *dev_id)348{349 /* this driver only supports one card at a time */350 if (wdt_dev || !isapnp)351 return -EBUSY;352 353 wdt_dev = dev;354 io = pnp_port_start(wdt_dev, 0);355 io_len = pnp_port_len(wdt_dev, 0);356 357 if (!request_region(io, io_len, SC1200_MODULE_NAME)) {358 pr_err("Unable to register IO port %#x\n", io);359 return -EBUSY;360 }361 362 pr_info("PnP device found at io port %#x/%d\n", io, io_len);363 return 0;364}365 366static void scl200wdt_pnp_remove(struct pnp_dev *dev)367{368 if (wdt_dev) {369 release_region(io, io_len);370 wdt_dev = NULL;371 }372}373 374static struct pnp_driver scl200wdt_pnp_driver = {375 .name = "scl200wdt",376 .id_table = scl200wdt_pnp_devices,377 .probe = scl200wdt_pnp_probe,378 .remove = scl200wdt_pnp_remove,379};380 381#endif /* CONFIG_PNP */382 383 384static int __init sc1200wdt_init(void)385{386 int ret;387 388 pr_info("%s\n", SC1200_MODULE_VER);389 390#if defined CONFIG_PNP391 if (isapnp) {392 ret = pnp_register_driver(&scl200wdt_pnp_driver);393 if (ret)394 goto out_clean;395 }396#endif397 398 if (io == -1) {399 pr_err("io parameter must be specified\n");400 ret = -EINVAL;401 goto out_pnp;402 }403 404#if defined CONFIG_PNP405 /* now that the user has specified an IO port and we haven't detected406 * any devices, disable pnp support */407 if (isapnp)408 pnp_unregister_driver(&scl200wdt_pnp_driver);409 isapnp = 0;410#endif411 412 if (!request_region(io, io_len, SC1200_MODULE_NAME)) {413 pr_err("Unable to register IO port %#x\n", io);414 ret = -EBUSY;415 goto out_pnp;416 }417 418 ret = sc1200wdt_probe();419 if (ret)420 goto out_io;421 422 ret = register_reboot_notifier(&sc1200wdt_notifier);423 if (ret) {424 pr_err("Unable to register reboot notifier err = %d\n", ret);425 goto out_io;426 }427 428 ret = misc_register(&sc1200wdt_miscdev);429 if (ret) {430 pr_err("Unable to register miscdev on minor %d\n",431 WATCHDOG_MINOR);432 goto out_rbt;433 }434 435 /* ret = 0 */436 437out_clean:438 return ret;439 440out_rbt:441 unregister_reboot_notifier(&sc1200wdt_notifier);442 443out_io:444 release_region(io, io_len);445 446out_pnp:447#if defined CONFIG_PNP448 if (isapnp)449 pnp_unregister_driver(&scl200wdt_pnp_driver);450#endif451 goto out_clean;452}453 454 455static void __exit sc1200wdt_exit(void)456{457 misc_deregister(&sc1200wdt_miscdev);458 unregister_reboot_notifier(&sc1200wdt_notifier);459 460#if defined CONFIG_PNP461 if (isapnp)462 pnp_unregister_driver(&scl200wdt_pnp_driver);463 else464#endif465 release_region(io, io_len);466}467 468module_init(sc1200wdt_init);469module_exit(sc1200wdt_exit);470 471MODULE_AUTHOR("Zwane Mwaikambo <zwane@commfireservices.com>");472MODULE_DESCRIPTION(473 "Driver for National Semiconductor PC87307/PC97307 watchdog component");474MODULE_LICENSE("GPL");475