Commit 63811bb4 authored by Stephen Rothwell's avatar Stephen Rothwell

Merge commit 'watchdog/master'

parents 466b4db8 376f3819
...@@ -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 WM831X_WATCHDOG
tristate "WM831x watchdog"
depends on MFD_WM831X
help
Support for the watchdog in the WM831x AudioPlus PMICs. When
the watchdog triggers the system will be reset.
config WM8350_WATCHDOG config WM8350_WATCHDOG
tristate "WM8350 watchdog" tristate "WM8350 watchdog"
depends on MFD_WM8350 depends on MFD_WM8350
...@@ -266,6 +273,15 @@ config STMP3XXX_WATCHDOG ...@@ -266,6 +273,15 @@ config STMP3XXX_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 stmp3xxx_wdt. module will be called stmp3xxx_wdt.
config NUC900_WATCHDOG
tristate "Nuvoton NUC900 watchdog"
depends on ARCH_W90X900
help
Say Y here if to include support for the watchdog timer
for the Nuvoton NUC900 series SoCs.
To compile this driver as a module, choose M here: the
module will be called nuc900_wdt.
# AVR32 Architecture # AVR32 Architecture
config AT32AP700X_WDT config AT32AP700X_WDT
...@@ -369,6 +385,28 @@ config SC520_WDT ...@@ -369,6 +385,28 @@ config SC520_WDT
You can compile this driver directly into the kernel, or use You can compile this driver directly into the kernel, or use
it as a module. The module will be called sc520_wdt. it as a module. The module will be called sc520_wdt.
config SBC_FITPC2_WATCHDOG
tristate "Compulab SBC-FITPC2 watchdog"
depends on X86
---help---
This is the driver for the built-in watchdog timer on the fit-PC2
Single-board computer made by Compulab.
It`s possible to enable watchdog timer either from BIOS (F2) or from booted Linux.
When "Watchdog Timer Value" enabled one can set 31-255 s operational range.
Entering BIOS setup temporary disables watchdog operation regardless to current state,
so system will not be restarted while user in BIOS setup.
Once watchdog was enabled the system will be restarted every
"Watchdog Timer Value" period, so to prevent it user can restart or
disable the watchdog.
To compile this driver as a module, choose M here: the
module will be called sbc_fitpc2_wdt.
Most people will say N.
config EUROTECH_WDT config EUROTECH_WDT
tristate "Eurotech CPU-1220/1410 Watchdog Timer" tristate "Eurotech CPU-1220/1410 Watchdog Timer"
depends on X86 depends on X86
......
...@@ -44,6 +44,7 @@ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o ...@@ -44,6 +44,7 @@ obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
# AVR32 Architecture # AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
...@@ -64,6 +65,7 @@ obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o ...@@ -64,6 +65,7 @@ obj-$(CONFIG_ALIM1535_WDT) += alim1535_wdt.o
obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
obj-$(CONFIG_GEODE_WDT) += geodewdt.o obj-$(CONFIG_GEODE_WDT) += geodewdt.o
obj-$(CONFIG_SC520_WDT) += sc520_wdt.o obj-$(CONFIG_SC520_WDT) += sc520_wdt.o
obj-$(CONFIG_SBC_FITPC2_WATCHDOG) += sbc_fitpc2_wdt.o
obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o obj-$(CONFIG_EUROTECH_WDT) += eurotechwdt.o
obj-$(CONFIG_IB700_WDT) += ib700wdt.o obj-$(CONFIG_IB700_WDT) += ib700wdt.o
obj-$(CONFIG_IBMASR) += ibmasr.o obj-$(CONFIG_IBMASR) += ibmasr.o
...@@ -139,5 +141,6 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o ...@@ -139,5 +141,6 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
# XTENSA Architecture # XTENSA Architecture
# Architecture Independant # Architecture Independant
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
...@@ -28,9 +28,8 @@ ...@@ -28,9 +28,8 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/ioport.h> #include <linux/ioport.h>
#include <linux/io.h> #include <linux/io.h>
...@@ -76,24 +75,10 @@ static unsigned expect_close; ...@@ -76,24 +75,10 @@ static unsigned expect_close;
/* XXX currently fixed, allows max margin ~68.72 secs */ /* XXX currently fixed, allows max margin ~68.72 secs */
#define prescale_value 0xffff #define prescale_value 0xffff
/* Offset of the WDT registers */ /* Resource of the WDT registers */
static unsigned long ar7_regs_wdt; static struct resource *ar7_regs_wdt;
/* Pointer to the remapped WDT IO space */ /* Pointer to the remapped WDT IO space */
static struct ar7_wdt *ar7_wdt; static struct ar7_wdt *ar7_wdt;
static void ar7_wdt_get_regs(void)
{
u16 chip_id = ar7_chip_id();
switch (chip_id) {
case AR7_CHIP_7100:
case AR7_CHIP_7200:
ar7_regs_wdt = AR7_REGS_WDT;
break;
default:
ar7_regs_wdt = UR8_REGS_WDT;
break;
}
}
static void ar7_wdt_kick(u32 value) static void ar7_wdt_kick(u32 value)
{ {
...@@ -202,20 +187,6 @@ static int ar7_wdt_release(struct inode *inode, struct file *file) ...@@ -202,20 +187,6 @@ static int ar7_wdt_release(struct inode *inode, struct file *file)
return 0; return 0;
} }
static int ar7_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
if (code == SYS_HALT || code == SYS_POWER_OFF)
if (!nowayout)
ar7_wdt_disable_wdt();
return NOTIFY_DONE;
}
static struct notifier_block ar7_wdt_notifier = {
.notifier_call = ar7_wdt_notify_sys,
};
static ssize_t ar7_wdt_write(struct file *file, const char *data, static ssize_t ar7_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos) size_t len, loff_t *ppos)
{ {
...@@ -299,56 +270,86 @@ static struct miscdevice ar7_wdt_miscdev = { ...@@ -299,56 +270,86 @@ static struct miscdevice ar7_wdt_miscdev = {
.fops = &ar7_wdt_fops, .fops = &ar7_wdt_fops,
}; };
static int __init ar7_wdt_init(void) static int __devinit ar7_wdt_probe(struct platform_device *pdev)
{ {
int rc; int rc;
spin_lock_init(&wdt_lock); spin_lock_init(&wdt_lock);
ar7_wdt_get_regs(); ar7_regs_wdt =
platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
if (!ar7_regs_wdt) {
printk(KERN_ERR DRVNAME ": could not get registers resource\n");
rc = -ENODEV;
goto out;
}
if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt), if (!request_mem_region(ar7_regs_wdt->start,
LONGNAME)) { resource_size(ar7_regs_wdt), LONGNAME)) {
printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n"); printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
return -EBUSY; rc = -EBUSY;
goto out;
} }
ar7_wdt = (struct ar7_wdt *) ar7_wdt = ioremap(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt)); if (!ar7_wdt) {
printk(KERN_ERR DRVNAME ": could not ioremap registers\n");
rc = -ENXIO;
goto out_mem_region;
}
ar7_wdt_disable_wdt(); ar7_wdt_disable_wdt();
ar7_wdt_prescale(prescale_value); ar7_wdt_prescale(prescale_value);
ar7_wdt_update_margin(margin); ar7_wdt_update_margin(margin);
rc = register_reboot_notifier(&ar7_wdt_notifier);
if (rc) {
printk(KERN_ERR DRVNAME
": unable to register reboot notifier\n");
goto out_alloc;
}
rc = misc_register(&ar7_wdt_miscdev); rc = misc_register(&ar7_wdt_miscdev);
if (rc) { if (rc) {
printk(KERN_ERR DRVNAME ": unable to register misc device\n"); printk(KERN_ERR DRVNAME ": unable to register misc device\n");
goto out_register; goto out_alloc;
} }
goto out; goto out;
out_register:
unregister_reboot_notifier(&ar7_wdt_notifier);
out_alloc: out_alloc:
iounmap(ar7_wdt); iounmap(ar7_wdt);
release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt)); out_mem_region:
release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
out: out:
return rc; return rc;
} }
static void __exit ar7_wdt_cleanup(void) static int __devexit ar7_wdt_remove(struct platform_device *pdev)
{ {
misc_deregister(&ar7_wdt_miscdev); misc_deregister(&ar7_wdt_miscdev);
unregister_reboot_notifier(&ar7_wdt_notifier);
iounmap(ar7_wdt); iounmap(ar7_wdt);
release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt)); release_mem_region(ar7_regs_wdt->start, resource_size(ar7_regs_wdt));
return 0;
}
static void ar7_wdt_shutdown(struct platform_device *pdev)
{
if (!nowayout)
ar7_wdt_disable_wdt();
}
static struct platform_driver ar7_wdt_driver = {
.probe = ar7_wdt_probe,
.remove = __devexit_p(ar7_wdt_remove),
.shutdown = ar7_wdt_shutdown,
.driver = {
.owner = THIS_MODULE,
.name = "ar7_wdt",
},
};
static int __init ar7_wdt_init(void)
{
return platform_driver_register(&ar7_wdt_driver);
}
static void __exit ar7_wdt_cleanup(void)
{
platform_driver_unregister(&ar7_wdt_driver);
} }
module_init(ar7_wdt_init); module_init(ar7_wdt_init);
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <asm/reg_booke.h> #include <asm/reg_booke.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/time.h>
#include <asm/div64.h>
/* If the kernel parameter wdt=1, the watchdog will be enabled at boot. /* If the kernel parameter wdt=1, the watchdog will be enabled at boot.
* Also, the wdt_period sets the watchdog timer period timeout. * Also, the wdt_period sets the watchdog timer period timeout.
...@@ -32,7 +34,7 @@ ...@@ -32,7 +34,7 @@
*/ */
#ifdef CONFIG_FSL_BOOKE #ifdef CONFIG_FSL_BOOKE
#define WDT_PERIOD_DEFAULT 63 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */ #define WDT_PERIOD_DEFAULT 38 /* Ex. wdt_period=28 bus=333Mhz,reset=~40sec */
#else #else
#define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */ #define WDT_PERIOD_DEFAULT 3 /* Refer to the PPC40x and PPC4xx manuals */
#endif /* for timing information */ #endif /* for timing information */
...@@ -41,7 +43,7 @@ u32 booke_wdt_enabled; ...@@ -41,7 +43,7 @@ u32 booke_wdt_enabled;
u32 booke_wdt_period = WDT_PERIOD_DEFAULT; u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
#ifdef CONFIG_FSL_BOOKE #ifdef CONFIG_FSL_BOOKE
#define WDTP(x) ((((63-x)&0x3)<<30)|(((63-x)&0x3c)<<15)) #define WDTP(x) ((((x)&0x3)<<30)|(((x)&0x3c)<<15))
#define WDTP_MASK (WDTP(0)) #define WDTP_MASK (WDTP(0))
#else #else
#define WDTP(x) (TCR_WP(x)) #define WDTP(x) (TCR_WP(x))
...@@ -50,6 +52,45 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT; ...@@ -50,6 +52,45 @@ u32 booke_wdt_period = WDT_PERIOD_DEFAULT;
static DEFINE_SPINLOCK(booke_wdt_lock); static DEFINE_SPINLOCK(booke_wdt_lock);
/* For the specified period, determine the number of seconds
* corresponding to the reset time. There will be a watchdog
* exception at approximately 3/5 of this time.
*
* The formula to calculate this is given by:
* 2.5 * (2^(63-period+1)) / timebase_freq
*
* In order to simplify things, we assume that period is
* at least 1. This will still result in a very long timeout.
*/
static unsigned long long period_to_sec(unsigned int period)
{
unsigned long long tmp = 1ULL << (64 - period);
unsigned long tmp2 = ppc_tb_freq;
/* tmp may be a very large number and we don't want to overflow,
* so divide the timebase freq instead of multiplying tmp
*/
tmp2 = tmp2 / 5 * 2;
do_div(tmp, tmp2);
return tmp;
}
/*
* This procedure will find the highest period which will give a timeout
* greater than the one required. e.g. for a bus speed of 66666666 and
* and a parameter of 2 secs, then this procedure will return a value of 38.
*/
static unsigned int sec_to_period(unsigned int secs)
{
unsigned int period;
for (period = 63; period > 0; period--) {
if (period_to_sec(period) >= secs)
return period;
}
return 0;
}
static void __booke_wdt_ping(void *data) static void __booke_wdt_ping(void *data)
{ {
mtspr(SPRN_TSR, TSR_ENW|TSR_WIS); mtspr(SPRN_TSR, TSR_ENW|TSR_WIS);
...@@ -93,7 +134,7 @@ static long booke_wdt_ioctl(struct file *file, ...@@ -93,7 +134,7 @@ static long booke_wdt_ioctl(struct file *file,
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user(arg, &ident, sizeof(struct watchdog_info))) if (copy_to_user((void *)arg, &ident, sizeof(ident)))
return -EFAULT; return -EFAULT;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
return put_user(ident.options, p); return put_user(ident.options, p);
...@@ -115,8 +156,16 @@ static long booke_wdt_ioctl(struct file *file, ...@@ -115,8 +156,16 @@ static long booke_wdt_ioctl(struct file *file,
booke_wdt_ping(); booke_wdt_ping();
return 0; return 0;
case WDIOC_SETTIMEOUT: case WDIOC_SETTIMEOUT:
if (get_user(booke_wdt_period, p)) if (get_user(tmp, p))
return -EFAULT; return -EFAULT;
#ifdef CONFIG_FSL_BOOKE
/* period of 1 gives the largest possible timeout */
if (tmp > period_to_sec(1))
return -EINVAL;
booke_wdt_period = sec_to_period(tmp);
#else
booke_wdt_period = tmp;
#endif
mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) | mtspr(SPRN_TCR, (mfspr(SPRN_TCR) & ~WDTP_MASK) |
WDTP(booke_wdt_period)); WDTP(booke_wdt_period));
return 0; return 0;
......
...@@ -110,7 +110,7 @@ static void coh901327_enable(u16 timeout) ...@@ -110,7 +110,7 @@ static void coh901327_enable(u16 timeout)
* Wait 3 32 kHz cycles for it to take effect * Wait 3 32 kHz cycles for it to take effect
*/ */
freq = clk_get_rate(clk); freq = clk_get_rate(clk);
delay_ns = (1000000000 + freq - 1) / freq; /* Freq to ns and round up */ delay_ns = DIV_ROUND_UP(1000000000, freq); /* Freq to ns and round up */
delay_ns = 3 * delay_ns; /* Wait 3 cycles */ delay_ns = 3 * delay_ns; /* Wait 3 cycles */
ndelay(delay_ns); ndelay(delay_ns);
/* Enable the watchdog interrupt */ /* Enable the watchdog interrupt */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/clk.h>
#define MODULE_NAME "DAVINCI-WDT: " #define MODULE_NAME "DAVINCI-WDT: "
...@@ -69,6 +70,7 @@ static unsigned long wdt_status; ...@@ -69,6 +70,7 @@ static unsigned long wdt_status;
static struct resource *wdt_mem; static struct resource *wdt_mem;
static void __iomem *wdt_base; static void __iomem *wdt_base;
struct clk *wdt_clk;
static void wdt_service(void) static void wdt_service(void)
{ {
...@@ -86,6 +88,9 @@ static void wdt_enable(void) ...@@ -86,6 +88,9 @@ static void wdt_enable(void)
{ {
u32 tgcr; u32 tgcr;
u32 timer_margin; u32 timer_margin;
unsigned long wdt_freq;
wdt_freq = clk_get_rate(wdt_clk);
spin_lock(&io_lock); spin_lock(&io_lock);
...@@ -99,9 +104,9 @@ static void wdt_enable(void) ...@@ -99,9 +104,9 @@ static void wdt_enable(void)
iowrite32(0, wdt_base + TIM12); iowrite32(0, wdt_base + TIM12);
iowrite32(0, wdt_base + TIM34); iowrite32(0, wdt_base + TIM34);
/* set timeout period */ /* set timeout period */
timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) & 0xffffffff); timer_margin = (((u64)heartbeat * wdt_freq) & 0xffffffff);
iowrite32(timer_margin, wdt_base + PRD12); iowrite32(timer_margin, wdt_base + PRD12);
timer_margin = (((u64)heartbeat * CLOCK_TICK_RATE) >> 32); timer_margin = (((u64)heartbeat * wdt_freq) >> 32);
iowrite32(timer_margin, wdt_base + PRD34); iowrite32(timer_margin, wdt_base + PRD34);
/* enable run continuously */ /* enable run continuously */
iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR); iowrite32(ENAMODE12_PERIODIC, wdt_base + TCR);
...@@ -199,6 +204,12 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev) ...@@ -199,6 +204,12 @@ static int __devinit davinci_wdt_probe(struct platform_device *pdev)
struct resource *res; struct resource *res;
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
wdt_clk = clk_get(dev, NULL);
if (WARN_ON(IS_ERR(wdt_clk)))
return PTR_ERR(wdt_clk);
clk_enable(wdt_clk);
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT; heartbeat = DEFAULT_HEARTBEAT;
...@@ -245,6 +256,10 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev) ...@@ -245,6 +256,10 @@ static int __devexit davinci_wdt_remove(struct platform_device *pdev)
kfree(wdt_mem); kfree(wdt_mem);
wdt_mem = NULL; wdt_mem = NULL;
} }
clk_disable(wdt_clk);
clk_put(wdt_clk);
return 0; return 0;
} }
......
...@@ -139,7 +139,7 @@ static long iop_wdt_ioctl(struct file *file, ...@@ -139,7 +139,7 @@ static long iop_wdt_ioctl(struct file *file,
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof ident)) if (copy_to_user(argp, &ident, sizeof(ident)))
ret = -EFAULT; ret = -EFAULT;
else else
ret = 0; ret = 0;
......
/*
* Copyright (c) 2009 Nuvoton technology corporation.
*
* Wan ZongShun <mcuos.com@gmail.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;version 2 of the License.
*
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/uaccess.h>
#define REG_WTCR 0x1c
#define WTCLK (0x01 << 10)
#define WTE (0x01 << 7) /*wdt enable*/
#define WTIS (0x03 << 4)
#define WTIF (0x01 << 3)
#define WTRF (0x01 << 2)
#define WTRE (0x01 << 1)
#define WTR (0x01 << 0)
/*
* The watchdog time interval can be calculated via following formula:
* WTIS real time interval (formula)
* 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
* 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
* 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
* 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
*
* The external crystal freq is 15Mhz in the nuc900 evaluation board.
* So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
* 0x03 = +- 16.92 seconds..
*/
#define WDT_HW_TIMEOUT 0x02
#define WDT_TIMEOUT (HZ/2)
#define WDT_HEARTBEAT 15
static int heartbeat = WDT_HEARTBEAT;
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeats in seconds. "
"(default = " __MODULE_STRING(WDT_HEARTBEAT) ")");
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) ")");
struct nuc900_wdt {
struct resource *res;
struct clk *wdt_clock;
struct platform_device *pdev;
void __iomem *wdt_base;
char expect_close;
struct timer_list timer;
spinlock_t wdt_lock;
unsigned long next_heartbeat;
};
static unsigned long nuc900wdt_busy;
struct nuc900_wdt *nuc900_wdt;
static inline void nuc900_wdt_keepalive(void)
{
unsigned int val;
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val |= (WTR | WTIF);
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
}
static inline void nuc900_wdt_start(void)
{
unsigned int val;
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val |= (WTRE | WTE | WTR | WTCLK | WTIF);
val &= ~WTIS;
val |= (WDT_HW_TIMEOUT << 0x04);
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
}
static inline void nuc900_wdt_stop(void)
{
unsigned int val;
del_timer(&nuc900_wdt->timer);
spin_lock(&nuc900_wdt->wdt_lock);
val = __raw_readl(nuc900_wdt->wdt_base + REG_WTCR);
val &= ~WTE;
__raw_writel(val, nuc900_wdt->wdt_base + REG_WTCR);
spin_unlock(&nuc900_wdt->wdt_lock);
}
static inline void nuc900_wdt_ping(void)
{
nuc900_wdt->next_heartbeat = jiffies + heartbeat * HZ;
}
static int nuc900_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &nuc900wdt_busy))
return -EBUSY;
nuc900_wdt_start();
return nonseekable_open(inode, file);
}
static int nuc900_wdt_close(struct inode *inode, struct file *file)
{
if (nuc900_wdt->expect_close == 42)
nuc900_wdt_stop();
else {
dev_crit(&nuc900_wdt->pdev->dev,
"Unexpected close, not stopping watchdog!\n");
nuc900_wdt_ping();
}
nuc900_wdt->expect_close = 0;
clear_bit(0, &nuc900wdt_busy);
return 0;
}
static const struct watchdog_info nuc900_wdt_info = {
.identity = "nuc900 watchdog",
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
static long nuc900_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_value;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &nuc900_wdt_info,
sizeof(nuc900_wdt_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
nuc900_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
heartbeat = new_value;
nuc900_wdt_ping();
return put_user(new_value, p);
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
default:
return -ENOTTY;
}
}
static ssize_t nuc900_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (!len)
return 0;
/* Scan for magic character */
if (!nowayout) {
size_t i;
nuc900_wdt->expect_close = 0;
for (i = 0; i < len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V') {
nuc900_wdt->expect_close = 42;
break;
}
}
}
nuc900_wdt_ping();
return len;
}
static void nuc900_wdt_timer_ping(unsigned long data)
{
if (time_before(jiffies, nuc900_wdt->next_heartbeat)) {
nuc900_wdt_keepalive();
mod_timer(&nuc900_wdt->timer, jiffies + WDT_TIMEOUT);
} else
dev_warn(&nuc900_wdt->pdev->dev, "Will reset the machine !\n");
}
static const struct file_operations nuc900wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = nuc900_wdt_ioctl,
.open = nuc900_wdt_open,
.release = nuc900_wdt_close,
.write = nuc900_wdt_write,
};
static struct miscdevice nuc900wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &nuc900wdt_fops,
};
static int __devinit nuc900wdt_probe(struct platform_device *pdev)
{
int ret = 0;
nuc900_wdt = kzalloc(sizeof(struct nuc900_wdt), GFP_KERNEL);
if (!nuc900_wdt)
return -ENOMEM;
nuc900_wdt->pdev = pdev;
spin_lock_init(&nuc900_wdt->wdt_lock);
nuc900_wdt->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (nuc900_wdt->res == NULL) {
dev_err(&pdev->dev, "no memory resource specified\n");
ret = -ENOENT;
goto err_get;
}
if (!request_mem_region(nuc900_wdt->res->start,
resource_size(nuc900_wdt->res), pdev->name)) {
dev_err(&pdev->dev, "failed to get memory region\n");
ret = -ENOENT;
goto err_get;
}
nuc900_wdt->wdt_base = ioremap(nuc900_wdt->res->start,
resource_size(nuc900_wdt->res));
if (nuc900_wdt->wdt_base == NULL) {
dev_err(&pdev->dev, "failed to ioremap() region\n");
ret = -EINVAL;
goto err_req;
}
nuc900_wdt->wdt_clock = clk_get(&pdev->dev, NULL);
if (IS_ERR(nuc900_wdt->wdt_clock)) {
dev_err(&pdev->dev, "failed to find watchdog clock source\n");
ret = PTR_ERR(nuc900_wdt->wdt_clock);
goto err_map;
}
clk_enable(nuc900_wdt->wdt_clock);
setup_timer(&nuc900_wdt->timer, nuc900_wdt_timer_ping, 0);
if (misc_register(&nuc900wdt_miscdev)) {
dev_err(&pdev->dev, "err register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
goto err_clk;
}
return 0;
err_clk:
clk_disable(nuc900_wdt->wdt_clock);
clk_put(nuc900_wdt->wdt_clock);
err_map:
iounmap(nuc900_wdt->wdt_base);
err_req:
release_mem_region(nuc900_wdt->res->start,
resource_size(nuc900_wdt->res));
err_get:
kfree(nuc900_wdt);
return ret;
}
static int __devexit nuc900wdt_remove(struct platform_device *pdev)
{
misc_deregister(&nuc900wdt_miscdev);
clk_disable(nuc900_wdt->wdt_clock);
clk_put(nuc900_wdt->wdt_clock);
iounmap(nuc900_wdt->wdt_base);
release_mem_region(nuc900_wdt->res->start,
resource_size(nuc900_wdt->res));
kfree(nuc900_wdt);
return 0;
}
static struct platform_driver nuc900wdt_driver = {
.probe = nuc900wdt_probe,
.remove = __devexit_p(nuc900wdt_remove),
.driver = {
.name = "nuc900-wdt",
.owner = THIS_MODULE,
},
};
static int __init nuc900_wdt_init(void)
{
return platform_driver_register(&nuc900wdt_driver);
}
static void __exit nuc900_wdt_exit(void)
{
platform_driver_unregister(&nuc900wdt_driver);
}
module_init(nuc900_wdt_init);
module_exit(nuc900_wdt_exit);
MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
MODULE_DESCRIPTION("Watchdog driver for NUC900");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:nuc900-wdt");
...@@ -340,7 +340,7 @@ static const struct resource *wdt_gpi_get_resource(struct platform_device *pdv, ...@@ -340,7 +340,7 @@ static const struct resource *wdt_gpi_get_resource(struct platform_device *pdv,
const char *name, unsigned int type) const char *name, unsigned int type)
{ {
char buf[80]; char buf[80];
if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf) if (snprintf(buf, sizeof(buf), "%s_0", name) >= sizeof(buf))
return NULL; return NULL;
return platform_get_resource_byname(pdv, type, buf); return platform_get_resource_byname(pdv, type, buf);
} }
......
/*
* Watchdog driver for SBC-FITPC2 board
*
* Author: Denis Turischev <denis@compulab.co.il>
*
* Adapted from the IXP2000 watchdog driver by Deepak Saxena.
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed "as is" without any
* warranty of any kind, whether express or implied.
*/
#define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
#include <linux/module.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/dmi.h>
#include <linux/io.h>
#include <linux/uaccess.h>
#include <asm/system.h>
static int nowayout = WATCHDOG_NOWAYOUT;
static unsigned int margin = 60; /* (secs) Default is 1 minute */
static unsigned long wdt_status;
static DEFINE_SPINLOCK(wdt_lock);
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
#define COMMAND_PORT 0x4c
#define DATA_PORT 0x48
#define IFACE_ON_COMMAND 1
#define REBOOT_COMMAND 2
#define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
static void wdt_send_data(unsigned char command, unsigned char data)
{
outb(command, COMMAND_PORT);
mdelay(100);
outb(data, DATA_PORT);
mdelay(200);
}
static void wdt_enable(void)
{
spin_lock(&wdt_lock);
wdt_send_data(IFACE_ON_COMMAND, 1);
wdt_send_data(REBOOT_COMMAND, margin);
spin_unlock(&wdt_lock);
}
static void wdt_disable(void)
{
spin_lock(&wdt_lock);
wdt_send_data(IFACE_ON_COMMAND, 0);
wdt_send_data(REBOOT_COMMAND, 0);
spin_unlock(&wdt_lock);
}
static int fitpc2_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
return -EBUSY;
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
wdt_enable();
return nonseekable_open(inode, file);
}
static ssize_t fitpc2_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
size_t i;
if (!len)
return 0;
if (nowayout) {
len = 0;
goto out;
}
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
out:
wdt_enable();
return len;
}
static struct watchdog_info ident = {
.options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
.identity = WATCHDOG_NAME,
};
static long fitpc2_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = -ENOTTY;
int time;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
ret = put_user(0, (int *)arg);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(0, (int *)arg);
break;
case WDIOC_KEEPALIVE:
wdt_enable();
ret = 0;
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, (int *)arg);
if (ret)
break;
if (time < 31 || time > 255) {
ret = -EINVAL;
break;
}
margin = time;
wdt_enable();
/* Fall through */
case WDIOC_GETTIMEOUT:
ret = put_user(margin, (int *)arg);
break;
}
return ret;
}
static int fitpc2_wdt_release(struct inode *inode, struct file *file)
{
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
wdt_disable();
pr_info("Device disabled\n");
} else {
pr_warning("Device closed unexpectedly -"
" timer will not stop\n");
wdt_enable();
}
clear_bit(WDT_IN_USE, &wdt_status);
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
return 0;
}
static const struct file_operations fitpc2_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = fitpc2_wdt_write,
.unlocked_ioctl = fitpc2_wdt_ioctl,
.open = fitpc2_wdt_open,
.release = fitpc2_wdt_release,
};
static struct miscdevice fitpc2_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &fitpc2_wdt_fops,
};
static int __init fitpc2_wdt_init(void)
{
int err;
if (strcmp("SBC-FITPC2", dmi_get_system_info(DMI_BOARD_NAME))) {
pr_info("board name is: %s. Should be SBC-FITPC2\n",
dmi_get_system_info(DMI_BOARD_NAME));
return -ENODEV;
}
if (!request_region(COMMAND_PORT, 1, WATCHDOG_NAME)) {
pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT);
return -EIO;
}
if (!request_region(DATA_PORT, 1, WATCHDOG_NAME)) {
pr_err("I/O address 0x%04x already in use\n", DATA_PORT);
err = -EIO;
goto err_data_port;
}
if (margin < 31 || margin > 255) {
pr_err("margin must be in range 31 - 255"
" seconds, you tried to set %d\n", margin);
err = -EINVAL;
goto err_margin;
}
err = misc_register(&fitpc2_wdt_miscdev);
if (!err) {
pr_err("cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, err);
goto err_margin;
}
return 0;
err_margin:
release_region(DATA_PORT, 1);
err_data_port:
release_region(COMMAND_PORT, 1);
return err;
}
static void __exit fitpc2_wdt_exit(void)
{
misc_deregister(&fitpc2_wdt_miscdev);
release_region(DATA_PORT, 1);
release_region(COMMAND_PORT, 1);
}
module_init(fitpc2_wdt_init);
module_exit(fitpc2_wdt_exit);
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
...@@ -197,7 +197,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -197,7 +197,7 @@ static long sc1200wdt_ioctl(struct file *file, unsigned int cmd,
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof ident)) if (copy_to_user(argp, &ident, sizeof(ident)))
return -EFAULT; return -EFAULT;
return 0; return 0;
......
...@@ -80,7 +80,7 @@ static unsigned long open_lock; ...@@ -80,7 +80,7 @@ static unsigned long open_lock;
static DEFINE_SPINLOCK(wdtpci_lock); static DEFINE_SPINLOCK(wdtpci_lock);
static char expect_close; static char expect_close;
static int io; static resource_size_t io;
static int irq; static int irq;
/* Default timeout */ /* Default timeout */
...@@ -647,14 +647,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, ...@@ -647,14 +647,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
goto out_pci; goto out_pci;
} }
irq = dev->irq; if (pci_request_region(dev, 2, "wdt_pci")) {
io = pci_resource_start(dev, 2); printk(KERN_ERR PFX "I/O address 0x%llx already in use\n",
(unsigned long long)pci_resource_start(dev, 2));
if (request_region(io, 16, "wdt_pci") == NULL) {
printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", io);
goto out_pci; goto out_pci;
} }
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_DISABLED | IRQF_SHARED,
"wdt_pci", &wdtpci_miscdev)) { "wdt_pci", &wdtpci_miscdev)) {
printk(KERN_ERR PFX "IRQ %d is not free\n", irq); printk(KERN_ERR PFX "IRQ %d is not free\n", irq);
...@@ -662,8 +663,8 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, ...@@ -662,8 +663,8 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
} }
printk(KERN_INFO printk(KERN_INFO
"PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%04x (Interrupt %d)\n", "PCI-WDT500/501 (PCI-WDG-CSM) driver 0.10 at 0x%llx (Interrupt %d)\n",
io, irq); (unsigned long long)io, irq);
/* Check that the heartbeat value is within its range; /* Check that the heartbeat value is within its range;
if not reset to the default */ if not reset to the default */
...@@ -717,7 +718,7 @@ out_rbt: ...@@ -717,7 +718,7 @@ out_rbt:
out_irq: out_irq:
free_irq(irq, &wdtpci_miscdev); free_irq(irq, &wdtpci_miscdev);
out_reg: out_reg:
release_region(io, 16); pci_release_region(dev, 2);
out_pci: out_pci:
pci_disable_device(dev); pci_disable_device(dev);
goto out; goto out;
...@@ -733,7 +734,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) ...@@ -733,7 +734,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
misc_deregister(&temp_miscdev); misc_deregister(&temp_miscdev);
unregister_reboot_notifier(&wdtpci_notifier); unregister_reboot_notifier(&wdtpci_notifier);
free_irq(irq, &wdtpci_miscdev); free_irq(irq, &wdtpci_miscdev);
release_region(io, 16); pci_release_region(pdev, 2);
pci_disable_device(pdev); pci_disable_device(pdev);
dev_count--; dev_count--;
} }
......
/*
* Watchdog driver for the wm831x PMICs
*
* Copyright (C) 2009 Wolfson Microelectronics
*
* 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/gpio.h>
#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
#include <linux/mfd/wm831x/watchdog.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 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;
/* 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 */
} wm831x_wdt_cfgs[] = {
{ 1, 2 },
{ 2, 3 },
{ 4, 4 },
{ 8, 5 },
{ 16, 6 },
{ 32, 7 },
{ 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)
{
int ret;
mutex_lock(&wdt_mutex);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_ENA, WM831X_WDOG_ENA);
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_stop(struct wm831x *wm831x)
{
int ret;
mutex_lock(&wdt_mutex);
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_set_bits(wm831x, WM831X_WATCHDOG,
WM831X_WDOG_ENA, 0);
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_kick(struct wm831x *wm831x)
{
int ret;
u16 reg;
mutex_lock(&wdt_mutex);
if (update_gpio) {
gpio_set_value_cansleep(update_gpio, update_state);
update_state = !update_state;
ret = 0;
goto out;
}
reg = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (!(reg & WM831X_WDOG_RST_SRC)) {
dev_err(wm831x->dev, "Hardware watchdog update unsupported\n");
ret = -EINVAL;
goto out;
}
reg |= WM831X_WDOG_RESET;
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev, "Failed to unlock security key: %d\n",
ret);
}
out:
mutex_unlock(&wdt_mutex);
return ret;
}
static int wm831x_wdt_open(struct inode *inode, struct file *file)
{
int ret;
if (!wm831x)
return -ENODEV;
if (test_and_set_bit(0, &wm831x_wdt_users))
return -EBUSY;
ret = wm831x_wdt_start(wm831x);
if (ret != 0)
return ret;
return nonseekable_open(inode, file);
}
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);
}
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;
}
static struct watchdog_info ident = {
.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 = {
.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,
};
static int __devinit wm831x_wdt_probe(struct platform_device *pdev)
{
struct wm831x_pdata *chip_pdata;
struct wm831x_watchdog_pdata *pdata;
int reg, ret;
wm831x = dev_get_drvdata(pdev->dev.parent);
ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read watchdog status: %d\n",
ret);
goto err;
}
reg = ret;
if (reg & WM831X_WDOG_DEBUG)
dev_warn(wm831x->dev, "Watchdog is paused\n");
/* Apply any configuration */
if (pdev->dev.parent->platform_data) {
chip_pdata = pdev->dev.parent->platform_data;
pdata = chip_pdata->watchdog;
} else {
pdata = NULL;
}
if (pdata) {
reg &= ~(WM831X_WDOG_SECACT_MASK | WM831X_WDOG_PRIMACT_MASK |
WM831X_WDOG_RST_SRC);
reg |= pdata->primary << WM831X_WDOG_PRIMACT_SHIFT;
reg |= pdata->secondary << WM831X_WDOG_SECACT_SHIFT;
reg |= pdata->software << WM831X_WDOG_RST_SRC_SHIFT;
if (pdata->update_gpio) {
ret = gpio_request(pdata->update_gpio,
"Watchdog update");
if (ret < 0) {
dev_err(wm831x->dev,
"Failed to request update GPIO: %d\n",
ret);
goto err;
}
ret = gpio_direction_output(pdata->update_gpio, 0);
if (ret != 0) {
dev_err(wm831x->dev,
"gpio_direction_output returned: %d\n",
ret);
goto err_gpio;
}
update_gpio = pdata->update_gpio;
/* Make sure the watchdog takes hardware updates */
reg |= WM831X_WDOG_RST_SRC;
}
ret = wm831x_reg_unlock(wm831x);
if (ret == 0) {
ret = wm831x_reg_write(wm831x, WM831X_WATCHDOG, reg);
wm831x_reg_lock(wm831x);
} else {
dev_err(wm831x->dev,
"Failed to unlock security key: %d\n", ret);
goto err_gpio;
}
}
wm831x_wdt_miscdev.parent = &pdev->dev;
ret = misc_register(&wm831x_wdt_miscdev);
if (ret != 0) {
dev_err(wm831x->dev, "Failed to register miscdev: %d\n", ret);
goto err_gpio;
}
return 0;
err_gpio:
if (update_gpio) {
gpio_free(update_gpio);
update_gpio = 0;
}
err:
return ret;
}
static int __devexit wm831x_wdt_remove(struct platform_device *pdev)
{
if (update_gpio) {
gpio_free(update_gpio);
update_gpio = 0;
}
misc_deregister(&wm831x_wdt_miscdev);
return 0;
}
static struct platform_driver wm831x_wdt_driver = {
.probe = wm831x_wdt_probe,
.remove = __devexit_p(wm831x_wdt_remove),
.driver = {
.name = "wm831x-watchdog",
},
};
static int __init wm831x_wdt_init(void)
{
return platform_driver_register(&wm831x_wdt_driver);
}
module_init(wm831x_wdt_init);
static void __exit wm831x_wdt_exit(void)
{
platform_driver_unregister(&wm831x_wdt_driver);
}
module_exit(wm831x_wdt_exit);
MODULE_AUTHOR("Mark Brown");
MODULE_DESCRIPTION("WM831x Watchdog");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:wm831x-watchdog");
/*
* include/linux/mfd/wm831x/watchdog.h -- Watchdog for WM831x
*
* Copyright 2009 Wolfson Microelectronics PLC.
*
* Author: Mark Brown <broonie@opensource.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; either version 2 of the License, or (at your
* option) any later version.
*
*/
#ifndef __MFD_WM831X_WATCHDOG_H__
#define __MFD_WM831X_WATCHDOG_H__
/*
* R16388 (0x4004) - Watchdog
*/
#define WM831X_WDOG_ENA 0x8000 /* WDOG_ENA */
#define WM831X_WDOG_ENA_MASK 0x8000 /* WDOG_ENA */
#define WM831X_WDOG_ENA_SHIFT 15 /* WDOG_ENA */
#define WM831X_WDOG_ENA_WIDTH 1 /* WDOG_ENA */
#define WM831X_WDOG_DEBUG 0x4000 /* WDOG_DEBUG */
#define WM831X_WDOG_DEBUG_MASK 0x4000 /* WDOG_DEBUG */
#define WM831X_WDOG_DEBUG_SHIFT 14 /* WDOG_DEBUG */
#define WM831X_WDOG_DEBUG_WIDTH 1 /* WDOG_DEBUG */
#define WM831X_WDOG_RST_SRC 0x2000 /* WDOG_RST_SRC */
#define WM831X_WDOG_RST_SRC_MASK 0x2000 /* WDOG_RST_SRC */
#define WM831X_WDOG_RST_SRC_SHIFT 13 /* WDOG_RST_SRC */
#define WM831X_WDOG_RST_SRC_WIDTH 1 /* WDOG_RST_SRC */
#define WM831X_WDOG_SLPENA 0x1000 /* WDOG_SLPENA */
#define WM831X_WDOG_SLPENA_MASK 0x1000 /* WDOG_SLPENA */
#define WM831X_WDOG_SLPENA_SHIFT 12 /* WDOG_SLPENA */
#define WM831X_WDOG_SLPENA_WIDTH 1 /* WDOG_SLPENA */
#define WM831X_WDOG_RESET 0x0800 /* WDOG_RESET */
#define WM831X_WDOG_RESET_MASK 0x0800 /* WDOG_RESET */
#define WM831X_WDOG_RESET_SHIFT 11 /* WDOG_RESET */
#define WM831X_WDOG_RESET_WIDTH 1 /* WDOG_RESET */
#define WM831X_WDOG_SECACT_MASK 0x0300 /* WDOG_SECACT - [9:8] */
#define WM831X_WDOG_SECACT_SHIFT 8 /* WDOG_SECACT - [9:8] */
#define WM831X_WDOG_SECACT_WIDTH 2 /* WDOG_SECACT - [9:8] */
#define WM831X_WDOG_PRIMACT_MASK 0x0030 /* WDOG_PRIMACT - [5:4] */
#define WM831X_WDOG_PRIMACT_SHIFT 4 /* WDOG_PRIMACT - [5:4] */
#define WM831X_WDOG_PRIMACT_WIDTH 2 /* WDOG_PRIMACT - [5:4] */
#define WM831X_WDOG_TO_MASK 0x0007 /* WDOG_TO - [2:0] */
#define WM831X_WDOG_TO_SHIFT 0 /* WDOG_TO - [2:0] */
#define WM831X_WDOG_TO_WIDTH 3 /* WDOG_TO - [2:0] */
#endif
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