1 /* 2 * w83627hf/thf WDT driver 3 * 4 * (c) Copyright 2013 Guenter Roeck 5 * converted to watchdog infrastructure 6 * 7 * (c) Copyright 2007 Vlad Drukker <vlad@storewiz.com> 8 * added support for W83627THF. 9 * 10 * (c) Copyright 2003,2007 Pádraig Brady <P@draigBrady.com> 11 * 12 * Based on advantechwdt.c which is based on wdt.c. 13 * Original copyright messages: 14 * 15 * (c) Copyright 2000-2001 Marek Michalkiewicz <marekm@linux.org.pl> 16 * 17 * (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>, 18 * All Rights Reserved. 19 * 20 * This program is free software; you can redistribute it and/or 21 * modify it under the terms of the GNU General Public License 22 * as published by the Free Software Foundation; either version 23 * 2 of the License, or (at your option) any later version. 24 * 25 * Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 26 * warranty for any of this software. This material is provided 27 * "AS-IS" and at no charge. 28 * 29 * (c) Copyright 1995 Alan Cox <alan@lxorguk.ukuu.org.uk> 30 */ 31 32 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 33 34 #include <linux/module.h> 35 #include <linux/moduleparam.h> 36 #include <linux/types.h> 37 #include <linux/watchdog.h> 38 #include <linux/ioport.h> 39 #include <linux/notifier.h> 40 #include <linux/reboot.h> 41 #include <linux/init.h> 42 #include <linux/io.h> 43 44 #define WATCHDOG_NAME "w83627hf/thf/hg/dhg WDT" 45 #define WATCHDOG_TIMEOUT 60 /* 60 sec default timeout */ 46 47 /* You must set this - there is no sane way to probe for this board. */ 48 static int wdt_io = 0x2E; 49 module_param(wdt_io, int, 0); 50 MODULE_PARM_DESC(wdt_io, "w83627hf/thf WDT io port (default 0x2E)"); 51 52 static int timeout; /* in seconds */ 53 module_param(timeout, int, 0); 54 MODULE_PARM_DESC(timeout, 55 "Watchdog timeout in seconds. 1 <= timeout <= 255, default=" 56 __MODULE_STRING(WATCHDOG_TIMEOUT) "."); 57 58 static bool nowayout = WATCHDOG_NOWAYOUT; 59 module_param(nowayout, bool, 0); 60 MODULE_PARM_DESC(nowayout, 61 "Watchdog cannot be stopped once started (default=" 62 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 63 64 /* 65 * Kernel methods. 66 */ 67 68 #define WDT_EFER (wdt_io+0) /* Extended Function Enable Registers */ 69 #define WDT_EFIR (wdt_io+0) /* Extended Function Index Register 70 (same as EFER) */ 71 #define WDT_EFDR (WDT_EFIR+1) /* Extended Function Data Register */ 72 73 #define W83627HF_LD_WDT 0x08 74 75 static void superio_outb(int reg, int val) 76 { 77 outb(reg, WDT_EFER); 78 outb(val, WDT_EFDR); 79 } 80 81 static inline int superio_inb(int reg) 82 { 83 outb(reg, WDT_EFER); 84 return inb(WDT_EFDR); 85 } 86 87 static int superio_enter(void) 88 { 89 if (!request_muxed_region(wdt_io, 2, WATCHDOG_NAME)) 90 return -EBUSY; 91 92 outb_p(0x87, WDT_EFER); /* Enter extended function mode */ 93 outb_p(0x87, WDT_EFER); /* Again according to manual */ 94 95 return 0; 96 } 97 98 static void superio_select(int ld) 99 { 100 superio_outb(0x07, ld); 101 } 102 103 static void superio_exit(void) 104 { 105 outb_p(0xAA, WDT_EFER); /* Leave extended function mode */ 106 release_region(wdt_io, 2); 107 } 108 109 /* tyan motherboards seem to set F5 to 0x4C ? 110 * So explicitly init to appropriate value. */ 111 112 static int w83627hf_init(struct watchdog_device *wdog) 113 { 114 int ret; 115 unsigned char t; 116 117 ret = superio_enter(); 118 if (ret) 119 return ret; 120 121 superio_select(W83627HF_LD_WDT); 122 t = superio_inb(0x20); /* check chip version */ 123 if (t == 0x82) { /* W83627THF */ 124 t = (superio_inb(0x2b) & 0xf7); 125 superio_outb(0x2b, t | 0x04); /* set GPIO3 to WDT0 */ 126 } else if (t == 0x88 || t == 0xa0) { /* W83627EHF / W83627DHG */ 127 t = superio_inb(0x2d); 128 superio_outb(0x2d, t & ~0x01); /* set GPIO5 to WDT0 */ 129 } 130 131 /* set CR30 bit 0 to activate GPIO2 */ 132 t = superio_inb(0x30); 133 if (!(t & 0x01)) 134 superio_outb(0x30, t | 0x01); 135 136 t = superio_inb(0xF6); 137 if (t != 0) { 138 pr_info("Watchdog already running. Resetting timeout to %d sec\n", 139 wdog->timeout); 140 superio_outb(0xF6, wdog->timeout); 141 } 142 143 /* set second mode & disable keyboard turning off watchdog */ 144 t = superio_inb(0xF5) & ~0x0C; 145 /* enable the WDTO# output low pulse to the KBRST# pin */ 146 t |= 0x02; 147 superio_outb(0xF5, t); 148 149 /* disable keyboard & mouse turning off watchdog */ 150 t = superio_inb(0xF7) & ~0xC0; 151 superio_outb(0xF7, t); 152 153 superio_exit(); 154 155 return 0; 156 } 157 158 static int wdt_set_time(unsigned int timeout) 159 { 160 int ret; 161 162 ret = superio_enter(); 163 if (ret) 164 return ret; 165 166 superio_select(W83627HF_LD_WDT); 167 superio_outb(0xF6, timeout); 168 superio_exit(); 169 170 return 0; 171 } 172 173 static int wdt_start(struct watchdog_device *wdog) 174 { 175 return wdt_set_time(wdog->timeout); 176 } 177 178 static int wdt_stop(struct watchdog_device *wdog) 179 { 180 return wdt_set_time(0); 181 } 182 183 static int wdt_set_timeout(struct watchdog_device *wdog, unsigned int timeout) 184 { 185 wdog->timeout = timeout; 186 187 return 0; 188 } 189 190 static unsigned int wdt_get_time(struct watchdog_device *wdog) 191 { 192 unsigned int timeleft; 193 int ret; 194 195 ret = superio_enter(); 196 if (ret) 197 return 0; 198 199 superio_select(W83627HF_LD_WDT); 200 timeleft = superio_inb(0xF6); 201 superio_exit(); 202 203 return timeleft; 204 } 205 206 /* 207 * Notifier for system down 208 */ 209 static int wdt_notify_sys(struct notifier_block *this, unsigned long code, 210 void *unused) 211 { 212 if (code == SYS_DOWN || code == SYS_HALT) 213 wdt_set_time(0); /* Turn the WDT off */ 214 215 return NOTIFY_DONE; 216 } 217 218 /* 219 * Kernel Interfaces 220 */ 221 222 static struct watchdog_info wdt_info = { 223 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 224 .identity = "W83627HF Watchdog", 225 }; 226 227 static struct watchdog_ops wdt_ops = { 228 .owner = THIS_MODULE, 229 .start = wdt_start, 230 .stop = wdt_stop, 231 .set_timeout = wdt_set_timeout, 232 .get_timeleft = wdt_get_time, 233 }; 234 235 static struct watchdog_device wdt_dev = { 236 .info = &wdt_info, 237 .ops = &wdt_ops, 238 .timeout = WATCHDOG_TIMEOUT, 239 .min_timeout = 1, 240 .max_timeout = 255, 241 }; 242 243 /* 244 * The WDT needs to learn about soft shutdowns in order to 245 * turn the timebomb registers off. 246 */ 247 248 static struct notifier_block wdt_notifier = { 249 .notifier_call = wdt_notify_sys, 250 }; 251 252 static int __init wdt_init(void) 253 { 254 int ret; 255 256 pr_info("WDT driver for the Winbond(TM) W83627HF/THF/HG/DHG Super I/O chip initialising\n"); 257 258 watchdog_init_timeout(&wdt_dev, timeout, NULL); 259 watchdog_set_nowayout(&wdt_dev, nowayout); 260 261 ret = w83627hf_init(&wdt_dev); 262 if (ret) { 263 pr_err("failed to initialize watchdog (err=%d)\n", ret); 264 return ret; 265 } 266 267 ret = register_reboot_notifier(&wdt_notifier); 268 if (ret != 0) { 269 pr_err("cannot register reboot notifier (err=%d)\n", ret); 270 return ret; 271 } 272 273 ret = watchdog_register_device(&wdt_dev); 274 if (ret) 275 goto unreg_reboot; 276 277 pr_info("initialized. timeout=%d sec (nowayout=%d)\n", 278 wdt_dev.timeout, nowayout); 279 280 return ret; 281 282 unreg_reboot: 283 unregister_reboot_notifier(&wdt_notifier); 284 return ret; 285 } 286 287 static void __exit wdt_exit(void) 288 { 289 watchdog_unregister_device(&wdt_dev); 290 unregister_reboot_notifier(&wdt_notifier); 291 } 292 293 module_init(wdt_init); 294 module_exit(wdt_exit); 295 296 MODULE_LICENSE("GPL"); 297 MODULE_AUTHOR("Pádraig Brady <P@draigBrady.com>"); 298 MODULE_DESCRIPTION("w83627hf/thf WDT driver"); 299