Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[WATCHDOG] IT8212F watchdog driver

This patch adds support for the ITE Tech Inc. IT8712F EC-LPC Super I/O
chipset found on many Pentium III and AMD motherboards. Developed using code
from other watchdog drivers and the datasheet on ITE Tech homepage.

Signed-off-by: Jorge Boncompte <jorge@dti2.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

authored by

Jorge Boncompte [DTI2] and committed by
Wim Van Sebroeck
38ff6fd2 6027f661

+411
+10
drivers/watchdog/Kconfig
··· 392 392 devices. At this moment we only have additional support for some 393 393 SuperMicro Inc. motherboards. 394 394 395 + config IT8712F_WDT 396 + tristate "IT8712F (Smart Guardian) Watchdog Timer" 397 + depends on X86 398 + ---help--- 399 + This is the driver for the built-in watchdog timer on the IT8712F 400 + Super I/0 chipset used on many motherboards. 401 + 402 + To compile this driver as a module, choose M here: the 403 + module will be called it8712f_wdt. 404 + 395 405 config SC1200_WDT 396 406 tristate "National Semiconductor PC87307/PC97307 (ala SC1200) Watchdog" 397 407 depends on X86
+1
drivers/watchdog/Makefile
··· 66 66 obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o 67 67 obj-$(CONFIG_I6300ESB_WDT) += i6300esb.o 68 68 obj-$(CONFIG_ITCO_WDT) += iTCO_wdt.o iTCO_vendor_support.o 69 + obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o 69 70 obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o 70 71 obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o 71 72 obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
+400
drivers/watchdog/it8712f_wdt.c
··· 1 + /* 2 + * IT8712F "Smart Guardian" Watchdog support 3 + * 4 + * Copyright (c) 2006-2007 Jorge Boncompte - DTI2 <jorge@dti2.net> 5 + * 6 + * Based on info and code taken from: 7 + * 8 + * drivers/char/watchdog/scx200_wdt.c 9 + * drivers/hwmon/it87.c 10 + * IT8712F EC-LPC I/O Preliminary Specification 0.9.2.pdf 11 + * 12 + * This program is free software; you can redistribute it and/or 13 + * modify it under the terms of the GNU General Public License as 14 + * published by the Free Software Foundation; either version 2 of the 15 + * License, or (at your option) any later version. 16 + * 17 + * The author(s) of this software shall not be held liable for damages 18 + * of any nature resulting due to the use of this software. This 19 + * software is provided AS-IS with no warranties. 20 + */ 21 + 22 + #include <linux/module.h> 23 + #include <linux/moduleparam.h> 24 + #include <linux/init.h> 25 + #include <linux/miscdevice.h> 26 + #include <linux/watchdog.h> 27 + #include <linux/notifier.h> 28 + #include <linux/reboot.h> 29 + #include <linux/fs.h> 30 + #include <linux/pci.h> 31 + #include <linux/spinlock.h> 32 + 33 + #include <asm/uaccess.h> 34 + #include <asm/io.h> 35 + 36 + #define NAME "it8712f_wdt" 37 + 38 + MODULE_AUTHOR("Jorge Boncompte - DTI2 <jorge@dti2.net>"); 39 + MODULE_DESCRIPTION("IT8712F Watchdog Driver"); 40 + MODULE_LICENSE("GPL"); 41 + MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 42 + 43 + static int margin = 60; /* in seconds */ 44 + module_param(margin, int, 0); 45 + MODULE_PARM_DESC(margin, "Watchdog margin in seconds"); 46 + 47 + static int nowayout = WATCHDOG_NOWAYOUT; 48 + module_param(nowayout, int, 0); 49 + MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); 50 + 51 + static struct semaphore it8712f_wdt_sem; 52 + static unsigned expect_close; 53 + static spinlock_t io_lock; 54 + 55 + /* Dog Food address - We use the game port address */ 56 + static unsigned short address; 57 + 58 + #define REG 0x2e /* The register to read/write */ 59 + #define VAL 0x2f /* The value to read/write */ 60 + 61 + #define LDN 0x07 /* Register: Logical device select */ 62 + #define DEVID 0x20 /* Register: Device ID */ 63 + #define DEVREV 0x22 /* Register: Device Revision */ 64 + #define ACT_REG 0x30 /* LDN Register: Activation */ 65 + #define BASE_REG 0x60 /* LDN Register: Base address */ 66 + 67 + #define IT8712F_DEVID 0x8712 68 + 69 + #define LDN_GPIO 0x07 /* GPIO and Watch Dog Timer */ 70 + #define LDN_GAME 0x09 /* Game Port */ 71 + 72 + #define WDT_CONTROL 0x71 /* WDT Register: Control */ 73 + #define WDT_CONFIG 0x72 /* WDT Register: Configuration */ 74 + #define WDT_TIMEOUT 0x73 /* WDT Register: Timeout Value */ 75 + 76 + #define WDT_RESET_GAME 0x10 77 + #define WDT_RESET_KBD 0x20 78 + #define WDT_RESET_MOUSE 0x40 79 + #define WDT_RESET_CIR 0x80 80 + 81 + #define WDT_UNIT_SEC 0x80 /* If 0 in MINUTES */ 82 + 83 + #define WDT_OUT_PWROK 0x10 84 + #define WDT_OUT_KRST 0x40 85 + 86 + static int 87 + superio_inb(int reg) 88 + { 89 + outb(reg, REG); 90 + return inb(VAL); 91 + } 92 + 93 + static void 94 + superio_outb(int val, int reg) 95 + { 96 + outb(reg, REG); 97 + outb(val, VAL); 98 + } 99 + 100 + static int 101 + superio_inw(int reg) 102 + { 103 + int val; 104 + outb(reg++, REG); 105 + val = inb(VAL) << 8; 106 + outb(reg, REG); 107 + val |= inb(VAL); 108 + return val; 109 + } 110 + 111 + static inline void 112 + superio_select(int ldn) 113 + { 114 + outb(LDN, REG); 115 + outb(ldn, VAL); 116 + } 117 + 118 + static inline void 119 + superio_enter(void) 120 + { 121 + spin_lock(&io_lock); 122 + outb(0x87, REG); 123 + outb(0x01, REG); 124 + outb(0x55, REG); 125 + outb(0x55, REG); 126 + } 127 + 128 + static inline void 129 + superio_exit(void) 130 + { 131 + outb(0x02, REG); 132 + outb(0x02, VAL); 133 + spin_unlock(&io_lock); 134 + } 135 + 136 + static inline void 137 + it8712f_wdt_ping(void) 138 + { 139 + inb(address); 140 + } 141 + 142 + static void 143 + it8712f_wdt_update_margin(void) 144 + { 145 + int config = WDT_OUT_KRST | WDT_OUT_PWROK; 146 + 147 + printk(KERN_INFO NAME ": timer margin %d seconds\n", margin); 148 + 149 + /* The timeout register only has 8bits wide */ 150 + if (margin < 256) 151 + config |= WDT_UNIT_SEC; /* else UNIT are MINUTES */ 152 + superio_outb(config, WDT_CONFIG); 153 + 154 + superio_outb((margin > 255) ? (margin / 60) : margin, WDT_TIMEOUT); 155 + } 156 + 157 + static void 158 + it8712f_wdt_enable(void) 159 + { 160 + printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); 161 + superio_enter(); 162 + superio_select(LDN_GPIO); 163 + 164 + superio_outb(WDT_RESET_GAME, WDT_CONTROL); 165 + 166 + it8712f_wdt_update_margin(); 167 + 168 + superio_exit(); 169 + 170 + it8712f_wdt_ping(); 171 + } 172 + 173 + static void 174 + it8712f_wdt_disable(void) 175 + { 176 + printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); 177 + 178 + superio_enter(); 179 + superio_select(LDN_GPIO); 180 + 181 + superio_outb(0, WDT_CONFIG); 182 + superio_outb(0, WDT_CONTROL); 183 + superio_outb(0, WDT_TIMEOUT); 184 + 185 + superio_exit(); 186 + } 187 + 188 + static int 189 + it8712f_wdt_notify(struct notifier_block *this, 190 + unsigned long code, void *unused) 191 + { 192 + if (code == SYS_HALT || code == SYS_POWER_OFF) 193 + if (!nowayout) 194 + it8712f_wdt_disable(); 195 + 196 + return NOTIFY_DONE; 197 + } 198 + 199 + static struct notifier_block it8712f_wdt_notifier = { 200 + .notifier_call = it8712f_wdt_notify, 201 + }; 202 + 203 + static ssize_t 204 + it8712f_wdt_write(struct file *file, const char __user *data, 205 + size_t len, loff_t *ppos) 206 + { 207 + /* check for a magic close character */ 208 + if (len) { 209 + size_t i; 210 + 211 + it8712f_wdt_ping(); 212 + 213 + expect_close = 0; 214 + for (i = 0; i < len; ++i) { 215 + char c; 216 + if (get_user(c, data+i)) 217 + return -EFAULT; 218 + if (c == 'V') 219 + expect_close = 42; 220 + } 221 + } 222 + 223 + return len; 224 + } 225 + 226 + static int 227 + it8712f_wdt_ioctl(struct inode *inode, struct file *file, 228 + unsigned int cmd, unsigned long arg) 229 + { 230 + void __user *argp = (void __user *)arg; 231 + int __user *p = argp; 232 + static struct watchdog_info ident = { 233 + .identity = "IT8712F Watchdog", 234 + .firmware_version = 1, 235 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING, 236 + }; 237 + int new_margin; 238 + 239 + switch (cmd) { 240 + default: 241 + return -ENOTTY; 242 + case WDIOC_GETSUPPORT: 243 + if (copy_to_user(argp, &ident, sizeof(ident))) 244 + return -EFAULT; 245 + return 0; 246 + case WDIOC_GETSTATUS: 247 + case WDIOC_GETBOOTSTATUS: 248 + return put_user(0, p); 249 + case WDIOC_KEEPALIVE: 250 + it8712f_wdt_ping(); 251 + return 0; 252 + case WDIOC_SETTIMEOUT: 253 + if (get_user(new_margin, p)) 254 + return -EFAULT; 255 + if (new_margin < 1) 256 + return -EINVAL; 257 + margin = new_margin; 258 + superio_enter(); 259 + superio_select(LDN_GPIO); 260 + 261 + it8712f_wdt_update_margin(); 262 + 263 + superio_exit(); 264 + it8712f_wdt_ping(); 265 + case WDIOC_GETTIMEOUT: 266 + if (put_user(margin, p)) 267 + return -EFAULT; 268 + return 0; 269 + } 270 + } 271 + 272 + static int 273 + it8712f_wdt_open(struct inode *inode, struct file *file) 274 + { 275 + /* only allow one at a time */ 276 + if (down_trylock(&it8712f_wdt_sem)) 277 + return -EBUSY; 278 + it8712f_wdt_enable(); 279 + 280 + return nonseekable_open(inode, file); 281 + } 282 + 283 + static int 284 + it8712f_wdt_release(struct inode *inode, struct file *file) 285 + { 286 + if (expect_close != 42) { 287 + printk(KERN_WARNING NAME 288 + ": watchdog device closed unexpectedly, will not" 289 + " disable the watchdog timer\n"); 290 + } else if (!nowayout) { 291 + it8712f_wdt_disable(); 292 + } 293 + expect_close = 0; 294 + up(&it8712f_wdt_sem); 295 + 296 + return 0; 297 + } 298 + 299 + static struct file_operations it8712f_wdt_fops = { 300 + .owner = THIS_MODULE, 301 + .llseek = no_llseek, 302 + .write = it8712f_wdt_write, 303 + .ioctl = it8712f_wdt_ioctl, 304 + .open = it8712f_wdt_open, 305 + .release = it8712f_wdt_release, 306 + }; 307 + 308 + static struct miscdevice it8712f_wdt_miscdev = { 309 + .minor = WATCHDOG_MINOR, 310 + .name = "watchdog", 311 + .fops = &it8712f_wdt_fops, 312 + }; 313 + 314 + static int __init 315 + it8712f_wdt_find(unsigned short *address) 316 + { 317 + int err = -ENODEV; 318 + int chip_type; 319 + 320 + superio_enter(); 321 + chip_type = superio_inw(DEVID); 322 + if (chip_type != IT8712F_DEVID) 323 + goto exit; 324 + 325 + superio_select(LDN_GAME); 326 + superio_outb(1, ACT_REG); 327 + if (!(superio_inb(ACT_REG) & 0x01)) { 328 + printk(KERN_ERR NAME ": Device not activated, skipping\n"); 329 + goto exit; 330 + } 331 + 332 + *address = superio_inw(BASE_REG); 333 + if (*address == 0) { 334 + printk(KERN_ERR NAME ": Base address not set, skipping\n"); 335 + goto exit; 336 + } 337 + 338 + err = 0; 339 + printk(KERN_DEBUG NAME ": Found IT%04xF chip revision %d - " 340 + "using DogFood address 0x%x\n", 341 + chip_type, superio_inb(DEVREV) & 0x0f, *address); 342 + 343 + exit: 344 + superio_exit(); 345 + return err; 346 + } 347 + 348 + static int __init 349 + it8712f_wdt_init(void) 350 + { 351 + int err = 0; 352 + 353 + spin_lock_init(&io_lock); 354 + 355 + if (it8712f_wdt_find(&address)) 356 + return -ENODEV; 357 + 358 + if (!request_region(address, 1, "IT8712F Watchdog")) { 359 + printk(KERN_WARNING NAME ": watchdog I/O region busy\n"); 360 + return -EBUSY; 361 + } 362 + 363 + it8712f_wdt_disable(); 364 + 365 + sema_init(&it8712f_wdt_sem, 1); 366 + 367 + err = register_reboot_notifier(&it8712f_wdt_notifier); 368 + if (err) { 369 + printk(KERN_ERR NAME ": unable to register reboot notifier\n"); 370 + goto out; 371 + } 372 + 373 + err = misc_register(&it8712f_wdt_miscdev); 374 + if (err) { 375 + printk(KERN_ERR NAME 376 + ": cannot register miscdev on minor=%d (err=%d)\n", 377 + WATCHDOG_MINOR, err); 378 + goto reboot_out; 379 + } 380 + 381 + return 0; 382 + 383 + 384 + reboot_out: 385 + unregister_reboot_notifier(&it8712f_wdt_notifier); 386 + out: 387 + release_region(address, 1); 388 + return err; 389 + } 390 + 391 + static void __exit 392 + it8712f_wdt_exit(void) 393 + { 394 + misc_deregister(&it8712f_wdt_miscdev); 395 + unregister_reboot_notifier(&it8712f_wdt_notifier); 396 + release_region(address, 1); 397 + } 398 + 399 + module_init(it8712f_wdt_init); 400 + module_exit(it8712f_wdt_exit);