提交 cf9c1b92 编写于 作者: L 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] wdt_pci.c: remove #ifdef CONFIG_WDT_501_PCI
  [WATCHDOG] hpwdt: Add NMI priority option
  [WATCHDOG] OMAP fixes: enable clock in probe, trigger timer reload
  [WATCHDOG] add bcm47xx watchdog driver
  [WATCHDOG] Freescale STMP: watchdog driver
  [WATCHDOG] twl4030 watchdog driver
  [WATCHDOG] U300 COH 901 327 watchdog driver
  [WATCHDOG] Add pnx833x_wdt
......@@ -19,30 +19,41 @@ Last reviewed: 06/02/2009
not be updated in a timely fashion and a hardware system reset (also known as
an Automatic Server Recovery (ASR)) event will occur.
The hpwdt driver also has three (3) module parameters. They are the following:
The hpwdt driver also has four (4) module parameters. They are the following:
soft_margin - allows the user to set the watchdog timer value
allow_kdump - allows the user to save off a kernel dump image after an NMI
nowayout - basic watchdog parameter that does not allow the timer to
be restarted or an impending ASR to be escaped.
priority - determines whether or not the hpwdt driver is first on the
die_notify list to handle NMIs or last. The default value
for this module parameter is 0 or LAST. If the user wants to
enable NMI sourcing then reload the hpwdt driver with
priority=1 (and boot with nmi_watchdog=0).
NOTE: More information about watchdog drivers in general, including the ioctl
interface to /dev/watchdog can be found in
Documentation/watchdog/watchdog-api.txt and Documentation/IPMI.txt.
The NMI sourcing capability is disabled when the driver discovers that the
nmi_watchdog is turned on (nmi_watchdog = 1). This is due to the inability to
The priority parameter was introduced due to other kernel software that relied
on handling NMIs (like oprofile). Keeping hpwdt's priority at 0 (or LAST)
enables the users of NMIs for non critical events to be work as expected.
The NMI sourcing capability is disabled by default due to the inability to
distinguish between "NMI Watchdog Ticks" and "HW generated NMI events" in the
Linux kernel. What this means is that the hpwdt nmi handler code is called
each time the NMI signal fires off. This could amount to several thousands of
NMIs in a matter of seconds. If a user sees the Linux kernel's "dazed and
confused" message in the logs or if the system gets into a hung state, then
the user should reboot with nmi_watchdog=0.
the hpwdt driver can be reloaded with the "priority" module parameter set
(priority=1).
1. If the kernel has not been booted with nmi_watchdog turned off then
edit /boot/grub/menu.lst and place the nmi_watchdog=0 at the end of the
currently booting kernel line.
2. reboot the sever
3. Once the system comes up perform a rmmod hpwdt
4. insmod /lib/modules/`uname -r`/kernel/drivers/char/watchdog/hpwdt.ko priority=1
Now, the hpwdt can successfully receive and source the NMI and provide a log
message that details the reason for the NMI (as determined by the HP BIOS).
......
......@@ -101,6 +101,12 @@
#define twl_has_usb() false
#endif
#if defined(CONFIG_TWL4030_WATCHDOG) || \
defined(CONFIG_TWL4030_WATCHDOG_MODULE)
#define twl_has_watchdog() true
#else
#define twl_has_watchdog() false
#endif
/* Triton Core internal information (BEGIN) */
......@@ -526,6 +532,12 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
usb_transceiver = child;
}
if (twl_has_watchdog()) {
child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0);
if (IS_ERR(child))
return PTR_ERR(child);
}
if (twl_has_regulator()) {
/*
child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
......
......@@ -240,6 +240,32 @@ config ORION_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called orion_wdt.
config COH901327_WATCHDOG
bool "ST-Ericsson COH 901 327 watchdog"
depends on ARCH_U300
default y if MACH_U300
help
Say Y here to include Watchdog timer support for the
watchdog embedded into the ST-Ericsson U300 series platforms.
This watchdog is used to reset the system and thus cannot be
compiled as a module.
config TWL4030_WATCHDOG
tristate "TWL4030 Watchdog"
depends on TWL4030_CORE
help
Support for TI TWL4030 watchdog. Say 'Y' here to enable the
watchdog timer support for TWL4030 chips.
config STMP3XXX_WATCHDOG
tristate "Freescale STMP3XXX watchdog"
depends on ARCH_STMP3XXX
help
Say Y here if to include support for the watchdog timer
for the Sigmatel STMP37XX/378X SoC.
To compile this driver as a module, choose M here: the
module will be called stmp3xxx_wdt.
# AVR32 Architecture
config AT32AP700X_WDT
......@@ -703,6 +729,12 @@ config SBC_EPX_C3_WATCHDOG
# MIPS Architecture
config BCM47XX_WDT
tristate "Broadcom BCM47xx Watchdog Timer"
depends on BCM47XX
help
Hardware driver for the Broadcom BCM47xx Watchog Timer.
config RC32434_WDT
tristate "IDT RC32434 SoC Watchdog Timer"
depends on MIKROTIK_RB532
......@@ -729,6 +761,15 @@ config WDT_MTX1
Hardware driver for the MTX-1 boards. This is a watchdog timer that
will reboot the machine after a 100 seconds timer expired.
config PNX833X_WDT
tristate "PNX833x Hardware Watchdog"
depends on SOC_PNX8335
help
Hardware driver for the PNX833x's watchdog. This is a
watchdog timer that will reboot the machine after a programable
timer has expired and no process has written to /dev/watchdog during
that time.
config WDT_RM9K_GPI
tristate "RM9000/GPI hardware watchdog"
depends on CPU_RM9000
......@@ -966,24 +1007,16 @@ config WDTPCI
---help---
If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N.
To compile this driver as a module, choose M here: the
module will be called wdt_pci.
config WDT_501_PCI
bool "PCI-WDT501 features"
depends on WDTPCI
help
Saying Y here and creating a character special file /dev/temperature
with major number 10 and minor number 131 ("man mknod") will give
you a thermometer inside your computer: reading from
/dev/temperature yields one byte, the temperature in degrees
Fahrenheit. This works only if you have a PCI-WDT501 watchdog board
installed.
If you have a PCI-WDT501 watchdog board then you can enable the
temperature sensor by setting the type parameter to 501.
If you want to enable the Fan Tachometer on the PCI-WDT501, then you
can do this via the tachometer parameter. Only do this if you have a
fan tachometer actually set up.
To compile this driver as a module, choose M here: the
module will be called wdt_pci.
#
# USB-based Watchdog Cards
#
......
......@@ -28,6 +28,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
......@@ -41,6 +42,8 @@ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
# AVR32 Architecture
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
......@@ -98,9 +101,11 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
# M68KNOMMU Architecture
# MIPS Architecture
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
......
/*
* Watchdog driver for Broadcom BCM47XX
*
* Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs>
* Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr>
*
* 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.
*/
#include <linux/bitops.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/reboot.h>
#include <linux/types.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#include <linux/ssb/ssb_embedded.h>
#include <asm/mach-bcm47xx/bcm47xx.h>
#define DRV_NAME "bcm47xx_wdt"
#define WDT_DEFAULT_TIME 30 /* seconds */
#define WDT_MAX_TIME 255 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
static int nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="
__MODULE_STRING(WDT_DEFAULT_TIME) ")");
#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#endif
static unsigned long bcm47xx_wdt_busy;
static char expect_release;
static struct timer_list wdt_timer;
static atomic_t ticks;
static inline void bcm47xx_wdt_hw_start(void)
{
/* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */
ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff);
}
static inline int bcm47xx_wdt_hw_stop(void)
{
return ssb_watchdog_timer_set(&ssb_bcm47xx, 0);
}
static void bcm47xx_timer_tick(unsigned long unused)
{
if (!atomic_dec_and_test(&ticks)) {
bcm47xx_wdt_hw_start();
mod_timer(&wdt_timer, jiffies + HZ);
} else {
printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n");
}
}
static inline void bcm47xx_wdt_pet(void)
{
atomic_set(&ticks, wdt_time);
}
static void bcm47xx_wdt_start(void)
{
bcm47xx_wdt_pet();
bcm47xx_timer_tick(0);
}
static void bcm47xx_wdt_pause(void)
{
del_timer_sync(&wdt_timer);
bcm47xx_wdt_hw_stop();
}
static void bcm47xx_wdt_stop(void)
{
bcm47xx_wdt_pause();
}
static int bcm47xx_wdt_settimeout(int new_time)
{
if ((new_time <= 0) || (new_time > WDT_MAX_TIME))
return -EINVAL;
wdt_time = new_time;
return 0;
}
static int bcm47xx_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &bcm47xx_wdt_busy))
return -EBUSY;
bcm47xx_wdt_start();
return nonseekable_open(inode, file);
}
static int bcm47xx_wdt_release(struct inode *inode, struct file *file)
{
if (expect_release == 42) {
bcm47xx_wdt_stop();
} else {
printk(KERN_CRIT DRV_NAME
": Unexpected close, not stopping watchdog!\n");
bcm47xx_wdt_start();
}
clear_bit(0, &bcm47xx_wdt_busy);
expect_release = 0;
return 0;
}
static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;
expect_release = 0;
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_release = 42;
}
}
bcm47xx_wdt_pet();
}
return len;
}
static struct watchdog_info bcm47xx_wdt_info = {
.identity = DRV_NAME,
.options = WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
};
static long bcm47xx_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_value, retval = -EINVAL;;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &bcm47xx_wdt_info,
sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_SETOPTIONS:
if (get_user(new_value, p))
return -EFAULT;
if (new_value & WDIOS_DISABLECARD) {
bcm47xx_wdt_stop();
retval = 0;
}
if (new_value & WDIOS_ENABLECARD) {
bcm47xx_wdt_start();
retval = 0;
}
return retval;
case WDIOC_KEEPALIVE:
bcm47xx_wdt_pet();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
if (bcm47xx_wdt_settimeout(new_value))
return -EINVAL;
bcm47xx_wdt_pet();
case WDIOC_GETTIMEOUT:
return put_user(wdt_time, p);
default:
return -ENOTTY;
}
}
static int bcm47xx_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
bcm47xx_wdt_stop();
return NOTIFY_DONE;
}
static const struct file_operations bcm47xx_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = bcm47xx_wdt_ioctl,
.open = bcm47xx_wdt_open,
.release = bcm47xx_wdt_release,
.write = bcm47xx_wdt_write,
};
static struct miscdevice bcm47xx_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &bcm47xx_wdt_fops,
};
static struct notifier_block bcm47xx_wdt_notifier = {
.notifier_call = bcm47xx_wdt_notify_sys,
};
static int __init bcm47xx_wdt_init(void)
{
int ret;
if (bcm47xx_wdt_hw_stop() < 0)
return -ENODEV;
setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L);
if (bcm47xx_wdt_settimeout(wdt_time)) {
bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME);
printk(KERN_INFO DRV_NAME ": "
"wdt_time value must be 0 < wdt_time < %d, using %d\n",
(WDT_MAX_TIME + 1), wdt_time);
}
ret = register_reboot_notifier(&bcm47xx_wdt_notifier);
if (ret)
return ret;
ret = misc_register(&bcm47xx_wdt_miscdev);
if (ret) {
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
return ret;
}
printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n",
wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
static void __exit bcm47xx_wdt_exit(void)
{
if (!nowayout)
bcm47xx_wdt_stop();
misc_deregister(&bcm47xx_wdt_miscdev);
unregister_reboot_notifier(&bcm47xx_wdt_notifier);
}
module_init(bcm47xx_wdt_init);
module_exit(bcm47xx_wdt_exit);
MODULE_AUTHOR("Aleksandar Radovanovic");
MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/*
* coh901327_wdt.c
*
* Copyright (C) 2008-2009 ST-Ericsson AB
* License terms: GNU General Public License (GPL) version 2
* Watchdog driver for the ST-Ericsson AB COH 901 327 IP core
* Author: Linus Walleij <linus.walleij@stericsson.com>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/interrupt.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitops.h>
#include <linux/uaccess.h>
#include <linux/clk.h>
#define DRV_NAME "WDOG COH 901 327"
/*
* COH 901 327 register definitions
*/
/* WDOG_FEED Register 32bit (-/W) */
#define U300_WDOG_FR 0x00
#define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU
/* WDOG_TIMEOUT Register 32bit (R/W) */
#define U300_WDOG_TR 0x04
#define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU
/* WDOG_DISABLE1 Register 32bit (-/W) */
#define U300_WDOG_D1R 0x08
#define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU
/* WDOG_DISABLE2 Register 32bit (R/W) */
#define U300_WDOG_D2R 0x0C
#define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU
#define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU
#define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U
/* WDOG_STATUS Register 32bit (R/W) */
#define U300_WDOG_SR 0x10
#define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U
#define U300_WDOG_SR_STATUS_NORMAL 0x0000U
#define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U
/* WDOG_COUNT Register 32bit (R/-) */
#define U300_WDOG_CR 0x14
#define U300_WDOG_CR_VALID_IND 0x8000U
#define U300_WDOG_CR_VALID_STABLE 0x0000U
#define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU
/* WDOG_JTAGOVR Register 32bit (R/W) */
#define U300_WDOG_JOR 0x18
#define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U
#define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U
/* WDOG_RESTART Register 32bit (-/W) */
#define U300_WDOG_RR 0x1C
#define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU
/* WDOG_IRQ_EVENT Register 32bit (R/W) */
#define U300_WDOG_IER 0x20
#define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U
#define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U
/* WDOG_IRQ_MASK Register 32bit (R/W) */
#define U300_WDOG_IMR 0x24
#define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U
/* WDOG_IRQ_FORCE Register 32bit (R/W) */
#define U300_WDOG_IFR 0x28
#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U
/* Default timeout in seconds = 1 minute */
static int margin = 60;
static resource_size_t phybase;
static resource_size_t physize;
static int irq;
static void __iomem *virtbase;
static unsigned long coh901327_users;
static unsigned long boot_status;
static u16 wdogenablestore;
static u16 irqmaskstore;
static struct device *parent;
/*
* The watchdog block is of course always clocked, the
* clk_enable()/clk_disable() calls are mainly for performing reference
* counting higher up in the clock hierarchy.
*/
static struct clk *clk;
/*
* Enabling and disabling functions.
*/
static void coh901327_enable(u16 timeout)
{
u16 val;
clk_enable(clk);
/* Restart timer if it is disabled */
val = readw(virtbase + U300_WDOG_D2R);
if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
virtbase + U300_WDOG_RR);
/* Acknowledge any pending interrupt so it doesn't just fire off */
writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
virtbase + U300_WDOG_IER);
/* Enable the watchdog interrupt */
writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR);
/* Activate the watchdog timer */
writew(timeout, virtbase + U300_WDOG_TR);
/* Start the watchdog timer */
writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR);
/*
* Extra read so that this change propagate in the watchdog.
*/
(void) readw(virtbase + U300_WDOG_CR);
val = readw(virtbase + U300_WDOG_D2R);
clk_disable(clk);
if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
dev_err(parent,
"%s(): watchdog not enabled! D2R value %04x\n",
__func__, val);
}
static void coh901327_disable(void)
{
u16 val;
clk_enable(clk);
/* Disable the watchdog interrupt if it is active */
writew(0x0000U, virtbase + U300_WDOG_IMR);
/* If the watchdog is currently enabled, attempt to disable it */
val = readw(virtbase + U300_WDOG_D2R);
if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) {
writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER,
virtbase + U300_WDOG_D1R);
writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
virtbase + U300_WDOG_D2R);
/* Write this twice (else problems occur) */
writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER,
virtbase + U300_WDOG_D2R);
}
val = readw(virtbase + U300_WDOG_D2R);
clk_disable(clk);
if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED)
dev_err(parent,
"%s(): watchdog not disabled! D2R value %04x\n",
__func__, val);
}
static void coh901327_start(void)
{
coh901327_enable(margin * 100);
}
static void coh901327_keepalive(void)
{
clk_enable(clk);
/* Feed the watchdog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
clk_disable(clk);
}
static int coh901327_settimeout(int time)
{
/*
* Max margin is 327 since the 10ms
* timeout register is max
* 0x7FFF = 327670ms ~= 327s.
*/
if (time <= 0 || time > 327)
return -EINVAL;
margin = time;
clk_enable(clk);
/* Set new timeout value */
writew(margin * 100, virtbase + U300_WDOG_TR);
/* Feed the dog */
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
clk_disable(clk);
return 0;
}
/*
* This interrupt occurs 10 ms before the watchdog WILL bark.
*/
static irqreturn_t coh901327_interrupt(int irq, void *data)
{
u16 val;
/*
* Ack IRQ? If this occurs we're FUBAR anyway, so
* just acknowledge, disable the interrupt and await the imminent end.
* If you at some point need a host of callbacks to be called
* when the system is about to watchdog-reset, add them here!
*
* NOTE: on future versions of this IP-block, it will be possible
* to prevent a watchdog reset by feeding the watchdog at this
* point.
*/
clk_enable(clk);
val = readw(virtbase + U300_WDOG_IER);
if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND)
writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE,
virtbase + U300_WDOG_IER);
writew(0x0000U, virtbase + U300_WDOG_IMR);
clk_disable(clk);
dev_crit(parent, "watchdog is barking!\n");
return IRQ_HANDLED;
}
/*
* Allow only one user (daemon) to open the watchdog
*/
static int coh901327_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(1, &coh901327_users))
return -EBUSY;
coh901327_start();
return nonseekable_open(inode, file);
}
static int coh901327_release(struct inode *inode, struct file *file)
{
clear_bit(1, &coh901327_users);
coh901327_disable();
return 0;
}
static ssize_t coh901327_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (len)
coh901327_keepalive();
return len;
}
static long coh901327_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int ret = -ENOTTY;
u16 val;
int time;
int new_options;
union {
struct watchdog_info __user *ident;
int __user *i;
} uarg;
static struct watchdog_info ident = {
.options = WDIOF_CARDRESET |
WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
.identity = "COH 901 327 Watchdog",
.firmware_version = 1,
};
uarg.i = (int __user *)arg;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user(uarg.ident, &ident,
sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
ret = put_user(0, uarg.i);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, uarg.i);
break;
case WDIOC_SETOPTIONS:
ret = get_user(new_options, uarg.i);
if (ret)
break;
if (new_options & WDIOS_DISABLECARD)
coh901327_disable();
if (new_options & WDIOS_ENABLECARD)
coh901327_start();
ret = 0;
break;
case WDIOC_KEEPALIVE:
coh901327_keepalive();
ret = 0;
break;
case WDIOC_SETTIMEOUT:
ret = get_user(time, uarg.i);
if (ret)
break;
ret = coh901327_settimeout(time);
if (ret)
break;
/* Then fall through to return set value */
case WDIOC_GETTIMEOUT:
ret = put_user(margin, uarg.i);
break;
case WDIOC_GETTIMELEFT:
clk_enable(clk);
/* Read repeatedly until the value is stable! */
val = readw(virtbase + U300_WDOG_CR);
while (val & U300_WDOG_CR_VALID_IND)
val = readw(virtbase + U300_WDOG_CR);
val &= U300_WDOG_CR_COUNT_VALUE_MASK;
clk_disable(clk);
if (val != 0)
val /= 100;
ret = put_user(val, uarg.i);
break;
}
return ret;
}
static const struct file_operations coh901327_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = coh901327_write,
.unlocked_ioctl = coh901327_ioctl,
.open = coh901327_open,
.release = coh901327_release,
};
static struct miscdevice coh901327_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &coh901327_fops,
};
static int __exit coh901327_remove(struct platform_device *pdev)
{
misc_deregister(&coh901327_miscdev);
coh901327_disable();
free_irq(irq, pdev);
clk_put(clk);
iounmap(virtbase);
release_mem_region(phybase, physize);
return 0;
}
static int __init coh901327_probe(struct platform_device *pdev)
{
int ret;
u16 val;
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
parent = &pdev->dev;
physize = resource_size(res);
phybase = res->start;
if (request_mem_region(phybase, physize, DRV_NAME) == NULL) {
ret = -EBUSY;
goto out;
}
virtbase = ioremap(phybase, physize);
if (!virtbase) {
ret = -ENOMEM;
goto out_no_remap;
}
clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
dev_err(&pdev->dev, "could not get clock\n");
goto out_no_clk;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "could not enable clock\n");
goto out_no_clk_enable;
}
val = readw(virtbase + U300_WDOG_SR);
switch (val) {
case U300_WDOG_SR_STATUS_TIMED_OUT:
dev_info(&pdev->dev,
"watchdog timed out since last chip reset!\n");
boot_status = WDIOF_CARDRESET;
/* Status will be cleared below */
break;
case U300_WDOG_SR_STATUS_NORMAL:
dev_info(&pdev->dev,
"in normal status, no timeouts have occurred.\n");
break;
default:
dev_info(&pdev->dev,
"contains an illegal status code (%08x)\n", val);
break;
}
val = readw(virtbase + U300_WDOG_D2R);
switch (val) {
case U300_WDOG_D2R_DISABLE_STATUS_DISABLED:
dev_info(&pdev->dev, "currently disabled.\n");
break;
case U300_WDOG_D2R_DISABLE_STATUS_ENABLED:
dev_info(&pdev->dev,
"currently enabled! (disabling it now)\n");
coh901327_disable();
break;
default:
dev_err(&pdev->dev,
"contains an illegal enable/disable code (%08x)\n",
val);
break;
}
/* Reset the watchdog */
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,
DRV_NAME " Bark", pdev)) {
ret = -EIO;
goto out_no_irq;
}
clk_disable(clk);
ret = misc_register(&coh901327_miscdev);
if (ret == 0)
dev_info(&pdev->dev,
"initialized. timer margin=%d sec\n", margin);
else
goto out_no_wdog;
return 0;
out_no_wdog:
free_irq(irq, pdev);
out_no_irq:
clk_disable(clk);
out_no_clk_enable:
clk_put(clk);
out_no_clk:
iounmap(virtbase);
out_no_remap:
release_mem_region(phybase, SZ_4K);
out:
return ret;
}
#ifdef CONFIG_PM
static int coh901327_suspend(struct platform_device *pdev, pm_message_t state)
{
irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U;
wdogenablestore = readw(virtbase + U300_WDOG_D2R);
/* If watchdog is on, disable it here and now */
if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED)
coh901327_disable();
return 0;
}
static int coh901327_resume(struct platform_device *pdev)
{
/* Restore the watchdog interrupt */
writew(irqmaskstore, virtbase + U300_WDOG_IMR);
if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) {
/* Restart the watchdog timer */
writew(U300_WDOG_RR_RESTART_VALUE_RESUME,
virtbase + U300_WDOG_RR);
writew(U300_WDOG_FR_FEED_RESTART_TIMER,
virtbase + U300_WDOG_FR);
}
return 0;
}
#else
#define coh901327_suspend NULL
#define coh901327_resume NULL
#endif
/*
* Mistreating the watchdog is the only way to perform a software reset of the
* system on EMP platforms. So we implement this and export a symbol for it.
*/
void coh901327_watchdog_reset(void)
{
/* Enable even if on JTAG too */
writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE,
virtbase + U300_WDOG_JOR);
/*
* Timeout = 5s, we have to wait for the watchdog reset to
* actually take place: the watchdog will be reloaded with the
* default value immediately, so we HAVE to reboot and get back
* into the kernel in 30s, or the device will reboot again!
* The boot loader will typically deactivate the watchdog, so we
* need time enough for the boot loader to get to the point of
* deactivating the watchdog before it is shut down by it.
*
* NOTE: on future versions of the watchdog, this restriction is
* gone: the watchdog will be reloaded with a defaul value (1 min)
* instead of last value, and you can conveniently set the watchdog
* timeout to 10ms (value = 1) without any problems.
*/
coh901327_enable(500);
/* Return and await doom */
}
static struct platform_driver coh901327_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "coh901327_wdog",
},
.remove = __exit_p(coh901327_remove),
.suspend = coh901327_suspend,
.resume = coh901327_resume,
};
static int __init coh901327_init(void)
{
return platform_driver_probe(&coh901327_driver, coh901327_probe);
}
module_init(coh901327_init);
static void __exit coh901327_exit(void)
{
platform_driver_unregister(&coh901327_driver);
}
module_exit(coh901327_exit);
MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>");
MODULE_DESCRIPTION("COH 901 327 Watchdog");
module_param(margin, int, 0);
MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
......@@ -120,7 +120,8 @@ static int nowayout = WATCHDOG_NOWAYOUT;
static char expect_release;
static unsigned long hpwdt_is_open;
static unsigned int allow_kdump;
static int hpwdt_nmi_sourcing;
static unsigned int hpwdt_nmi_sourcing;
static unsigned int priority; /* hpwdt at end of die_notify list */
static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_timer_reg;
......@@ -623,7 +624,7 @@ static struct miscdevice hpwdt_miscdev = {
static struct notifier_block die_notifier = {
.notifier_call = hpwdt_pretimeout,
.priority = 0x7FFFFFFF,
.priority = 0,
};
/*
......@@ -641,7 +642,8 @@ static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev)
hpwdt_nmi_sourcing = 1;
else
dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this "
"functionality you must reboot with nmi_watchdog=0.\n");
"functionality you must reboot with nmi_watchdog=0 "
"and load the hpwdt driver with priority=1.\n");
}
#else
static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev)
......@@ -714,6 +716,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
cmn_regs.u1.rah = 0x0D;
cmn_regs.u1.ral = 0x02;
/*
* If the priority is set to 1, then we will be put first on the
* die notify list to handle a critical NMI. The default is to
* be last so other users of the NMI signal can function.
*/
if (priority)
die_notifier.priority = 0x7FFFFFFF;
retval = register_die_notifier(&die_notifier);
if (retval != 0) {
dev_warn(&dev->dev,
......@@ -733,9 +743,11 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
printk(KERN_INFO
"hp Watchdog Timer Driver: %s"
", timer margin: %d seconds (nowayout=%d)"
", allow kernel dump: %s (default = 0/OFF).\n",
", allow kernel dump: %s (default = 0/OFF)"
", priority: %s (default = 0/LAST).\n",
HPWDT_VERSION, soft_margin, nowayout,
(allow_kdump == 0) ? "OFF" : "ON");
(allow_kdump == 0) ? "OFF" : "ON",
(priority == 0) ? "LAST" : "FIRST");
return 0;
......@@ -798,5 +810,9 @@ module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
module_param(priority, int, 0);
MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last"
" (default = 0/Last)\n");
module_init(hpwdt_init);
module_exit(hpwdt_cleanup);
......@@ -159,6 +159,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file)
file->private_data = (void *) wdev;
omap_wdt_set_timeout(wdev);
omap_wdt_ping(wdev); /* trigger loading of new timeout value */
omap_wdt_enable(wdev);
return nonseekable_open(inode, file);
......@@ -313,6 +314,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, wdev);
clk_enable(wdev->ick);
clk_enable(wdev->fck);
omap_wdt_disable(wdev);
omap_wdt_adjust_timeout(timer_margin);
......@@ -332,6 +336,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev)
/* autogate OCP interface clock */
__raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG);
clk_disable(wdev->ick);
clk_disable(wdev->fck);
omap_wdt_dev = pdev;
return 0;
......
/*
* PNX833x Hardware Watchdog Driver
* Copyright 2008 NXP Semiconductors
* Daniel Laird <daniel.j.laird@nxp.com>
* Andre McCurdy <andre.mccurdy@nxp.com>
*
* Heavily based upon - IndyDog 0.3
* A Hardware Watchdog Device for SGI IP22
*
* (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
*
* 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.
*
* based on softdog.c by Alan Cox <alan@redhat.com>
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/mach-pnx833x/pnx833x.h>
#define PFX "pnx833x: "
#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */
#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */
/** CONFIG block */
#define PNX833X_CONFIG (0x07000U)
#define PNX833X_CONFIG_CPU_WATCHDOG (0x54)
#define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58)
#define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c)
/** RESET block */
#define PNX833X_RESET (0x08000U)
#define PNX833X_RESET_CONFIG (0x08)
static int pnx833x_wdt_alive;
/* Set default timeout in MHZ.*/
static int pnx833x_wdt_timeout = (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY);
module_param(pnx833x_wdt_timeout, int, 0);
MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default="
__MODULE_STRING(pnx833x_wdt_timeout) "(30 seconds).");
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 int start_enabled = 1;
module_param(start_enabled, int, 0);
MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion "
"(default=" __MODULE_STRING(start_enabled) ")");
static void pnx833x_wdt_start(void)
{
/* Enable watchdog causing reset. */
PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1;
/* Set timeout.*/
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
/* Enable watchdog. */
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1;
printk(KERN_INFO PFX "Started watchdog timer.\n");
}
static void pnx833x_wdt_stop(void)
{
/* Disable watchdog causing reset. */
PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE;
/* Disable watchdog.*/
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE;
printk(KERN_INFO PFX "Stopped watchdog timer.\n");
}
static void pnx833x_wdt_ping(void)
{
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout;
}
/*
* Allow only one person to hold it open
*/
static int pnx833x_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(0, &pnx833x_wdt_alive))
return -EBUSY;
if (nowayout)
__module_get(THIS_MODULE);
/* Activate timer */
if (!start_enabled)
pnx833x_wdt_start();
pnx833x_wdt_ping();
printk(KERN_INFO "Started watchdog timer.\n");
return nonseekable_open(inode, file);
}
static int pnx833x_wdt_release(struct inode *inode, struct file *file)
{
/* Shut off the timer.
* Lock it in if it's a module and we defined ...NOWAYOUT */
if (!nowayout)
pnx833x_wdt_stop(); /* Turn the WDT off */
clear_bit(0, &pnx833x_wdt_alive);
return 0;
}
static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
{
/* Refresh the timer. */
if (len)
pnx833x_wdt_ping();
return len;
}
static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int options, new_timeout = 0;
uint32_t timeout, timeout_left = 0;
static struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT,
.firmware_version = 0,
.identity = "Hardware Watchdog for PNX833x",
};
switch (cmd) {
default:
return -ENOTTY;
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg,
&ident, sizeof(ident)))
return -EFAULT;
return 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *)arg);
case WDIOC_SETOPTIONS:
if (get_user(options, (int *)arg))
return -EFAULT;
if (options & WDIOS_DISABLECARD)
pnx833x_wdt_stop();
if (options & WDIOS_ENABLECARD)
pnx833x_wdt_start();
return 0;
case WDIOC_KEEPALIVE:
pnx833x_wdt_ping();
return 0;
case WDIOC_SETTIMEOUT:
{
if (get_user(new_timeout, (int *)arg))
return -EFAULT;
pnx833x_wdt_timeout = new_timeout;
PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout;
return put_user(new_timeout, (int *)arg);
}
case WDIOC_GETTIMEOUT:
timeout = PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_WATCHDOG_COMPARE);
return put_user(timeout, (int *)arg);
case WDIOC_GETTIMELEFT:
timeout_left = PNX833X_REG(PNX833X_CONFIG +
PNX833X_CONFIG_CPU_WATCHDOG);
return put_user(timeout_left, (int *)arg);
}
}
static int pnx833x_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
pnx833x_wdt_stop(); /* Turn the WDT off */
return NOTIFY_DONE;
}
static const struct file_operations pnx833x_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = pnx833x_wdt_write,
.unlocked_ioctl = pnx833x_wdt_ioctl,
.open = pnx833x_wdt_open,
.release = pnx833x_wdt_release,
};
static struct miscdevice pnx833x_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &pnx833x_wdt_fops,
};
static struct notifier_block pnx833x_wdt_notifier = {
.notifier_call = pnx833x_wdt_notify_sys,
};
static char banner[] __initdata =
KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n";
static int __init watchdog_init(void)
{
int ret, cause;
/* Lets check the reason for the reset.*/
cause = PNX833X_REG(PNX833X_RESET);
/*If bit 31 is set then watchdog was cause of reset.*/
if (cause & 0x80000000) {
printk(KERN_INFO PFX "The system was previously reset due to "
"the watchdog firing - please investigate...\n");
}
ret = register_reboot_notifier(&pnx833x_wdt_notifier);
if (ret) {
printk(KERN_ERR PFX
"cannot register reboot notifier (err=%d)\n", ret);
return ret;
}
ret = misc_register(&pnx833x_wdt_miscdev);
if (ret) {
printk(KERN_ERR PFX
"cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&pnx833x_wdt_notifier);
return ret;
}
printk(banner);
if (start_enabled)
pnx833x_wdt_start();
return 0;
}
static void __exit watchdog_exit(void)
{
misc_deregister(&pnx833x_wdt_miscdev);
unregister_reboot_notifier(&pnx833x_wdt_notifier);
}
module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_AUTHOR("Daniel Laird/Andre McCurdy");
MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/*
* Watchdog driver for Freescale STMP37XX/STMP378X
*
* Author: Vitaly Wool <vital@embeddedalley.com>
*
* Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <mach/platform.h>
#include <mach/regs-rtc.h>
#define DEFAULT_HEARTBEAT 19
#define MAX_HEARTBEAT (0x10000000 >> 6)
/* missing bitmask in headers */
#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */
static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock);
static unsigned long wdt_status;
static const int nowayout = WATCHDOG_NOWAYOUT;
static int heartbeat = DEFAULT_HEARTBEAT;
static unsigned long boot_status;
static void wdt_enable(u32 value)
{
spin_lock(&stmp3xxx_wdt_io_lock);
__raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG);
stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
spin_unlock(&stmp3xxx_wdt_io_lock);
}
static void wdt_disable(void)
{
spin_lock(&stmp3xxx_wdt_io_lock);
stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL);
spin_unlock(&stmp3xxx_wdt_io_lock);
}
static void wdt_ping(void)
{
wdt_enable(heartbeat * WDOG_COUNTER_RATE);
}
static int stmp3xxx_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_ping();
return nonseekable_open(inode, file);
}
static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (len) {
if (!nowayout) {
size_t i;
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);
}
}
wdt_ping();
}
return len;
}
static struct watchdog_info ident = {
.options = WDIOF_CARDRESET |
WDIOF_MAGICCLOSE |
WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING,
.identity = "STMP3XXX Watchdog",
};
static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_heartbeat, opts;
int ret = -ENOTTY;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
ret = put_user(0, p);
break;
case WDIOC_GETBOOTSTATUS:
ret = put_user(boot_status, p);
break;
case WDIOC_SETOPTIONS:
if (get_user(opts, p)) {
ret = -EFAULT;
break;
}
if (opts & WDIOS_DISABLECARD)
wdt_disable();
else if (opts & WDIOS_ENABLECARD)
wdt_ping();
else {
pr_debug("%s: unknown option 0x%x\n", __func__, opts);
ret = -EINVAL;
break;
}
ret = 0;
break;
case WDIOC_KEEPALIVE:
wdt_ping();
ret = 0;
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_heartbeat, p)) {
ret = -EFAULT;
break;
}
if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) {
ret = -EINVAL;
break;
}
heartbeat = new_heartbeat;
wdt_ping();
/* Fall through */
case WDIOC_GETTIMEOUT:
ret = put_user(heartbeat, p);
break;
}
return ret;
}
static int stmp3xxx_wdt_release(struct inode *inode, struct file *file)
{
int ret = 0;
if (!nowayout) {
if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) {
wdt_ping();
pr_debug("%s: Device closed unexpectdly\n", __func__);
ret = -EINVAL;
} else {
wdt_disable();
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
}
}
clear_bit(WDT_IN_USE, &wdt_status);
return ret;
}
static const struct file_operations stmp3xxx_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = stmp3xxx_wdt_write,
.unlocked_ioctl = stmp3xxx_wdt_ioctl,
.open = stmp3xxx_wdt_open,
.release = stmp3xxx_wdt_release,
};
static struct miscdevice stmp3xxx_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &stmp3xxx_wdt_fops,
};
static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev)
{
int ret = 0;
if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
heartbeat = DEFAULT_HEARTBEAT;
boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) &
BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER;
boot_status = !!boot_status;
stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER,
REGS_RTC_BASE + HW_RTC_PERSISTENT1);
wdt_disable(); /* disable for now */
ret = misc_register(&stmp3xxx_wdt_miscdev);
if (ret < 0) {
dev_err(&pdev->dev, "cannot register misc device\n");
return ret;
}
printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n",
heartbeat);
return ret;
}
static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev)
{
misc_deregister(&stmp3xxx_wdt_miscdev);
return 0;
}
#ifdef CONFIG_PM
static int wdt_suspended;
static u32 wdt_saved_time;
static int stmp3xxx_wdt_suspend(struct platform_device *pdev,
pm_message_t state)
{
if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) &
BM_RTC_CTRL_WATCHDOGEN) {
wdt_suspended = 1;
wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG);
wdt_disable();
}
return 0;
}
static int stmp3xxx_wdt_resume(struct platform_device *pdev)
{
if (wdt_suspended) {
wdt_enable(wdt_saved_time);
wdt_suspended = 0;
}
return 0;
}
#else
#define stmp3xxx_wdt_suspend NULL
#define stmp3xxx_wdt_resume NULL
#endif
static struct platform_driver platform_wdt_driver = {
.driver = {
.name = "stmp3xxx_wdt",
},
.probe = stmp3xxx_wdt_probe,
.remove = __devexit_p(stmp3xxx_wdt_remove),
.suspend = stmp3xxx_wdt_suspend,
.resume = stmp3xxx_wdt_resume,
};
static int __init stmp3xxx_wdt_init(void)
{
return platform_driver_register(&platform_wdt_driver);
}
static void __exit stmp3xxx_wdt_exit(void)
{
return platform_driver_unregister(&platform_wdt_driver);
}
module_init(stmp3xxx_wdt_init);
module_exit(stmp3xxx_wdt_exit);
MODULE_DESCRIPTION("STMP3XXX Watchdog Driver");
MODULE_LICENSE("GPL");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat period in seconds from 1 to "
__MODULE_STRING(MAX_HEARTBEAT) ", default "
__MODULE_STRING(DEFAULT_HEARTBEAT));
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
/*
* Copyright (C) Nokia Corporation
*
* Written by Timo Kokkonen <timo.t.kokkonen at nokia.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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/watchdog.h>
#include <linux/platform_device.h>
#include <linux/miscdevice.h>
#include <linux/uaccess.h>
#include <linux/i2c/twl4030.h>
#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3
#define TWL4030_WDT_STATE_OPEN 0x1
#define TWL4030_WDT_STATE_ACTIVE 0x8
static struct platform_device *twl4030_wdt_dev;
struct twl4030_wdt {
struct miscdevice miscdev;
int timer_margin;
unsigned long state;
};
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 int twl4030_wdt_write(unsigned char val)
{
return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val,
TWL4030_WATCHDOG_CFG_REG_OFFS);
}
static int twl4030_wdt_enable(struct twl4030_wdt *wdt)
{
return twl4030_wdt_write(wdt->timer_margin + 1);
}
static int twl4030_wdt_disable(struct twl4030_wdt *wdt)
{
return twl4030_wdt_write(0);
}
static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout)
{
if (timeout < 0 || timeout > 30) {
dev_warn(wdt->miscdev.parent,
"Timeout can only be in the range [0-30] seconds");
return -EINVAL;
}
wdt->timer_margin = timeout;
return twl4030_wdt_enable(wdt);
}
static ssize_t twl4030_wdt_write_fop(struct file *file,
const char __user *data, size_t len, loff_t *ppos)
{
struct twl4030_wdt *wdt = file->private_data;
if (len)
twl4030_wdt_enable(wdt);
return len;
}
static long twl4030_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_margin;
struct twl4030_wdt *wdt = file->private_data;
static const struct watchdog_info twl4030_wd_ident = {
.identity = "TWL4030 Watchdog",
.options = WDIOF_SETTIMEOUT,
.firmware_version = 0,
};
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &twl4030_wd_ident,
sizeof(twl4030_wd_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
twl4030_wdt_enable(wdt);
break;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, p))
return -EFAULT;
if (twl4030_wdt_set_timeout(wdt, new_margin))
return -EINVAL;
return put_user(wdt->timer_margin, p);
case WDIOC_GETTIMEOUT:
return put_user(wdt->timer_margin, p);
default:
return -ENOTTY;
}
return 0;
}
static int twl4030_wdt_open(struct inode *inode, struct file *file)
{
struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev);
/* /dev/watchdog can only be opened once */
if (test_and_set_bit(0, &wdt->state))
return -EBUSY;
wdt->state |= TWL4030_WDT_STATE_ACTIVE;
file->private_data = (void *) wdt;
twl4030_wdt_enable(wdt);
return nonseekable_open(inode, file);
}
static int twl4030_wdt_release(struct inode *inode, struct file *file)
{
struct twl4030_wdt *wdt = file->private_data;
if (nowayout) {
dev_alert(wdt->miscdev.parent,
"Unexpected close, watchdog still running!\n");
twl4030_wdt_enable(wdt);
} else {
if (twl4030_wdt_disable(wdt))
return -EFAULT;
wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
}
clear_bit(0, &wdt->state);
return 0;
}
static const struct file_operations twl4030_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = twl4030_wdt_open,
.release = twl4030_wdt_release,
.unlocked_ioctl = twl4030_wdt_ioctl,
.write = twl4030_wdt_write_fop,
};
static int __devinit twl4030_wdt_probe(struct platform_device *pdev)
{
int ret = 0;
struct twl4030_wdt *wdt;
wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
wdt->state = 0;
wdt->timer_margin = 30;
wdt->miscdev.parent = &pdev->dev;
wdt->miscdev.fops = &twl4030_wdt_fops;
wdt->miscdev.minor = WATCHDOG_MINOR;
wdt->miscdev.name = "watchdog";
platform_set_drvdata(pdev, wdt);
twl4030_wdt_dev = pdev;
ret = misc_register(&wdt->miscdev);
if (ret) {
dev_err(wdt->miscdev.parent,
"Failed to register misc device\n");
platform_set_drvdata(pdev, NULL);
kfree(wdt);
twl4030_wdt_dev = NULL;
return ret;
}
return 0;
}
static int __devexit twl4030_wdt_remove(struct platform_device *pdev)
{
struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
if (twl4030_wdt_disable(wdt))
return -EFAULT;
wdt->state &= ~TWL4030_WDT_STATE_ACTIVE;
misc_deregister(&wdt->miscdev);
platform_set_drvdata(pdev, NULL);
kfree(wdt);
twl4030_wdt_dev = NULL;
return 0;
}
#ifdef CONFIG_PM
static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state)
{
struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
return twl4030_wdt_disable(wdt);
return 0;
}
static int twl4030_wdt_resume(struct platform_device *pdev)
{
struct twl4030_wdt *wdt = platform_get_drvdata(pdev);
if (wdt->state & TWL4030_WDT_STATE_ACTIVE)
return twl4030_wdt_enable(wdt);
return 0;
}
#else
#define twl4030_wdt_suspend NULL
#define twl4030_wdt_resume NULL
#endif
static struct platform_driver twl4030_wdt_driver = {
.probe = twl4030_wdt_probe,
.remove = __devexit_p(twl4030_wdt_remove),
.suspend = twl4030_wdt_suspend,
.resume = twl4030_wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = "twl4030_wdt",
},
};
static int __devinit twl4030_wdt_init(void)
{
return platform_driver_register(&twl4030_wdt_driver);
}
module_init(twl4030_wdt_init);
static void __devexit twl4030_wdt_exit(void)
{
platform_driver_unregister(&twl4030_wdt_driver);
}
module_exit(twl4030_wdt_exit);
MODULE_AUTHOR("Nokia Corporation");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:twl4030_wdt");
......@@ -2,7 +2,7 @@
* Industrial Computer Source PCI-WDT500/501 driver
*
* (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>,
* All Rights Reserved.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -99,14 +99,16 @@ MODULE_PARM_DESC(nowayout,
"Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
#ifdef CONFIG_WDT_501_PCI
/* Support for the Fan Tachometer on the PCI-WDT501 */
static int tachometer;
module_param(tachometer, int, 0);
MODULE_PARM_DESC(tachometer,
"PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
#endif /* CONFIG_WDT_501_PCI */
"PCI-WDT501 Fan Tachometer support (0=disable, default=0)");
static int type = 500;
module_param(type, int, 0);
MODULE_PARM_DESC(type,
"PCI-WDT501 Card type (500 or 501 , default=500)");
/*
* Programming support
......@@ -266,22 +268,21 @@ static int wdtpci_get_status(int *status)
*status |= WDIOF_EXTERN1;
if (new_status & WDC_SR_ISII1)
*status |= WDIOF_EXTERN2;
#ifdef CONFIG_WDT_501_PCI
if (!(new_status & WDC_SR_TGOOD))
*status |= WDIOF_OVERHEAT;
if (!(new_status & WDC_SR_PSUOVER))
*status |= WDIOF_POWEROVER;
if (!(new_status & WDC_SR_PSUUNDR))
*status |= WDIOF_POWERUNDER;
if (tachometer) {
if (!(new_status & WDC_SR_FANGOOD))
*status |= WDIOF_FANFAULT;
if (type == 501) {
if (!(new_status & WDC_SR_TGOOD))
*status |= WDIOF_OVERHEAT;
if (!(new_status & WDC_SR_PSUOVER))
*status |= WDIOF_POWEROVER;
if (!(new_status & WDC_SR_PSUUNDR))
*status |= WDIOF_POWERUNDER;
if (tachometer) {
if (!(new_status & WDC_SR_FANGOOD))
*status |= WDIOF_FANFAULT;
}
}
#endif /* CONFIG_WDT_501_PCI */
return 0;
}
#ifdef CONFIG_WDT_501_PCI
/**
* wdtpci_get_temperature:
*
......@@ -300,7 +301,6 @@ static int wdtpci_get_temperature(int *temperature)
*temperature = (c * 11 / 15) + 7;
return 0;
}
#endif /* CONFIG_WDT_501_PCI */
/**
* wdtpci_interrupt:
......@@ -327,22 +327,22 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
printk(KERN_CRIT PFX "status %d\n", status);
#ifdef CONFIG_WDT_501_PCI
if (!(status & WDC_SR_TGOOD)) {
u8 alarm = inb(WDT_RT);
printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", alarm);
udelay(8);
}
if (!(status & WDC_SR_PSUOVER))
printk(KERN_CRIT PFX "PSU over voltage.\n");
if (!(status & WDC_SR_PSUUNDR))
printk(KERN_CRIT PFX "PSU under voltage.\n");
if (tachometer) {
if (!(status & WDC_SR_FANGOOD))
printk(KERN_CRIT PFX "Possible fan fault.\n");
if (type == 501) {
if (!(status & WDC_SR_TGOOD)) {
printk(KERN_CRIT PFX "Overheat alarm.(%d)\n",
inb(WDT_RT));
udelay(8);
}
if (!(status & WDC_SR_PSUOVER))
printk(KERN_CRIT PFX "PSU over voltage.\n");
if (!(status & WDC_SR_PSUUNDR))
printk(KERN_CRIT PFX "PSU under voltage.\n");
if (tachometer) {
if (!(status & WDC_SR_FANGOOD))
printk(KERN_CRIT PFX "Possible fan fault.\n");
}
}
#endif /* CONFIG_WDT_501_PCI */
if (!(status&WDC_SR_WCCR)) {
if (!(status & WDC_SR_WCCR)) {
#ifdef SOFTWARE_REBOOT
#ifdef ONLY_TESTING
printk(KERN_CRIT PFX "Would Reboot.\n");
......@@ -371,12 +371,13 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id)
*/
static ssize_t wdtpci_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
size_t count, loff_t *ppos)
{
if (count) {
if (!nowayout) {
size_t i;
/* In case it was set long ago */
expect_close = 0;
for (i = 0; i != count; i++) {
......@@ -406,10 +407,10 @@ static ssize_t wdtpci_write(struct file *file, const char __user *buf,
static long wdtpci_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int new_heartbeat;
int status;
void __user *argp = (void __user *)arg;
int __user *p = argp;
int new_heartbeat;
int status;
static struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT|
......@@ -421,11 +422,12 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd,
/* Add options according to the card we have */
ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2);
#ifdef CONFIG_WDT_501_PCI
ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER);
if (tachometer)
ident.options |= WDIOF_FANFAULT;
#endif /* CONFIG_WDT_501_PCI */
if (type == 501) {
ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|
WDIOF_POWEROVER);
if (tachometer)
ident.options |= WDIOF_FANFAULT;
}
switch (cmd) {
case WDIOC_GETSUPPORT:
......@@ -503,7 +505,6 @@ static int wdtpci_release(struct inode *inode, struct file *file)
return 0;
}
#ifdef CONFIG_WDT_501_PCI
/**
* wdtpci_temp_read:
* @file: file handle to the watchdog board
......@@ -554,7 +555,6 @@ static int wdtpci_temp_release(struct inode *inode, struct file *file)
{
return 0;
}
#endif /* CONFIG_WDT_501_PCI */
/**
* notify_sys:
......@@ -596,7 +596,6 @@ static struct miscdevice wdtpci_miscdev = {
.fops = &wdtpci_fops,
};
#ifdef CONFIG_WDT_501_PCI
static const struct file_operations wdtpci_temp_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
......@@ -610,7 +609,6 @@ static struct miscdevice temp_miscdev = {
.name = "temperature",
.fops = &wdtpci_temp_fops,
};
#endif /* CONFIG_WDT_501_PCI */
/*
* The WDT card needs to learn about soft shutdowns in order to
......@@ -633,6 +631,11 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
return -ENODEV;
}
if (type != 500 && type != 501) {
printk(KERN_ERR PFX "unknown card type '%d'.\n", type);
return -ENODEV;
}
if (pci_enable_device(dev)) {
printk(KERN_ERR PFX "Not possible to enable PCI Device\n");
return -ENODEV;
......@@ -678,15 +681,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
goto out_irq;
}
#ifdef CONFIG_WDT_501_PCI
ret = misc_register(&temp_miscdev);
if (ret) {
printk(KERN_ERR PFX
if (type == 501) {
ret = misc_register(&temp_miscdev);
if (ret) {
printk(KERN_ERR PFX
"cannot register miscdev on minor=%d (err=%d)\n",
TEMP_MINOR, ret);
goto out_rbt;
TEMP_MINOR, ret);
goto out_rbt;
}
}
#endif /* CONFIG_WDT_501_PCI */
ret = misc_register(&wdtpci_miscdev);
if (ret) {
......@@ -698,20 +701,18 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev,
printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n",
heartbeat, nowayout);
#ifdef CONFIG_WDT_501_PCI
printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
if (type == 501)
printk(KERN_INFO "wdt: Fan Tachometer is %s\n",
(tachometer ? "Enabled" : "Disabled"));
#endif /* CONFIG_WDT_501_PCI */
ret = 0;
out:
return ret;
out_misc:
#ifdef CONFIG_WDT_501_PCI
misc_deregister(&temp_miscdev);
if (type == 501)
misc_deregister(&temp_miscdev);
out_rbt:
#endif /* CONFIG_WDT_501_PCI */
unregister_reboot_notifier(&wdtpci_notifier);
out_irq:
free_irq(irq, &wdtpci_miscdev);
......@@ -728,9 +729,8 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
/* here we assume only one device will ever have
* been picked up and registered by probe function */
misc_deregister(&wdtpci_miscdev);
#ifdef CONFIG_WDT_501_PCI
misc_deregister(&temp_miscdev);
#endif /* CONFIG_WDT_501_PCI */
if (type == 501)
misc_deregister(&temp_miscdev);
unregister_reboot_notifier(&wdtpci_notifier);
free_irq(irq, &wdtpci_miscdev);
release_region(io, 16);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册