watchdog: it87: Convert to use watchdog core infrastructure

Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
This commit is contained in:
Guenter Roeck 2017-06-10 21:04:33 -07:00 committed by Wim Van Sebroeck
parent 7334030173
commit 1d7b80394c
2 changed files with 70 additions and 302 deletions

View file

@ -1069,6 +1069,7 @@ config IT8712F_WDT
config IT87_WDT config IT87_WDT
tristate "IT87 Watchdog Timer" tristate "IT87 Watchdog Timer"
depends on X86 depends on X86
select WATCHDOG_CORE
---help--- ---help---
This is the driver for the hardware watchdog on the ITE IT8620, This is the driver for the hardware watchdog on the ITE IT8620,
IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728 IT8702, IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 and IT8728

View file

@ -32,26 +32,18 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/uaccess.h>
#include <linux/io.h> #include <linux/io.h>
#define WATCHDOG_VERSION "1.14"
#define WATCHDOG_NAME "IT87 WDT" #define WATCHDOG_NAME "IT87 WDT"
#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
#define WD_MAGIC 'V'
/* Defaults for Module Parameter */ /* Defaults for Module Parameter */
#define DEFAULT_NOGAMEPORT 0 #define DEFAULT_NOGAMEPORT 0
#define DEFAULT_NOCIR 0 #define DEFAULT_NOCIR 0
#define DEFAULT_EXCLUSIVE 1
#define DEFAULT_TIMEOUT 60 #define DEFAULT_TIMEOUT 60
#define DEFAULT_TESTMODE 0 #define DEFAULT_TESTMODE 0
#define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT #define DEFAULT_NOWAYOUT WATCHDOG_NOWAYOUT
@ -129,23 +121,17 @@
#define GP_BASE_DEFAULT 0x0201 #define GP_BASE_DEFAULT 0x0201
/* wdt_status */ /* wdt_status */
#define WDTS_TIMER_RUN 0 #define WDTS_USE_GP 0
#define WDTS_DEV_OPEN 1 #define WDTS_USE_CIR 1
#define WDTS_KEEPALIVE 2
#define WDTS_LOCKED 3
#define WDTS_USE_GP 4
#define WDTS_EXPECTED 5
#define WDTS_USE_CIR 6
static unsigned int base, gpact, ciract, max_units, chip_type; static unsigned int base, gpact, ciract, max_units, chip_type;
static unsigned long wdt_status; static unsigned long wdt_status;
static int nogameport = DEFAULT_NOGAMEPORT; static int nogameport = DEFAULT_NOGAMEPORT;
static int nocir = DEFAULT_NOCIR; static int nocir = DEFAULT_NOCIR;
static int exclusive = DEFAULT_EXCLUSIVE; static unsigned int timeout = DEFAULT_TIMEOUT;
static int timeout = DEFAULT_TIMEOUT; static int testmode = DEFAULT_TESTMODE;
static int testmode = DEFAULT_TESTMODE; static bool nowayout = DEFAULT_NOWAYOUT;
static bool nowayout = DEFAULT_NOWAYOUT;
module_param(nogameport, int, 0); module_param(nogameport, int, 0);
MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default=" MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
@ -153,9 +139,6 @@ MODULE_PARM_DESC(nogameport, "Forbid the activation of game port, default="
module_param(nocir, int, 0); module_param(nocir, int, 0);
MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default=" MODULE_PARM_DESC(nocir, "Forbid the use of Consumer IR interrupts to reset timer, default="
__MODULE_STRING(DEFAULT_NOCIR)); __MODULE_STRING(DEFAULT_NOCIR));
module_param(exclusive, int, 0);
MODULE_PARM_DESC(exclusive, "Watchdog exclusive device open, default="
__MODULE_STRING(DEFAULT_EXCLUSIVE));
module_param(timeout, int, 0); module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default=" MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds, default="
__MODULE_STRING(DEFAULT_TIMEOUT)); __MODULE_STRING(DEFAULT_TIMEOUT));
@ -227,7 +210,7 @@ static inline void superio_outw(int val, int reg)
} }
/* Internal function, should be called after superio_select(GPIO) */ /* Internal function, should be called after superio_select(GPIO) */
static void wdt_update_timeout(void) static void _wdt_update_timeout(void)
{ {
unsigned char cfg = WDT_KRST; unsigned char cfg = WDT_KRST;
int tm = timeout; int tm = timeout;
@ -249,6 +232,21 @@ static void wdt_update_timeout(void)
superio_outb(tm>>8, WDTVALMSB); superio_outb(tm>>8, WDTVALMSB);
} }
static int wdt_update_timeout(void)
{
int ret;
ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO);
_wdt_update_timeout();
superio_exit();
return 0;
}
static int wdt_round_time(int t) static int wdt_round_time(int t)
{ {
t += 59; t += 59;
@ -258,25 +256,22 @@ static int wdt_round_time(int t)
/* watchdog timer handling */ /* watchdog timer handling */
static void wdt_keepalive(void) static int wdt_keepalive(struct watchdog_device *wdd)
{ {
int ret = 0;
if (test_bit(WDTS_USE_GP, &wdt_status)) if (test_bit(WDTS_USE_GP, &wdt_status))
inb(base); inb(base);
else if (test_bit(WDTS_USE_CIR, &wdt_status)) else if (test_bit(WDTS_USE_CIR, &wdt_status))
/* The timer reloads with around 5 msec delay */ /* The timer reloads with around 5 msec delay */
outb(0x55, CIR_DR(base)); outb(0x55, CIR_DR(base));
else { else
if (superio_enter()) ret = wdt_update_timeout();
return;
superio_select(GPIO); return ret;
wdt_update_timeout();
superio_exit();
}
set_bit(WDTS_KEEPALIVE, &wdt_status);
} }
static int wdt_start(void) static int wdt_start(struct watchdog_device *wdd)
{ {
int ret = superio_enter(); int ret = superio_enter();
if (ret) if (ret)
@ -287,14 +282,15 @@ static int wdt_start(void)
superio_outb(WDT_GAMEPORT, WDTCTRL); superio_outb(WDT_GAMEPORT, WDTCTRL);
else if (test_bit(WDTS_USE_CIR, &wdt_status)) else if (test_bit(WDTS_USE_CIR, &wdt_status))
superio_outb(WDT_CIRINT, WDTCTRL); superio_outb(WDT_CIRINT, WDTCTRL);
wdt_update_timeout();
_wdt_update_timeout();
superio_exit(); superio_exit();
return 0; return 0;
} }
static int wdt_stop(void) static int wdt_stop(struct watchdog_device *wdd)
{ {
int ret = superio_enter(); int ret = superio_enter();
if (ret) if (ret)
@ -321,280 +317,49 @@ static int wdt_stop(void)
* Used within WDIOC_SETTIMEOUT watchdog device ioctl. * Used within WDIOC_SETTIMEOUT watchdog device ioctl.
*/ */
static int wdt_set_timeout(int t) static int wdt_set_timeout(struct watchdog_device *wdd, unsigned int t)
{ {
if (t < 1 || t > max_units * 60) int ret = 0;
return -EINVAL;
if (t > max_units) if (t > max_units)
timeout = wdt_round_time(t); t = wdt_round_time(t);
else
timeout = t;
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { wdd->timeout = t;
int ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO); if (watchdog_hw_running(wdd))
wdt_update_timeout(); ret = wdt_update_timeout();
superio_exit();
}
return 0;
}
/** return ret;
* wdt_get_status - determines the status supported by watchdog ioctl
* @status: status returned to user space
*
* The status bit of the device does not allow to distinguish
* between a regular system reset and a watchdog forced reset.
* But, in test mode it is useful, so it is supported through
* WDIOC_GETSTATUS watchdog ioctl. Additionally the driver
* reports the keepalive signal and the acception of the magic.
*
* Used within WDIOC_GETSTATUS watchdog device ioctl.
*/
static int wdt_get_status(int *status)
{
*status = 0;
if (testmode) {
int ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO);
if (superio_inb(WDTCTRL) & WDT_ZERO) {
superio_outb(0x00, WDTCTRL);
clear_bit(WDTS_TIMER_RUN, &wdt_status);
*status |= WDIOF_CARDRESET;
}
superio_exit();
}
if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
*status |= WDIOF_KEEPALIVEPING;
if (test_bit(WDTS_EXPECTED, &wdt_status))
*status |= WDIOF_MAGICCLOSE;
return 0;
}
/* /dev/watchdog handling */
/**
* wdt_open - watchdog file_operations .open
* @inode: inode of the device
* @file: file handle to the device
*
* The watchdog timer starts by opening the device.
*
* Used within the file operation of the watchdog device.
*/
static int wdt_open(struct inode *inode, struct file *file)
{
if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
return -EBUSY;
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
int ret;
if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
__module_get(THIS_MODULE);
ret = wdt_start();
if (ret) {
clear_bit(WDTS_LOCKED, &wdt_status);
clear_bit(WDTS_TIMER_RUN, &wdt_status);
clear_bit(WDTS_DEV_OPEN, &wdt_status);
return ret;
}
}
return nonseekable_open(inode, file);
}
/**
* wdt_release - watchdog file_operations .release
* @inode: inode of the device
* @file: file handle to the device
*
* Closing the watchdog device either stops the watchdog timer
* or in the case, that nowayout is set or the magic character
* wasn't written, a critical warning about an running watchdog
* timer is given.
*
* Used within the file operation of the watchdog device.
*/
static int wdt_release(struct inode *inode, struct file *file)
{
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
int ret = wdt_stop();
if (ret) {
/*
* Stop failed. Just keep the watchdog alive
* and hope nothing bad happens.
*/
set_bit(WDTS_EXPECTED, &wdt_status);
wdt_keepalive();
return ret;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status);
} else {
wdt_keepalive();
pr_crit("unexpected close, not stopping watchdog!\n");
}
}
clear_bit(WDTS_DEV_OPEN, &wdt_status);
return 0;
}
/**
* wdt_write - watchdog file_operations .write
* @file: file handle to the watchdog
* @buf: buffer to write
* @count: count of bytes
* @ppos: pointer to the position to write. No seeks allowed
*
* A write to a watchdog device is defined as a keepalive signal. Any
* write of data will do, as we don't define content meaning.
*
* Used within the file operation of the watchdog device.
*/
static ssize_t wdt_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
if (count) {
clear_bit(WDTS_EXPECTED, &wdt_status);
wdt_keepalive();
}
if (!nowayout) {
size_t ofs;
/* note: just in case someone wrote the magic character long ago */
for (ofs = 0; ofs != count; ofs++) {
char c;
if (get_user(c, buf + ofs))
return -EFAULT;
if (c == WD_MAGIC)
set_bit(WDTS_EXPECTED, &wdt_status);
}
}
return count;
} }
static const struct watchdog_info ident = { static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING, .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
.firmware_version = 1, .firmware_version = 1,
.identity = WATCHDOG_NAME, .identity = WATCHDOG_NAME,
}; };
/** static struct watchdog_ops wdt_ops = {
* wdt_ioctl - watchdog file_operations .unlocked_ioctl .owner = THIS_MODULE,
* @file: file handle to the device .start = wdt_start,
* @cmd: watchdog command .stop = wdt_stop,
* @arg: argument pointer .ping = wdt_keepalive,
* .set_timeout = wdt_set_timeout,
* The watchdog API defines a common set of functions for all watchdogs };
* according to their available features.
*
* Used within the file operation of the watchdog device.
*/
static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) static struct watchdog_device wdt_dev = {
{ .info = &ident,
int rc = 0, status, new_options, new_timeout; .ops = &wdt_ops,
union { .min_timeout = 1,
struct watchdog_info __user *ident; };
int __user *i;
} uarg;
uarg.i = (int __user *)arg;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(uarg.ident,
&ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
rc = wdt_get_status(&status);
if (rc)
return rc;
return put_user(status, uarg.i);
case WDIOC_GETBOOTSTATUS:
return put_user(0, uarg.i);
case WDIOC_KEEPALIVE:
wdt_keepalive();
return 0;
case WDIOC_SETOPTIONS:
if (get_user(new_options, uarg.i))
return -EFAULT;
switch (new_options) {
case WDIOS_DISABLECARD:
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
rc = wdt_stop();
if (rc)
return rc;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status);
return 0;
case WDIOS_ENABLECARD:
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
rc = wdt_start();
if (rc) {
clear_bit(WDTS_TIMER_RUN, &wdt_status);
return rc;
}
}
return 0;
default:
return -EFAULT;
}
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, uarg.i))
return -EFAULT;
rc = wdt_set_timeout(new_timeout);
case WDIOC_GETTIMEOUT:
if (put_user(timeout, uarg.i))
return -EFAULT;
return rc;
default:
return -ENOTTY;
}
}
static int wdt_notify_sys(struct notifier_block *this, unsigned long code, static int wdt_notify_sys(struct notifier_block *this, unsigned long code,
void *unused) void *unused)
{ {
if (code == SYS_DOWN || code == SYS_HALT) if (code == SYS_DOWN || code == SYS_HALT)
wdt_stop(); wdt_stop(&wdt_dev);
return NOTIFY_DONE; return NOTIFY_DONE;
} }
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wdt_write,
.unlocked_ioctl = wdt_ioctl,
.open = wdt_open,
.release = wdt_release,
};
static struct miscdevice wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &wdt_fops,
};
static struct notifier_block wdt_notifier = { static struct notifier_block wdt_notifier = {
.notifier_call = wdt_notify_sys, .notifier_call = wdt_notify_sys,
}; };
@ -708,19 +473,15 @@ static int __init it87_wdt_init(void)
if (timeout > max_units) if (timeout > max_units)
timeout = wdt_round_time(timeout); timeout = wdt_round_time(timeout);
wdt_dev.timeout = timeout;
wdt_dev.max_timeout = max_units * 60;
rc = register_reboot_notifier(&wdt_notifier); rc = register_reboot_notifier(&wdt_notifier);
if (rc) { if (rc) {
pr_err("Cannot register reboot notifier (err=%d)\n", rc); pr_err("Cannot register reboot notifier (err=%d)\n", rc);
goto err_out_region; goto err_out_region;
} }
rc = misc_register(&wdt_miscdev);
if (rc) {
pr_err("Cannot register miscdev on minor=%d (err=%d)\n",
wdt_miscdev.minor, rc);
goto err_out_reboot;
}
/* Initialize CIR to use it as keepalive source */ /* Initialize CIR to use it as keepalive source */
if (test_bit(WDTS_USE_CIR, &wdt_status)) { if (test_bit(WDTS_USE_CIR, &wdt_status)) {
outb(0x00, CIR_RCR(base)); outb(0x00, CIR_RCR(base));
@ -732,9 +493,15 @@ static int __init it87_wdt_init(void)
outb(0x09, CIR_IER(base)); outb(0x09, CIR_IER(base));
} }
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d exclusive=%d nogameport=%d nocir=%d)\n", rc = watchdog_register_device(&wdt_dev);
if (rc) {
pr_err("Cannot register watchdog device (err=%d)\n", rc);
goto err_out_reboot;
}
pr_info("Chip IT%04x revision %d initialized. timeout=%d sec (nowayout=%d testmode=%d nogameport=%d nocir=%d)\n",
chip_type, chip_rev, timeout, chip_type, chip_rev, timeout,
nowayout, testmode, exclusive, nogameport, nocir); nowayout, testmode, nogameport, nocir);
superio_exit(); superio_exit();
return 0; return 0;
@ -778,7 +545,7 @@ static void __exit it87_wdt_exit(void)
superio_exit(); superio_exit();
} }
misc_deregister(&wdt_miscdev); watchdog_unregister_device(&wdt_dev);
unregister_reboot_notifier(&wdt_notifier); unregister_reboot_notifier(&wdt_notifier);
if (test_bit(WDTS_USE_GP, &wdt_status)) if (test_bit(WDTS_USE_GP, &wdt_status))