Commit ad6b646f authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog

* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
  [WATCHDOG] Add support for the WM8350 watchdog
  [WATCHDOG] Add SMSC SCH311x Watchdog Timer.
  [WATCHDOG] ib700wdt - add timeout parameter
parents 61420f59 006948ba
...@@ -55,6 +55,13 @@ config SOFT_WATCHDOG ...@@ -55,6 +55,13 @@ config SOFT_WATCHDOG
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called softdog. module will be called softdog.
config WM8350_WATCHDOG
tristate "WM8350 watchdog"
depends on MFD_WM8350
help
Support for the watchdog in the WM8350 AudioPlus PMIC. When
the watchdog triggers the system will be reset.
# ALPHA Architecture # ALPHA Architecture
# ARM Architecture # ARM Architecture
...@@ -551,6 +558,18 @@ config CPU5_WDT ...@@ -551,6 +558,18 @@ config CPU5_WDT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called cpu5wdt. module will be called cpu5wdt.
config SMSC_SCH311X_WDT
tristate "SMSC SCH311X Watchdog Timer"
depends on X86
---help---
This is the driver for the hardware watchdog timer on the
SMSC SCH3112, SCH3114 and SCH3116 Super IO chipset
(LPC IO with 8042 KBC, Reset Generation, HWM and multiple
serial ports).
To compile this driver as a module, choose M here: the
module will be called sch311x_wdt.
config SMSC37B787_WDT config SMSC37B787_WDT
tristate "Winbond SMsC37B787 Watchdog Timer" tristate "Winbond SMsC37B787 Watchdog Timer"
depends on X86 depends on X86
......
...@@ -83,6 +83,7 @@ obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o ...@@ -83,6 +83,7 @@ obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
obj-$(CONFIG_SBC8360_WDT) += sbc8360.o obj-$(CONFIG_SBC8360_WDT) += sbc8360.o
obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o obj-$(CONFIG_SBC7240_WDT) += sbc7240_wdt.o
obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o obj-$(CONFIG_CPU5_WDT) += cpu5wdt.o
obj-$(CONFIG_SMSC_SCH311X_WDT) += sch311x_wdt.o
obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o obj-$(CONFIG_SMSC37B787_WDT) += smsc37b787_wdt.o
obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o obj-$(CONFIG_W83627HF_WDT) += w83627hf_wdt.o
obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o obj-$(CONFIG_W83697HF_WDT) += w83697hf_wdt.o
...@@ -133,4 +134,5 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o ...@@ -133,4 +134,5 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
# XTENSA Architecture # XTENSA Architecture
# Architecture Independant # Architecture Independant
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
...@@ -91,32 +91,16 @@ static char expect_close; ...@@ -91,32 +91,16 @@ static char expect_close;
* *
*/ */
static int wd_times[] = {
30, /* 0x0 */
28, /* 0x1 */
26, /* 0x2 */
24, /* 0x3 */
22, /* 0x4 */
20, /* 0x5 */
18, /* 0x6 */
16, /* 0x7 */
14, /* 0x8 */
12, /* 0x9 */
10, /* 0xA */
8, /* 0xB */
6, /* 0xC */
4, /* 0xD */
2, /* 0xE */
0, /* 0xF */
};
#define WDT_STOP 0x441 #define WDT_STOP 0x441
#define WDT_START 0x443 #define WDT_START 0x443
/* Default timeout */ /* Default timeout */
#define WD_TIMO 0 /* 30 seconds +/- 20%, from table */ #define WATCHDOG_TIMEOUT 30 /* 30 seconds +/- 20% */
static int timeout = WATCHDOG_TIMEOUT; /* in seconds */
static int wd_margin = WD_TIMO; module_param(timeout, int, 0);
MODULE_PARM_DESC(timeout,
"Watchdog timeout in seconds. 0<= timeout <=30, default="
__MODULE_STRING(WATCHDOG_TIMEOUT) ".");
static int nowayout = WATCHDOG_NOWAYOUT; static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0); module_param(nowayout, int, 0);
...@@ -131,6 +115,8 @@ MODULE_PARM_DESC(nowayout, ...@@ -131,6 +115,8 @@ MODULE_PARM_DESC(nowayout,
static void ibwdt_ping(void) static void ibwdt_ping(void)
{ {
int wd_margin = 15 - ((timeout + 1) / 2);
spin_lock(&ibwdt_lock); spin_lock(&ibwdt_lock);
/* Write a watchdog value */ /* Write a watchdog value */
...@@ -148,15 +134,10 @@ static void ibwdt_disable(void) ...@@ -148,15 +134,10 @@ static void ibwdt_disable(void)
static int ibwdt_set_heartbeat(int t) static int ibwdt_set_heartbeat(int t)
{ {
int i; if (t < 0 || t > 30)
if ((t < 0) || (t > 30))
return -EINVAL; return -EINVAL;
for (i = 0x0F; i > -1; i--) timeout = t;
if (wd_times[i] >= t)
break;
wd_margin = i;
return 0; return 0;
} }
...@@ -240,7 +221,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -240,7 +221,7 @@ static long ibwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* Fall */ /* Fall */
case WDIOC_GETTIMEOUT: case WDIOC_GETTIMEOUT:
return put_user(wd_times[wd_margin], p); return put_user(timeout, p);
default: default:
return -ENOTTY; return -ENOTTY;
...@@ -317,6 +298,14 @@ static int __devinit ibwdt_probe(struct platform_device *dev) ...@@ -317,6 +298,14 @@ static int __devinit ibwdt_probe(struct platform_device *dev)
goto out_nostartreg; goto out_nostartreg;
} }
/* Check that the heartbeat value is within it's range ;
* if not reset to the default */
if (ibwdt_set_heartbeat(timeout)) {
ibwdt_set_heartbeat(WATCHDOG_TIMEOUT);
printk(KERN_INFO PFX
"timeout value must be 0<=x<=30, using %d\n", timeout);
}
res = misc_register(&ibwdt_miscdev); res = misc_register(&ibwdt_miscdev);
if (res) { if (res) {
printk(KERN_ERR PFX "failed to register misc device\n"); printk(KERN_ERR PFX "failed to register misc device\n");
......
This diff is collapsed.
/*
* Watchdog driver for the wm8350
*
* Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#include <linux/mfd/wm8350/core.h>
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static unsigned long wm8350_wdt_users;
static struct miscdevice wm8350_wdt_miscdev;
static int wm8350_wdt_expect_close;
static DEFINE_MUTEX(wdt_mutex);
static struct {
int time; /* Seconds */
u16 val; /* To be set in WM8350_SYSTEM_CONTROL_2 */
} wm8350_wdt_cfgs[] = {
{ 1, 0x02 },
{ 2, 0x04 },
{ 4, 0x05 },
};
static struct wm8350 *get_wm8350(void)
{
return dev_get_drvdata(wm8350_wdt_miscdev.parent);
}
static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value)
{
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
wm8350_reg_unlock(wm8350);
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
reg &= ~WM8350_WDOG_TO_MASK;
reg |= value;
ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
wm8350_reg_lock(wm8350);
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm8350_wdt_start(struct wm8350 *wm8350)
{
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
wm8350_reg_unlock(wm8350);
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
reg &= ~WM8350_WDOG_MODE_MASK;
reg |= 0x20;
ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
wm8350_reg_lock(wm8350);
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm8350_wdt_stop(struct wm8350 *wm8350)
{
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
wm8350_reg_unlock(wm8350);
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
reg &= ~WM8350_WDOG_MODE_MASK;
ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
wm8350_reg_lock(wm8350);
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm8350_wdt_kick(struct wm8350 *wm8350)
{
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm8350_wdt_open(struct inode *inode, struct file *file)
{
struct wm8350 *wm8350 = get_wm8350();
int ret;
if (!wm8350)
return -ENODEV;
if (test_and_set_bit(0, &wm8350_wdt_users))
return -EBUSY;
ret = wm8350_wdt_start(wm8350);
if (ret != 0)
return ret;
return nonseekable_open(inode, file);
}
static int wm8350_wdt_release(struct inode *inode, struct file *file)
{
struct wm8350 *wm8350 = get_wm8350();
if (wm8350_wdt_expect_close)
wm8350_wdt_stop(wm8350);
else {
dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n");
wm8350_wdt_kick(wm8350);
}
clear_bit(0, &wm8350_wdt_users);
return 0;
}
static ssize_t wm8350_wdt_write(struct file *file,
const char __user *data, size_t count,
loff_t *ppos)
{
struct wm8350 *wm8350 = get_wm8350();
size_t i;
if (count) {
wm8350_wdt_kick(wm8350);
if (!nowayout) {
/* In case it was set long ago */
wm8350_wdt_expect_close = 0;
/* scan to see whether or not we got the magic
character */
for (i = 0; i != count; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
wm8350_wdt_expect_close = 42;
}
}
}
return count;
}
static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM8350 Watchdog",
};
static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct wm8350 *wm8350 = get_wm8350();
int ret = -ENOTTY, time, i;
void __user *argp = (void __user *)arg;
int __user *p = argp;
u16 reg;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
ret = put_user(0, p);
break;
case WDIOC_SETOPTIONS:
{
int options;
if (get_user(options, p))
return -EFAULT;
ret = -EINVAL;
/* Setting both simultaneously means at least one must fail */
if (options == WDIOS_DISABLECARD)
ret = wm8350_wdt_start(wm8350);
if (options == WDIOS_ENABLECARD)
ret = wm8350_wdt_stop(wm8350);
break;
}
case WDIOC_KEEPALIVE:
ret = wm8350_wdt_kick(wm8350);
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, p);
if (ret)
break;
if (time == 0) {
if (nowayout)
ret = -EINVAL;
else
wm8350_wdt_stop(wm8350);
break;
}
for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
if (wm8350_wdt_cfgs[i].time == time)
break;
if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
ret = -EINVAL;
else
ret = wm8350_wdt_set_timeout(wm8350,
wm8350_wdt_cfgs[i].val);
break;
case WDIOC_GETTIMEOUT:
reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
reg &= WM8350_WDOG_TO_MASK;
for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
if (wm8350_wdt_cfgs[i].val == reg)
break;
if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) {
dev_warn(wm8350->dev,
"Unknown watchdog configuration: %x\n", reg);
ret = -EINVAL;
} else
ret = put_user(wm8350_wdt_cfgs[i].time, p);
}
return ret;
}
static const struct file_operations wm8350_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wm8350_wdt_write,
.unlocked_ioctl = wm8350_wdt_ioctl,
.open = wm8350_wdt_open,
.release = wm8350_wdt_release,
};
static struct miscdevice wm8350_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &wm8350_wdt_fops,
};
static int wm8350_wdt_probe(struct platform_device *pdev)
{
struct wm8350 *wm8350 = platform_get_drvdata(pdev);
if (!wm8350) {
dev_err(wm8350->dev, "No driver data supplied\n");
return -ENODEV;
}
/* Default to 4s timeout */
wm8350_wdt_set_timeout(wm8350, 0x05);
wm8350_wdt_miscdev.parent = &pdev->dev;
return misc_register(&wm8350_wdt_miscdev);
}
static int __exit wm8350_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&wm8350_wdt_miscdev);
return 0;
}
static struct platform_driver wm8350_wdt_driver = {
.probe = wm8350_wdt_probe,
.remove = wm8350_wdt_remove,
.driver = {
.name = "wm8350-wdt",
},
};
static int __init wm8350_wdt_init(void)
{
return platform_driver_register(&wm8350_wdt_driver);
}
module_init(wm8350_wdt_init);
static void __exit wm8350_wdt_exit(void)
{
platform_driver_unregister(&wm8350_wdt_driver);
}
module_exit(wm8350_wdt_exit);
MODULE_AUTHOR("Mark Brown");
MODULE_DESCRIPTION("WM8350 Watchdog");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm8350-wdt");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment