···885885config W83627HF_WDT886886 tristate "W83627HF/W83627DHG Watchdog Timer"887887 depends on X86888888+ select WATCHDOG_CORE888889 ---help---889890 This is the driver for the hardware watchdog on the W83627HF chipset890891 as used in Advantech PC-9578 and Tyan S2721-533 motherboards
+52-173
drivers/watchdog/w83627hf_wdt.c
···11/*22 * w83627hf/thf WDT driver33 *44+ * (c) Copyright 2013 Guenter Roeck55+ * converted to watchdog infrastructure66+ *47 * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com>58 * added support for W83627THF.69 *···3431#include <linux/module.h>3532#include <linux/moduleparam.h>3633#include <linux/types.h>3737-#include <linux/miscdevice.h>3834#include <linux/watchdog.h>3939-#include <linux/fs.h>4035#include <linux/ioport.h>4136#include <linux/notifier.h>4237#include <linux/reboot.h>4338#include <linux/init.h>4444-#include <linux/spinlock.h>4539#include <linux/io.h>4646-#include <linux/uaccess.h>4747-48404941#define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT"5042#define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */5151-5252-static unsigned long wdt_is_open;5353-static char expect_close;5454-static DEFINE_SPINLOCK(io_lock);55435644/* You must set this - there is no sane way to probe for this board. */5745static int wdt_io = 0x2E;5846module_param(wdt_io, int, 0);5947MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)");60486161-static int timeout = WATCHDOG_TIMEOUT; /* in seconds */4949+static int timeout; /* in seconds */6250module_param(timeout, int, 0);6351MODULE_PARM_DESC(timeout,6452 "Watchdog timeout in seconds. 1 <= timeout <= 255, default="···104110/* tyan motherboards seem to set F5 to 0x4C ?105111 * So explicitly init to appropriate value. */106112107107-static void w83627hf_init(void)113113+static void w83627hf_init(struct watchdog_device *wdog)108114{109115 unsigned char t;110116···114120 t = inb_p(WDT_EFDR); /* read CRF6 */115121 if (t != 0) {116122 pr_info("Watchdog already running. Resetting timeout to %d sec\n",117117- timeout);118118- outb_p(timeout, WDT_EFDR); /* Write back to CRF6 */123123+ wdog->timeout);124124+ outb_p(wdog->timeout, WDT_EFDR); /* Write back to CRF6 */119125 }120126121127 outb_p(0xF5, WDT_EFER); /* Select CRF5 */···135141 w83627hf_unselect_wd_register();136142}137143138138-static void wdt_set_time(int timeout)144144+static int wdt_set_time(unsigned int timeout)139145{140140- spin_lock(&io_lock);141141-142146 w83627hf_select_wd_register();143147144148 outb_p(0xF6, WDT_EFER); /* Select CRF6 */···144152145153 w83627hf_unselect_wd_register();146154147147- spin_unlock(&io_lock);148148-}149149-150150-static int wdt_ping(void)151151-{152152- wdt_set_time(timeout);153155 return 0;154156}155157156156-static int wdt_disable(void)158158+static int wdt_start(struct watchdog_device *wdog)157159{158158- wdt_set_time(0);160160+ return wdt_set_time(wdog->timeout);161161+}162162+163163+static int wdt_stop(struct watchdog_device *wdog)164164+{165165+ return wdt_set_time(0);166166+}167167+168168+static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout)169169+{170170+ wdog->timeout = timeout;171171+159172 return 0;160173}161174162162-static int wdt_set_heartbeat(int t)175175+static unsigned int wdt_get_time(struct watchdog_device *wdog)163176{164164- if (t < 1 || t > 255)165165- return -EINVAL;166166- timeout = t;167167- return 0;168168-}169169-170170-static int wdt_get_time(void)171171-{172172- int timeleft;173173-174174- spin_lock(&io_lock);177177+ unsigned int timeleft;175178176179 w83627hf_select_wd_register();177180···175188176189 w83627hf_unselect_wd_register();177190178178- spin_unlock(&io_lock);179179-180191 return timeleft;181181-}182182-183183-static ssize_t wdt_write(struct file *file, const char __user *buf,184184- size_t count, loff_t *ppos)185185-{186186- if (count) {187187- if (!nowayout) {188188- size_t i;189189-190190- expect_close = 0;191191-192192- for (i = 0; i != count; i++) {193193- char c;194194- if (get_user(c, buf + i))195195- return -EFAULT;196196- if (c == 'V')197197- expect_close = 42;198198- }199199- }200200- wdt_ping();201201- }202202- return count;203203-}204204-205205-static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)206206-{207207- void __user *argp = (void __user *)arg;208208- int __user *p = argp;209209- int timeval;210210- static const struct watchdog_info ident = {211211- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |212212- WDIOF_MAGICCLOSE,213213- .firmware_version = 1,214214- .identity = "W83627HF WDT",215215- };216216-217217- switch (cmd) {218218- case WDIOC_GETSUPPORT:219219- if (copy_to_user(argp, &ident, sizeof(ident)))220220- return -EFAULT;221221- break;222222- case WDIOC_GETSTATUS:223223- case WDIOC_GETBOOTSTATUS:224224- return put_user(0, p);225225- case WDIOC_SETOPTIONS:226226- {227227- int options, retval = -EINVAL;228228-229229- if (get_user(options, p))230230- return -EFAULT;231231- if (options & WDIOS_DISABLECARD) {232232- wdt_disable();233233- retval = 0;234234- }235235- if (options & WDIOS_ENABLECARD) {236236- wdt_ping();237237- retval = 0;238238- }239239- return retval;240240- }241241- case WDIOC_KEEPALIVE:242242- wdt_ping();243243- break;244244- case WDIOC_SETTIMEOUT:245245- if (get_user(timeval, p))246246- return -EFAULT;247247- if (wdt_set_heartbeat(timeval))248248- return -EINVAL;249249- wdt_ping();250250- /* Fall */251251- case WDIOC_GETTIMEOUT:252252- return put_user(timeout, p);253253- case WDIOC_GETTIMELEFT:254254- timeval = wdt_get_time();255255- return put_user(timeval, p);256256- default:257257- return -ENOTTY;258258- }259259- return 0;260260-}261261-262262-static int wdt_open(struct inode *inode, struct file *file)263263-{264264- if (test_and_set_bit(0, &wdt_is_open))265265- return -EBUSY;266266- /*267267- * Activate268268- */269269-270270- wdt_ping();271271- return nonseekable_open(inode, file);272272-}273273-274274-static int wdt_close(struct inode *inode, struct file *file)275275-{276276- if (expect_close == 42)277277- wdt_disable();278278- else {279279- pr_crit("Unexpected close, not stopping watchdog!\n");280280- wdt_ping();281281- }282282- expect_close = 0;283283- clear_bit(0, &wdt_is_open);284284- return 0;285192}286193287194/*288195 * Notifier for system down289196 */290290-291197static int wdt_notify_sys(struct notifier_block *this, unsigned long code,292198 void *unused)293199{294200 if (code == SYS_DOWN || code == SYS_HALT)295295- wdt_disable(); /* Turn the WDT off */201201+ wdt_set_time(0); /* Turn the WDT off */296202297203 return NOTIFY_DONE;298204}···194314 * Kernel Interfaces195315 */196316197197-static const struct file_operations wdt_fops = {198198- .owner = THIS_MODULE,199199- .llseek = no_llseek,200200- .write = wdt_write,201201- .unlocked_ioctl = wdt_ioctl,202202- .open = wdt_open,203203- .release = wdt_close,317317+static struct watchdog_info wdt_info = {318318+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,319319+ .identity = "W83627HF Watchdog",204320};205321206206-static struct miscdevice wdt_miscdev = {207207- .minor = WATCHDOG_MINOR,208208- .name = "watchdog",209209- .fops = &wdt_fops,322322+static struct watchdog_ops wdt_ops = {323323+ .owner = THIS_MODULE,324324+ .start = wdt_start,325325+ .stop = wdt_stop,326326+ .set_timeout = wdt_set_timeout,327327+ .get_timeleft = wdt_get_time,328328+};329329+330330+static struct watchdog_device wdt_dev = {331331+ .info = &wdt_info,332332+ .ops = &wdt_ops,333333+ .timeout = WATCHDOG_TIMEOUT,334334+ .min_timeout = 1,335335+ .max_timeout = 255,210336};211337212338/*···230344231345 pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n");232346233233- if (wdt_set_heartbeat(timeout)) {234234- wdt_set_heartbeat(WATCHDOG_TIMEOUT);235235- pr_info("timeout value must be 1 <= timeout <= 255, using %d\n",236236- WATCHDOG_TIMEOUT);237237- }238238-239347 if (!request_region(wdt_io, 1, WATCHDOG_NAME)) {240348 pr_err("I/O address 0x%04x already in use\n", wdt_io);241241- ret = -EIO;242242- goto out;349349+ return -EIO;243350 }244351245245- w83627hf_init();352352+ watchdog_init_timeout(&wdt_dev, timeout, NULL);353353+ watchdog_set_nowayout(&wdt_dev, nowayout);354354+355355+ w83627hf_init(&wdt_dev);246356247357 ret = register_reboot_notifier(&wdt_notifier);248358 if (ret != 0) {···246364 goto unreg_regions;247365 }248366249249- ret = misc_register(&wdt_miscdev);250250- if (ret != 0) {251251- pr_err("cannot register miscdev on minor=%d (err=%d)\n",252252- WATCHDOG_MINOR, ret);367367+ ret = watchdog_register_device(&wdt_dev);368368+ if (ret)253369 goto unreg_reboot;254254- }255370256371 pr_info("initialized. timeout=%d sec (nowayout=%d)\n",257257- timeout, nowayout);372372+ wdt_dev.timeout, nowayout);258373259259-out:260374 return ret;375375+261376unreg_reboot:262377 unregister_reboot_notifier(&wdt_notifier);263378unreg_regions:264379 release_region(wdt_io, 1);265265- goto out;380380+ return ret;266381}267382268383static void __exit wdt_exit(void)269384{270270- misc_deregister(&wdt_miscdev);385385+ watchdog_unregister_device(&wdt_dev);271386 unregister_reboot_notifier(&wdt_notifier);272387 release_region(wdt_io, 1);273388}