Commit 06d8eb1b authored by Linus Torvalds's avatar Linus Torvalds

Merge git://www.linux-watchdog.org/linux-watchdog

* git://www.linux-watchdog.org/linux-watchdog:
  watchdog: Convert wm831x driver to watchdog core
  watchdog: s3c2410: convert to use the watchdog framework
  Documentation: watchdog: add guide how to convert drivers to new framework
  watchdog: iTCO_wdt.c - problems with newer hardware due to SMI clearing
  watchdog: Add WDIOC_GETTIMELEFT ioctl support to w83627 watchdog driver
  watchdog: irq: Remove IRQF_DISABLED
  watchdog: Octeon: Mark octeon_wdt interrupt as IRQF_NO_THREAD
  watchdog: sc520_wdt: Remove unnecessary cast.
parents cd3f07d1 00411ee9
Converting old watchdog drivers to the watchdog framework
by Wolfram Sang <w.sang@pengutronix.de>
=========================================================
Before the watchdog framework came into the kernel, every driver had to
implement the API on its own. Now, as the framework factored out the common
components, those drivers can be lightened making it a user of the framework.
This document shall guide you for this task. The necessary steps are described
as well as things to look out for.
Remove the file_operations struct
---------------------------------
Old drivers define their own file_operations for actions like open(), write(),
etc... These are now handled by the framework and just call the driver when
needed. So, in general, the 'file_operations' struct and assorted functions can
go. Only very few driver-specific details have to be moved to other functions.
Here is a overview of the functions and probably needed actions:
- open: Everything dealing with resource management (file-open checks, magic
close preparations) can simply go. Device specific stuff needs to go to the
driver specific start-function. Note that for some drivers, the start-function
also serves as the ping-function. If that is the case and you need start/stop
to be balanced (clocks!), you are better off refactoring a separate start-function.
- close: Same hints as for open apply.
- write: Can simply go, all defined behaviour is taken care of by the framework,
i.e. ping on write and magic char ('V') handling.
- ioctl: While the driver is allowed to have extensions to the IOCTL interface,
the most common ones are handled by the framework, supported by some assistance
from the driver:
WDIOC_GETSUPPORT:
Returns the mandatory watchdog_info struct from the driver
WDIOC_GETSTATUS:
Needs the status-callback defined, otherwise returns 0
WDIOC_GETBOOTSTATUS:
Needs the bootstatus member properly set. Make sure it is 0 if you
don't have further support!
WDIOC_SETOPTIONS:
No preparations needed
WDIOC_KEEPALIVE:
If wanted, options in watchdog_info need to have WDIOF_KEEPALIVEPING
set
WDIOC_SETTIMEOUT:
Options in watchdog_info need to have WDIOF_SETTIMEOUT set
and a set_timeout-callback has to be defined. The core will also
do limit-checking, if min_timeout and max_timeout in the watchdog
device are set. All is optional.
WDIOC_GETTIMEOUT:
No preparations needed
Other IOCTLs can be served using the ioctl-callback. Note that this is mainly
intended for porting old drivers; new drivers should not invent private IOCTLs.
Private IOCTLs are processed first. When the callback returns with
-ENOIOCTLCMD, the IOCTLs of the framework will be tried, too. Any other error
is directly given to the user.
Example conversion:
-static const struct file_operations s3c2410wdt_fops = {
- .owner = THIS_MODULE,
- .llseek = no_llseek,
- .write = s3c2410wdt_write,
- .unlocked_ioctl = s3c2410wdt_ioctl,
- .open = s3c2410wdt_open,
- .release = s3c2410wdt_release,
-};
Check the functions for device-specific stuff and keep it for later
refactoring. The rest can go.
Remove the miscdevice
---------------------
Since the file_operations are gone now, you can also remove the 'struct
miscdevice'. The framework will create it on watchdog_dev_register() called by
watchdog_register_device().
-static struct miscdevice s3c2410wdt_miscdev = {
- .minor = WATCHDOG_MINOR,
- .name = "watchdog",
- .fops = &s3c2410wdt_fops,
-};
Remove obsolete includes and defines
------------------------------------
Because of the simplifications, a few defines are probably unused now. Remove
them. Includes can be removed, too. For example:
- #include <linux/fs.h>
- #include <linux/miscdevice.h> (if MODULE_ALIAS_MISCDEV is not used)
- #include <linux/uaccess.h> (if no custom IOCTLs are used)
Add the watchdog operations
---------------------------
All possible callbacks are defined in 'struct watchdog_ops'. You can find it
explained in 'watchdog-kernel-api.txt' in this directory. start(), stop() and
owner must be set, the rest are optional. You will easily find corresponding
functions in the old driver. Note that you will now get a pointer to the
watchdog_device as a parameter to these functions, so you probably have to
change the function header. Other changes are most likely not needed, because
here simply happens the direct hardware access. If you have device-specific
code left from the above steps, it should be refactored into these callbacks.
Here is a simple example:
+static struct watchdog_ops s3c2410wdt_ops = {
+ .owner = THIS_MODULE,
+ .start = s3c2410wdt_start,
+ .stop = s3c2410wdt_stop,
+ .ping = s3c2410wdt_keepalive,
+ .set_timeout = s3c2410wdt_set_heartbeat,
+};
A typical function-header change looks like:
-static void s3c2410wdt_keepalive(void)
+static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
...
+
+ return 0;
}
...
- s3c2410wdt_keepalive();
+ s3c2410wdt_keepalive(&s3c2410_wdd);
Add the watchdog device
-----------------------
Now we need to create a 'struct watchdog_device' and populate it with the
necessary information for the framework. The struct is also explained in detail
in 'watchdog-kernel-api.txt' in this directory. We pass it the mandatory
watchdog_info struct and the newly created watchdog_ops. Often, old drivers
have their own record-keeping for things like bootstatus and timeout using
static variables. Those have to be converted to use the members in
watchdog_device. Note that the timeout values are unsigned int. Some drivers
use signed int, so this has to be converted, too.
Here is a simple example for a watchdog device:
+static struct watchdog_device s3c2410_wdd = {
+ .info = &s3c2410_wdt_ident,
+ .ops = &s3c2410wdt_ops,
+};
Register the watchdog device
----------------------------
Replace misc_register(&miscdev) with watchdog_register_device(&watchdog_dev).
Make sure the return value gets checked and the error message, if present,
still fits. Also convert the unregister case.
- ret = misc_register(&s3c2410wdt_miscdev);
+ ret = watchdog_register_device(&s3c2410_wdd);
...
- misc_deregister(&s3c2410wdt_miscdev);
+ watchdog_unregister_device(&s3c2410_wdd);
Update the Kconfig-entry
------------------------
The entry for the driver now needs to select WATCHDOG_CORE:
+ select WATCHDOG_CORE
Create a patch and send it to upstream
--------------------------------------
Make sure you understood Documentation/SubmittingPatches and send your patch to
linux-watchdog@vger.kernel.org. We are looking forward to it :)
......@@ -66,6 +66,7 @@ config SOFT_WATCHDOG
config WM831X_WATCHDOG
tristate "WM831x watchdog"
depends on MFD_WM831X
select WATCHDOG_CORE
help
Support for the watchdog in the WM831x AudioPlus PMICs. When
the watchdog triggers the system will be reset.
......@@ -170,6 +171,7 @@ config HAVE_S3C2410_WATCHDOG
config S3C2410_WATCHDOG
tristate "S3C2410 Watchdog"
depends on ARCH_S3C2410 || HAVE_S3C2410_WATCHDOG
select WATCHDOG_CORE
help
Watchdog timer block in the Samsung SoCs. This will reboot
the system when the timer expires with the watchdog enabled.
......
......@@ -429,7 +429,7 @@ static int __init coh901327_probe(struct platform_device *pdev)
writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR);
irq = platform_get_irq(pdev, 0);
if (request_irq(irq, coh901327_interrupt, IRQF_DISABLED,
if (request_irq(irq, coh901327_interrupt, 0,
DRV_NAME " Bark", pdev)) {
ret = -EIO;
goto out_no_irq;
......
......@@ -427,7 +427,7 @@ static int __init eurwdt_init(void)
{
int ret;
ret = request_irq(irq, eurwdt_interrupt, IRQF_DISABLED, "eurwdt", NULL);
ret = request_irq(irq, eurwdt_interrupt, 0, "eurwdt", NULL);
if (ret) {
printk(KERN_ERR "eurwdt: IRQ %d is not free.\n", irq);
goto out;
......
/*
* intel TCO Watchdog Driver
*
* (c) Copyright 2006-2010 Wim Van Sebroeck <wim@iguana.be>.
* (c) Copyright 2006-2011 Wim Van Sebroeck <wim@iguana.be>.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -44,7 +44,7 @@
/* Module and version information */
#define DRV_NAME "iTCO_wdt"
#define DRV_VERSION "1.06"
#define DRV_VERSION "1.07"
#define PFX DRV_NAME ": "
/* Includes */
......@@ -384,6 +384,11 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static int turn_SMI_watchdog_clear_off = 0;
module_param(turn_SMI_watchdog_clear_off, int, 0);
MODULE_PARM_DESC(turn_SMI_watchdog_clear_off,
"Turn off SMI clearing watchdog (default=0)");
/*
* Some TCO specific functions
*/
......@@ -808,10 +813,12 @@ static int __devinit iTCO_wdt_init(struct pci_dev *pdev,
ret = -EIO;
goto out_unmap;
}
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
val32 = inl(SMI_EN);
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
outl(val32, SMI_EN);
if (turn_SMI_watchdog_clear_off) {
/* Bit 13: TCO_EN -> 0 = Disables TCO logic generating an SMI# */
val32 = inl(SMI_EN);
val32 &= 0xffffdfff; /* Turn off SMI clearing watchdog */
outl(val32, SMI_EN);
}
/* The TCO I/O registers reside in a 32-byte range pointed to
by the TCOBASE value */
......
......@@ -367,8 +367,7 @@ static int __devinit mpcore_wdt_probe(struct platform_device *dev)
goto err_misc;
}
ret = request_irq(wdt->irq, mpcore_wdt_fire, IRQF_DISABLED,
"mpcore_wdt", wdt);
ret = request_irq(wdt->irq, mpcore_wdt_fire, 0, "mpcore_wdt", wdt);
if (ret) {
dev_printk(KERN_ERR, wdt->dev,
"cannot register IRQ%d for watchdog\n", wdt->irq);
......
......@@ -402,7 +402,7 @@ static void octeon_wdt_setup_interrupt(int cpu)
irq = OCTEON_IRQ_WDOG0 + core;
if (request_irq(irq, octeon_wdt_poke_irq,
IRQF_DISABLED, "octeon_wdt", octeon_wdt_poke_irq))
IRQF_NO_THREAD, "octeon_wdt", octeon_wdt_poke_irq))
panic("octeon_wdt: Couldn't obtain irq %d", irq);
cpumask_set_cpu(cpu, &irq_enabled_cpus);
......
......@@ -27,9 +27,8 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/timer.h>
#include <linux/miscdevice.h>
#include <linux/miscdevice.h> /* for MODULE_ALIAS_MISCDEV */
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
......@@ -38,6 +37,7 @@
#include <linux/io.h>
#include <linux/cpufreq.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <mach/map.h>
......@@ -74,14 +74,12 @@ MODULE_PARM_DESC(soft_noboot, "Watchdog action, set to 1 to ignore reboots, "
"0 to reboot (default 0)");
MODULE_PARM_DESC(debug, "Watchdog debug, set to >1 for debug (default 0)");
static unsigned long open_lock;
static struct device *wdt_dev; /* platform device attached to */
static struct resource *wdt_mem;
static struct resource *wdt_irq;
static struct clk *wdt_clock;
static void __iomem *wdt_base;
static unsigned int wdt_count;
static char expect_close;
static DEFINE_SPINLOCK(wdt_lock);
/* watchdog control routines */
......@@ -93,11 +91,13 @@ static DEFINE_SPINLOCK(wdt_lock);
/* functions */
static void s3c2410wdt_keepalive(void)
static int s3c2410wdt_keepalive(struct watchdog_device *wdd)
{
spin_lock(&wdt_lock);
writel(wdt_count, wdt_base + S3C2410_WTCNT);
spin_unlock(&wdt_lock);
return 0;
}
static void __s3c2410wdt_stop(void)
......@@ -109,14 +109,16 @@ static void __s3c2410wdt_stop(void)
writel(wtcon, wdt_base + S3C2410_WTCON);
}
static void s3c2410wdt_stop(void)
static int s3c2410wdt_stop(struct watchdog_device *wdd)
{
spin_lock(&wdt_lock);
__s3c2410wdt_stop();
spin_unlock(&wdt_lock);
return 0;
}
static void s3c2410wdt_start(void)
static int s3c2410wdt_start(struct watchdog_device *wdd)
{
unsigned long wtcon;
......@@ -142,6 +144,8 @@ static void s3c2410wdt_start(void)
writel(wdt_count, wdt_base + S3C2410_WTCNT);
writel(wtcon, wdt_base + S3C2410_WTCON);
spin_unlock(&wdt_lock);
return 0;
}
static inline int s3c2410wdt_is_running(void)
......@@ -149,7 +153,7 @@ static inline int s3c2410wdt_is_running(void)
return readl(wdt_base + S3C2410_WTCON) & S3C2410_WTCON_ENABLE;
}
static int s3c2410wdt_set_heartbeat(int timeout)
static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, unsigned timeout)
{
unsigned long freq = clk_get_rate(wdt_clock);
unsigned int count;
......@@ -182,8 +186,6 @@ static int s3c2410wdt_set_heartbeat(int timeout)
}
}
tmr_margin = timeout;
DBG("%s: timeout=%d, divisor=%d, count=%d (%08x)\n",
__func__, timeout, divisor, count, count/divisor);
......@@ -201,70 +203,6 @@ static int s3c2410wdt_set_heartbeat(int timeout)
return 0;
}
/*
* /dev/watchdog handling
*/
static int s3c2410wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &open_lock))
return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
expect_close = 0;
/* start the timer */
s3c2410wdt_start();
return nonseekable_open(inode, file);
}
static int s3c2410wdt_release(struct inode *inode, struct file *file)
{
/*
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/
if (expect_close == 42)
s3c2410wdt_stop();
else {
dev_err(wdt_dev, "Unexpected close, not stopping watchdog\n");
s3c2410wdt_keepalive();
}
expect_close = 0;
clear_bit(0, &open_lock);
return 0;
}
static ssize_t s3c2410wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
/*
* Refresh the timer.
*/
if (len) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_close = 42;
}
}
s3c2410wdt_keepalive();
}
return len;
}
#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
static const struct watchdog_info s3c2410_wdt_ident = {
......@@ -273,53 +211,17 @@ static const struct watchdog_info s3c2410_wdt_ident = {
.identity = "S3C2410 Watchdog",
};
static long s3c2410wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_margin;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &s3c2410_wdt_ident,
sizeof(s3c2410_wdt_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
s3c2410wdt_keepalive();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, p))
return -EFAULT;
if (s3c2410wdt_set_heartbeat(new_margin))
return -EINVAL;
s3c2410wdt_keepalive();
return put_user(tmr_margin, p);
case WDIOC_GETTIMEOUT:
return put_user(tmr_margin, p);
default:
return -ENOTTY;
}
}
/* kernel interface */
static const struct file_operations s3c2410wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = s3c2410wdt_write,
.unlocked_ioctl = s3c2410wdt_ioctl,
.open = s3c2410wdt_open,
.release = s3c2410wdt_release,
static struct watchdog_ops s3c2410wdt_ops = {
.owner = THIS_MODULE,
.start = s3c2410wdt_start,
.stop = s3c2410wdt_stop,
.ping = s3c2410wdt_keepalive,
.set_timeout = s3c2410wdt_set_heartbeat,
};
static struct miscdevice s3c2410wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &s3c2410wdt_fops,
static struct watchdog_device s3c2410_wdd = {
.info = &s3c2410_wdt_ident,
.ops = &s3c2410wdt_ops,
};
/* interrupt handler code */
......@@ -328,7 +230,7 @@ static irqreturn_t s3c2410wdt_irq(int irqno, void *param)
{
dev_info(wdt_dev, "watchdog timer expired (irq)\n");
s3c2410wdt_keepalive();
s3c2410wdt_keepalive(&s3c2410_wdd);
return IRQ_HANDLED;
}
......@@ -349,14 +251,14 @@ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
* the watchdog is running.
*/
s3c2410wdt_keepalive();
s3c2410wdt_keepalive(&s3c2410_wdd);
} else if (val == CPUFREQ_POSTCHANGE) {
s3c2410wdt_stop();
s3c2410wdt_stop(&s3c2410_wdd);
ret = s3c2410wdt_set_heartbeat(tmr_margin);
ret = s3c2410wdt_set_heartbeat(&s3c2410_wdd, s3c2410_wdd.timeout);
if (ret >= 0)
s3c2410wdt_start();
s3c2410wdt_start(&s3c2410_wdd);
else
goto err;
}
......@@ -365,7 +267,8 @@ static int s3c2410wdt_cpufreq_transition(struct notifier_block *nb,
return 0;
err:
dev_err(wdt_dev, "cannot set new value for timeout %d\n", tmr_margin);
dev_err(wdt_dev, "cannot set new value for timeout %d\n",
s3c2410_wdd.timeout);
return ret;
}
......@@ -396,10 +299,6 @@ static inline void s3c2410wdt_cpufreq_deregister(void)
}
#endif
/* device interface */
static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
{
struct device *dev;
......@@ -466,8 +365,8 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
/* see if we can actually set the requested timer margin, and if
* not, try the default value */
if (s3c2410wdt_set_heartbeat(tmr_margin)) {
started = s3c2410wdt_set_heartbeat(
if (s3c2410wdt_set_heartbeat(&s3c2410_wdd, tmr_margin)) {
started = s3c2410wdt_set_heartbeat(&s3c2410_wdd,
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
if (started == 0)
......@@ -479,22 +378,21 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
"cannot start\n");
}
ret = misc_register(&s3c2410wdt_miscdev);
ret = watchdog_register_device(&s3c2410_wdd);
if (ret) {
dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
dev_err(dev, "cannot register watchdog (%d)\n", ret);
goto err_cpufreq;
}
if (tmr_atboot && started == 0) {
dev_info(dev, "starting watchdog timer\n");
s3c2410wdt_start();
s3c2410wdt_start(&s3c2410_wdd);
} else if (!tmr_atboot) {
/* if we're not enabling the watchdog, then ensure it is
* disabled if it has been left running from the bootloader
* or other source */
s3c2410wdt_stop();
s3c2410wdt_stop(&s3c2410_wdd);
}
/* print out a statement of readiness */
......@@ -530,7 +428,7 @@ static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
static int __devexit s3c2410wdt_remove(struct platform_device *dev)
{
misc_deregister(&s3c2410wdt_miscdev);
watchdog_unregister_device(&s3c2410_wdd);
s3c2410wdt_cpufreq_deregister();
......@@ -550,7 +448,7 @@ static int __devexit s3c2410wdt_remove(struct platform_device *dev)
static void s3c2410wdt_shutdown(struct platform_device *dev)
{
s3c2410wdt_stop();
s3c2410wdt_stop(&s3c2410_wdd);
}
#ifdef CONFIG_PM
......@@ -565,7 +463,7 @@ static int s3c2410wdt_suspend(struct platform_device *dev, pm_message_t state)
wtdat_save = readl(wdt_base + S3C2410_WTDAT);
/* Note that WTCNT doesn't need to be saved. */
s3c2410wdt_stop();
s3c2410wdt_stop(&s3c2410_wdd);
return 0;
}
......
......@@ -300,7 +300,7 @@ static int __init sbwdog_init(void)
* get the resources
*/
ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED,
ident.identity, (void *)user_dog);
if (ret) {
printk(KERN_ERR "%s: failed to request irq 1 - %d\n",
......@@ -350,7 +350,7 @@ void platform_wd_setup(void)
{
int ret;
ret = request_irq(1, sbwdog_interrupt, IRQF_DISABLED | IRQF_SHARED,
ret = request_irq(1, sbwdog_interrupt, IRQF_SHARED,
"Kernel Watchdog", IOADDR(A_SCD_WDOG_CFG_0));
if (ret) {
printk(KERN_CRIT
......
......@@ -398,7 +398,7 @@ static int __init sc520_wdt_init(void)
WATCHDOG_TIMEOUT);
}
wdtmrctl = ioremap((unsigned long)(MMCR_BASE + OFFS_WDTMRCTL), 2);
wdtmrctl = ioremap(MMCR_BASE + OFFS_WDTMRCTL, 2);
if (!wdtmrctl) {
printk(KERN_ERR PFX "Unable to remap memory\n");
rc = -ENOMEM;
......
......@@ -142,7 +142,7 @@ static void w83627hf_init(void)
w83627hf_unselect_wd_register();
}
static void wdt_ctrl(int timeout)
static void wdt_set_time(int timeout)
{
spin_lock(&io_lock);
......@@ -158,13 +158,13 @@ static void wdt_ctrl(int timeout)
static int wdt_ping(void)
{
wdt_ctrl(timeout);
wdt_set_time(timeout);
return 0;
}
static int wdt_disable(void)
{
wdt_ctrl(0);
wdt_set_time(0);
return 0;
}
......@@ -176,6 +176,24 @@ static int wdt_set_heartbeat(int t)
return 0;
}
static int wdt_get_time(void)
{
int timeleft;
spin_lock(&io_lock);
w83627hf_select_wd_register();
outb_p(0xF6, WDT_EFER); /* Select CRF6 */
timeleft = inb_p(WDT_EFDR); /* Read Timeout counter to CRF6 */
w83627hf_unselect_wd_register();
spin_unlock(&io_lock);
return timeleft;
}
static ssize_t wdt_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
......@@ -202,7 +220,7 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_timeout;
int timeval;
static const struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
......@@ -238,14 +256,17 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
wdt_ping();
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, p))
if (get_user(timeval, p))
return -EFAULT;
if (wdt_set_heartbeat(new_timeout))
if (wdt_set_heartbeat(timeval))
return -EINVAL;
wdt_ping();
/* Fall */
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
case WDIOC_GETTIMELEFT:
timeval = wdt_get_time();
return put_user(timeval, p);
default:
return -ENOTTY;
}
......
......@@ -612,7 +612,7 @@ static int __init wdt_init(void)
goto out;
}
ret = request_irq(irq, wdt_interrupt, IRQF_DISABLED, "wdt501p", NULL);
ret = request_irq(irq, wdt_interrupt, 0, "wdt501p", NULL);
if (ret) {
printk(KERN_ERR "wdt: IRQ %d is not free.\n", irq);
goto outreg;
......
......@@ -643,7 +643,7 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
irq = dev->irq;
io = pci_resource_start(dev, 2);
if (request_irq(irq, wdtpci_interrupt, IRQF_DISABLED | IRQF_SHARED,
if (request_irq(irq, wdtpci_interrupt, IRQF_SHARED,
"wdt_pci", &wdtpci_miscdev)) {
printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
goto out_reg;
......
......@@ -12,8 +12,7 @@
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
......@@ -29,19 +28,19 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static unsigned long wm831x_wdt_users;
static struct miscdevice wm831x_wdt_miscdev;
static int wm831x_wdt_expect_close;
static DEFINE_MUTEX(wdt_mutex);
static struct wm831x *wm831x;
static unsigned int update_gpio;
static unsigned int update_state;
struct wm831x_wdt_drvdata {
struct watchdog_device wdt;
struct wm831x *wm831x;
struct mutex lock;
int update_gpio;
int update_state;
};
/* We can't use the sub-second values here but they're included
* for completeness. */
static struct {
int time; /* Seconds */
u16 val; /* WDOG_TO value */
unsigned int time; /* Seconds */
u16 val; /* WDOG_TO value */
} wm831x_wdt_cfgs[] = {
{ 1, 2 },
{ 2, 3 },
......@@ -52,32 +51,13 @@ static struct {
{ 33, 7 }, /* Actually 32.768s so include both, others round down */
};
static int wm831x_wdt_set_timeout(struct wm831x *wm831x, u16 value)
{
int ret;
mutex_lock(&wdt_mutex);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_TO_MASK, value);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
ret);
}
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm831x_wdt_start(struct wm831x *wm831x)
static int wm831x_wdt_start(struct watchdog_device *wdt_dev)
{
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret;
mutex_lock(&wdt_mutex);
mutex_lock(&driver_data->lock);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
......@@ -89,16 +69,18 @@ static int wm831x_wdt_start(struct wm831x *wm831x)
ret);
}
mutex_unlock(&wdt_mutex);
mutex_unlock(&driver_data->lock);
return ret;
}
static int wm831x_wdt_stop(struct wm831x *wm831x)
static int wm831x_wdt_stop(struct watchdog_device *wdt_dev)
{
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret;
mutex_lock(&wdt_mutex);
mutex_lock(&driver_data->lock);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
......@@ -110,26 +92,28 @@ static int wm831x_wdt_stop(struct wm831x *wm831x)
ret);
}
mutex_unlock(&wdt_mutex);
mutex_unlock(&driver_data->lock);
return ret;
}
static int wm831x_wdt_kick(struct wm831x *wm831x)
static int wm831x_wdt_ping(struct watchdog_device *wdt_dev)
{
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
mutex_lock(&driver_data->lock);
if (update_gpio) {
gpio_set_value_cansleep(update_gpio, update_state);
update_state = !update_state;
if (driver_data->update_gpio) {
gpio_set_value_cansleep(driver_data->update_gpio,
driver_data->update_state);
driver_data->update_state = !driver_data->update_state;
ret = 0;
goto out;
}
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (!(reg & WM831X_WDOG_RST_SRC)) {
......@@ -150,182 +134,59 @@ static int wm831x_wdt_kick(struct wm831x *wm831x)
}
out:
mutex_unlock(&wdt_mutex);
mutex_unlock(&driver_data->lock);
return ret;
}
static int wm831x_wdt_open(struct inode *inode, struct file *file)
static int wm831x_wdt_set_timeout(struct watchdog_device *wdt_dev,
unsigned int timeout)
{
int ret;
if (!wm831x)
return -ENODEV;
if (test_and_set_bit(0, &wm831x_wdt_users))
return -EBUSY;
struct wm831x_wdt_drvdata *driver_data = watchdog_get_drvdata(wdt_dev);
struct wm831x *wm831x = driver_data->wm831x;
int ret, i;
ret = wm831x_wdt_start(wm831x);
if (ret != 0)
return ret;
return nonseekable_open(inode, file);
}
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].time == timeout)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
ret = -EINVAL;
static int wm831x_wdt_release(struct inode *inode, struct file *file)
{
if (wm831x_wdt_expect_close)
wm831x_wdt_stop(wm831x);
else {
dev_warn(wm831x->dev, "Watchdog device closed uncleanly\n");
wm831x_wdt_kick(wm831x);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_TO_MASK,
wm831x_wdt_cfgs[i].val);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
ret);
}
clear_bit(0, &wm831x_wdt_users);
return 0;
}
static ssize_t wm831x_wdt_write(struct file *file,
const char __user *data, size_t count,
loff_t *ppos)
{
size_t i;
if (count) {
wm831x_wdt_kick(wm831x);
if (!nowayout) {
/* In case it was set long ago */
wm831x_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')
wm831x_wdt_expect_close = 42;
}
}
}
return count;
return ret;
}
static const struct watchdog_info ident = {
static const struct watchdog_info wm831x_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
.identity = "WM831x Watchdog",
};
static long wm831x_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
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 = wm831x_wdt_start(wm831x);
if (options == WDIOS_ENABLECARD)
ret = wm831x_wdt_stop(wm831x);
break;
}
case WDIOC_KEEPALIVE:
ret = wm831x_wdt_kick(wm831x);
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, p);
if (ret)
break;
if (time == 0) {
if (nowayout)
ret = -EINVAL;
else
wm831x_wdt_stop(wm831x);
break;
}
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].time == time)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
ret = -EINVAL;
else
ret = wm831x_wdt_set_timeout(wm831x,
wm831x_wdt_cfgs[i].val);
break;
case WDIOC_GETTIMEOUT:
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
reg &= WM831X_WDOG_TO_MASK;
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].val == reg)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs)) {
dev_warn(wm831x->dev,
"Unknown watchdog configuration: %x\n", reg);
ret = -EINVAL;
} else
ret = put_user(wm831x_wdt_cfgs[i].time, p);
}
return ret;
}
static const struct file_operations wm831x_wdt_fops = {
static const struct watchdog_ops wm831x_wdt_ops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = wm831x_wdt_write,
.unlocked_ioctl = wm831x_wdt_ioctl,
.open = wm831x_wdt_open,
.release = wm831x_wdt_release,
};
static struct miscdevice wm831x_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &wm831x_wdt_fops,
.start = wm831x_wdt_start,
.stop = wm831x_wdt_stop,
.ping = wm831x_wdt_ping,
.set_timeout = wm831x_wdt_set_timeout,
};
static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_pdata *chip_pdata;
struct wm831x_watchdog_pdata *pdata;
int reg, ret;
if (wm831x) {
dev_err(&pdev->dev, "wm831x watchdog already registered\n");
return -EBUSY;
}
wm831x = dev_get_drvdata(pdev->dev.parent);
struct wm831x_wdt_drvdata *driver_data;
struct watchdog_device *wm831x_wdt;
int reg, ret, i;
ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (ret < 0) {
......@@ -338,6 +199,36 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
if (reg & WM831X_WDOG_DEBUG)
dev_warn(wm831x->dev, "Watchdog is paused\n");
driver_data = kzalloc(sizeof(*driver_data), GFP_KERNEL);
if (!driver_data) {
dev_err(wm831x->dev, "Unable to alloacate watchdog device\n");
ret = -ENOMEM;
goto err;
}
mutex_init(&driver_data->lock);
driver_data->wm831x = wm831x;
wm831x_wdt = &driver_data->wdt;
wm831x_wdt->info = &wm831x_wdt_info;
wm831x_wdt->ops = &wm831x_wdt_ops;
watchdog_set_drvdata(wm831x_wdt, driver_data);
if (nowayout)
wm831x_wdt->status |= WDOG_NO_WAY_OUT;
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
reg &= WM831X_WDOG_TO_MASK;
for (i = 0; i < ARRAY_SIZE(wm831x_wdt_cfgs); i++)
if (wm831x_wdt_cfgs[i].val == reg)
break;
if (i == ARRAY_SIZE(wm831x_wdt_cfgs))
dev_warn(wm831x->dev,
"Unknown watchdog timeout: %x\n", reg);
else
wm831x_wdt->timeout = wm831x_wdt_cfgs[i].time;
/* Apply any configuration */
if (pdev->dev.parent->platform_data) {
chip_pdata = pdev->dev.parent->platform_data;
......@@ -361,7 +252,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n",
ret);
goto err;
goto err_alloc;
}
ret = gpio_direction_output(pdata->update_gpio, 0);
......@@ -372,7 +263,7 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
goto err_gpio;
}
update_gpio = pdata->update_gpio;
driver_data->update_gpio = pdata->update_gpio;
/* Make sure the watchdog takes hardware updates */
reg |= WM831X_WDOG_RST_SRC;
......@@ -389,33 +280,34 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
}
}
wm831x_wdt_miscdev.parent = &pdev->dev;
ret = misc_register(&wm831x_wdt_miscdev);
ret = watchdog_register_device(&driver_data->wdt);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret);
dev_err(wm831x->dev, "watchdog_register_device() failed: %d\n",
ret);
goto err_gpio;
}
dev_set_drvdata(&pdev->dev, driver_data);
return 0;
err_gpio:
if (update_gpio) {
gpio_free(update_gpio);
update_gpio = 0;
}
if (driver_data->update_gpio)
gpio_free(driver_data->update_gpio);
err_alloc:
kfree(driver_data);
err:
return ret;
}
static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
{
if (update_gpio) {
gpio_free(update_gpio);
update_gpio = 0;
}
struct wm831x_wdt_drvdata *driver_data = dev_get_drvdata(&pdev->dev);
watchdog_unregister_device(&driver_data->wdt);
misc_deregister(&wm831x_wdt_miscdev);
if (driver_data->update_gpio)
gpio_free(driver_data->update_gpio);
return 0;
}
......
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