Commit 2313bcdc authored by Linus Torvalds's avatar Linus Torvalds

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

* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-watchdog: (25 commits)
  watchdog: WatchDog Timer Driver Core - Add minimum and max timeout
  watchdog: WatchDog Timer Driver Core - Add ioctl call
  watchdog: WatchDog Timer Driver Core - Add nowayout feature
  watchdog: WatchDog Timer Driver Core - Add Magic Close feature
  watchdog: WatchDog Timer Driver Core - Add WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl
  watchdog: WatchDog Timer Driver Core - Add WDIOC_SETOPTIONS ioctl
  watchdog: WatchDog Timer Driver Core - Add WDIOC_KEEPALIVE ioctl
  watchdog: WatchDog Timer Driver Core - Add basic ioctl functionality
  watchdog: WatchDog Timer Driver Core - Add basic framework
  watchdog: hpwdt: add next gen HP servers
  watchdog: it8712f_wdt.c: improve includes
  watchdog: at91sam9/wdt: move register header to drivers
  watchdog: Add Xilinx watchdog timer driver
  watchdog: remove empty pm-functions
  watchdog: sp805: Flush posted writes in enable/disable.
  watchdog: sp805: Don't write 0 to the load value register.
  watchdog: imx2_wdt: add device tree probe support
  watchdog: s3c2410: Add support for device tree based probe
  watchdog: mpcore_wdt: Add suspend/resume support.
  watchdog: mtx1-wdt: use dev_{err,info} instead of printk()
  ...
parents 59ed2bb2 3f43f68e
* Freescale i.MX Watchdog Timer (WDT) Controller
Required properties:
- compatible : Should be "fsl,<soc>-wdt"
- reg : Should contain WDT registers location and length
- interrupts : Should contain WDT interrupt
Examples:
wdt@73f98000 {
compatible = "fsl,imx51-wdt", "fsl,imx21-wdt";
reg = <0x73f98000 0x4000>;
interrupts = <58>;
};
* Samsung's Watchdog Timer Controller
The Samsung's Watchdog controller is used for resuming system operation
after a preset amount of time during which the WDT reset event has not
occured.
Required properties:
- compatible : should be "samsung,s3c2410-wdt"
- reg : base physical address of the controller and length of memory mapped
region.
- interrupts : interrupt number to the cpu.
...@@ -8,6 +8,8 @@ src/ ...@@ -8,6 +8,8 @@ src/
- directory holding watchdog related example programs. - directory holding watchdog related example programs.
watchdog-api.txt watchdog-api.txt
- description of the Linux Watchdog driver API. - description of the Linux Watchdog driver API.
watchdog-kernel-api.txt
- description of the Linux WatchDog Timer Driver Core kernel API.
watchdog-parameters.txt watchdog-parameters.txt
- information on driver parameters (for drivers other than - information on driver parameters (for drivers other than
the ones that have driver-specific files here) the ones that have driver-specific files here)
......
The Linux WatchDog Timer Driver Core kernel API.
===============================================
Last reviewed: 22-Jul-2011
Wim Van Sebroeck <wim@iguana.be>
Introduction
------------
This document does not describe what a WatchDog Timer (WDT) Driver or Device is.
It also does not describe the API which can be used by user space to communicate
with a WatchDog Timer. If you want to know this then please read the following
file: Documentation/watchdog/watchdog-api.txt .
So what does this document describe? It describes the API that can be used by
WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core
Framework. This framework provides all interfacing towards user space so that
the same code does not have to be reproduced each time. This also means that
a watchdog timer driver then only needs to provide the different routines
(operations) that control the watchdog timer (WDT).
The API
-------
Each watchdog timer driver that wants to use the WatchDog Timer Driver Core
must #include <linux/watchdog.h> (you would have to do this anyway when
writing a watchdog device driver). This include file contains following
register/unregister routines:
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);
The watchdog_register_device routine registers a watchdog timer device.
The parameter of this routine is a pointer to a watchdog_device structure.
This routine returns zero on success and a negative errno code for failure.
The watchdog_unregister_device routine deregisters a registered watchdog timer
device. The parameter of this routine is the pointer to the registered
watchdog_device structure.
The watchdog device structure looks like this:
struct watchdog_device {
const struct watchdog_info *info;
const struct watchdog_ops *ops;
unsigned int bootstatus;
unsigned int timeout;
unsigned int min_timeout;
unsigned int max_timeout;
void *driver_data;
unsigned long status;
};
It contains following fields:
* info: a pointer to a watchdog_info structure. This structure gives some
additional information about the watchdog timer itself. (Like it's unique name)
* ops: a pointer to the list of watchdog operations that the watchdog supports.
* timeout: the watchdog timer's timeout value (in seconds).
* min_timeout: the watchdog timer's minimum timeout value (in seconds).
* max_timeout: the watchdog timer's maximum timeout value (in seconds).
* bootstatus: status of the device after booting (reported with watchdog
WDIOF_* status bits).
* driver_data: a pointer to the drivers private data of a watchdog device.
This data should only be accessed via the watchdog_set_drvadata and
watchdog_get_drvdata routines.
* status: this field contains a number of status bits that give extra
information about the status of the device (Like: is the watchdog timer
running/active, is the nowayout bit set, is the device opened via
the /dev/watchdog interface or not, ...).
The list of watchdog operations is defined as:
struct watchdog_ops {
struct module *owner;
/* mandatory operations */
int (*start)(struct watchdog_device *);
int (*stop)(struct watchdog_device *);
/* optional operations */
int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};
It is important that you first define the module owner of the watchdog timer
driver's operations. This module owner will be used to lock the module when
the watchdog is active. (This to avoid a system crash when you unload the
module and /dev/watchdog is still open).
Some operations are mandatory and some are optional. The mandatory operations
are:
* start: this is a pointer to the routine that starts the watchdog timer
device.
The routine needs a pointer to the watchdog timer device structure as a
parameter. It returns zero on success or a negative errno code for failure.
* stop: with this routine the watchdog timer device is being stopped.
The routine needs a pointer to the watchdog timer device structure as a
parameter. It returns zero on success or a negative errno code for failure.
Some watchdog timer hardware can only be started and not be stopped. The
driver supporting this hardware needs to make sure that a start and stop
routine is being provided. This can be done by using a timer in the driver
that regularly sends a keepalive ping to the watchdog timer hardware.
Not all watchdog timer hardware supports the same functionality. That's why
all other routines/operations are optional. They only need to be provided if
they are supported. These optional routines/operations are:
* ping: this is the routine that sends a keepalive ping to the watchdog timer
hardware.
The routine needs a pointer to the watchdog timer device structure as a
parameter. It returns zero on success or a negative errno code for failure.
Most hardware that does not support this as a separate function uses the
start function to restart the watchdog timer hardware. And that's also what
the watchdog timer driver core does: to send a keepalive ping to the watchdog
timer hardware it will either use the ping operation (when available) or the
start operation (when the ping operation is not available).
(Note: the WDIOC_KEEPALIVE ioctl call will only be active when the
WDIOF_KEEPALIVEPING bit has been set in the option field on the watchdog's
info structure).
* status: this routine checks the status of the watchdog timer device. The
status of the device is reported with watchdog WDIOF_* status flags/bits.
* set_timeout: this routine checks and changes the timeout of the watchdog
timer device. It returns 0 on success, -EINVAL for "parameter out of range"
and -EIO for "could not write value to the watchdog". On success the timeout
value of the watchdog_device will be changed to the value that was just used
to re-program the watchdog timer device.
(Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
watchdog's info structure).
* ioctl: if this routine is present then it will be called first before we do
our own internal ioctl call handling. This routine should return -ENOIOCTLCMD
if a command is not supported. The parameters that are passed to the ioctl
call are: watchdog_device, cmd and arg.
The status bits should (preferably) be set with the set_bit and clear_bit alike
bit-operations. The status bits that are defined are:
* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device
is active or not. When the watchdog is active after booting, then you should
set this status bit (Note: when you register the watchdog timer device with
this bit set, then opening /dev/watchdog will skip the start operation)
* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device
was opened via /dev/watchdog.
(This bit should only be used by the WatchDog Timer Driver Core).
* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character
has been sent (so that we can support the magic close feature).
(This bit should only be used by the WatchDog Timer Driver Core).
* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog.
If this bit is set then the watchdog timer will not be able to stop.
Note: The WatchDog Timer Driver Core supports the magic close feature and
the nowayout feature. To use the magic close feature you must set the
WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure.
The nowayout feature will overrule the magic close feature.
To get or set driver specific data the following two helper functions should be
used:
static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
The watchdog_set_drvdata function allows you to add driver specific data. The
arguments of this function are the watchdog device where you want to add the
driver specific data to and a pointer to the data itself.
The watchdog_get_drvdata function allows you to retrieve driver specific data.
The argument of this function is the watchdog device where you want to retrieve
data from. The function retruns the pointer to the driver specific data.
...@@ -28,6 +28,17 @@ menuconfig WATCHDOG ...@@ -28,6 +28,17 @@ menuconfig WATCHDOG
if WATCHDOG if WATCHDOG
config WATCHDOG_CORE
bool "WatchDog Timer Driver Core"
---help---
Say Y here if you want to use the new watchdog timer driver core.
This driver provides a framework for all watchdog timer drivers
and gives them the /dev/watchdog interface (and later also the
sysfs interface).
To compile this driver as a module, choose M here: the module will
be called watchdog.
config WATCHDOG_NOWAYOUT config WATCHDOG_NOWAYOUT
bool "Disable watchdog shutdown on close" bool "Disable watchdog shutdown on close"
help help
...@@ -186,6 +197,15 @@ config SA1100_WATCHDOG ...@@ -186,6 +197,15 @@ config SA1100_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 sa1100_wdt. module will be called sa1100_wdt.
config DW_WATCHDOG
tristate "Synopsys DesignWare watchdog"
depends on ARM && HAVE_CLK
help
Say Y here if to include support for the Synopsys DesignWare
watchdog timer found in many ARM chips.
To compile this driver as a module, choose M here: the
module will be called dw_wdt.
config MPCORE_WATCHDOG config MPCORE_WATCHDOG
tristate "MPcore watchdog" tristate "MPcore watchdog"
depends on HAVE_ARM_TWD depends on HAVE_ARM_TWD
...@@ -321,7 +341,7 @@ config MAX63XX_WATCHDOG ...@@ -321,7 +341,7 @@ config MAX63XX_WATCHDOG
config IMX2_WDT config IMX2_WDT
tristate "IMX2+ Watchdog" tristate "IMX2+ Watchdog"
depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 depends on IMX_HAVE_PLATFORM_IMX2_WDT
help help
This is the driver for the hardware watchdog This is the driver for the hardware watchdog
on the Freescale IMX2 and later processors. on the Freescale IMX2 and later processors.
...@@ -879,6 +899,20 @@ config M54xx_WATCHDOG ...@@ -879,6 +899,20 @@ config M54xx_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 m54xx_wdt. module will be called m54xx_wdt.
# MicroBlaze Architecture
config XILINX_WATCHDOG
tristate "Xilinx Watchdog timer"
depends on MICROBLAZE
---help---
Watchdog driver for the xps_timebase_wdt ip core.
IMPORTANT: The xps_timebase_wdt parent must have the property
"clock-frequency" at device tree.
To compile this driver as a module, choose M here: the
module will be called of_xilinx_wdt.
# MIPS Architecture # MIPS Architecture
config ATH79_WDT config ATH79_WDT
......
...@@ -2,6 +2,10 @@ ...@@ -2,6 +2,10 @@
# Makefile for the WatchDog device drivers. # Makefile for the WatchDog device drivers.
# #
# The WatchDog Timer Driver Core.
watchdog-objs += watchdog_core.o watchdog_dev.o
obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o
# Only one watchdog can succeed. We probe the ISA/PCI/USB based # Only one watchdog can succeed. We probe the ISA/PCI/USB based
# watchdog-cards first, then the architecture specific watchdog # watchdog-cards first, then the architecture specific watchdog
# drivers and then the architecture independent "softdog" driver. # drivers and then the architecture independent "softdog" driver.
...@@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o ...@@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
...@@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o ...@@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
# M68K Architecture # M68K Architecture
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
# MicroBlaze Architecture
obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o
# MIPS Architecture # MIPS Architecture
obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <mach/at91_wdt.h> #include "at91sam9_wdt.h"
#define DRV_NAME "AT91SAM9 Watchdog" #define DRV_NAME "AT91SAM9 Watchdog"
...@@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev) ...@@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
return res; return res;
} }
#ifdef CONFIG_PM
static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
{
return 0;
}
static int at91wdt_resume(struct platform_device *pdev)
{
return 0;
}
#else
#define at91wdt_suspend NULL
#define at91wdt_resume NULL
#endif
static struct platform_driver at91wdt_driver = { static struct platform_driver at91wdt_driver = {
.remove = __exit_p(at91wdt_remove), .remove = __exit_p(at91wdt_remove),
.suspend = at91wdt_suspend,
.resume = at91wdt_resume,
.driver = { .driver = {
.name = "at91_wdt", .name = "at91_wdt",
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
/* /*
* arch/arm/mach-at91/include/mach/at91_wdt.h * drivers/watchdog/at91sam9_wdt.h
* *
* Copyright (C) 2007 Andrew Victor * Copyright (C) 2007 Andrew Victor
* Copyright (C) 2007 Atmel Corporation. * Copyright (C) 2007 Atmel Corporation.
......
/*
* Copyright 2010-2011 Picochip Ltd., Jamie Iles
* http://www.picochip.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 file implements a driver for the Synopsys DesignWare watchdog device
* in the many ARM subsystems. The watchdog has 16 different timeout periods
* and these are a function of the input clock frequency.
*
* The DesignWare watchdog cannot be stopped once it has been started so we
* use a software timer to implement a ping that will keep the watchdog alive.
* If we receive an expected close for the watchdog then we keep the timer
* running, otherwise the timer is stopped and the watchdog will expire.
*/
#define pr_fmt(fmt) "dw_wdt: " fmt
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#define WDOG_CONTROL_REG_OFFSET 0x00
#define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
#define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
#define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
#define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c
#define WDOG_COUNTER_RESTART_KICK_VALUE 0x76
/* The maximum TOP (timeout period) value that can be set in the watchdog. */
#define DW_WDT_MAX_TOP 15
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) ")");
#define WDT_TIMEOUT (HZ / 2)
static struct {
spinlock_t lock;
void __iomem *regs;
struct clk *clk;
unsigned long in_use;
unsigned long next_heartbeat;
struct timer_list timer;
int expect_close;
} dw_wdt;
static inline int dw_wdt_is_enabled(void)
{
return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) &
WDOG_CONTROL_REG_WDT_EN_MASK;
}
static inline int dw_wdt_top_in_seconds(unsigned top)
{
/*
* There are 16 possible timeout values in 0..15 where the number of
* cycles is 2 ^ (16 + i) and the watchdog counts down.
*/
return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk);
}
static int dw_wdt_get_top(void)
{
int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF;
return dw_wdt_top_in_seconds(top);
}
static inline void dw_wdt_set_next_heartbeat(void)
{
dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ;
}
static int dw_wdt_set_top(unsigned top_s)
{
int i, top_val = DW_WDT_MAX_TOP;
/*
* Iterate over the timeout values until we find the closest match. We
* always look for >=.
*/
for (i = 0; i <= DW_WDT_MAX_TOP; ++i)
if (dw_wdt_top_in_seconds(i) >= top_s) {
top_val = i;
break;
}
/* Set the new value in the watchdog. */
writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET);
dw_wdt_set_next_heartbeat();
return dw_wdt_top_in_seconds(top_val);
}
static void dw_wdt_keepalive(void)
{
writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs +
WDOG_COUNTER_RESTART_REG_OFFSET);
}
static void dw_wdt_ping(unsigned long data)
{
if (time_before(jiffies, dw_wdt.next_heartbeat) ||
(!nowayout && !dw_wdt.in_use)) {
dw_wdt_keepalive();
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
} else
pr_crit("keepalive missed, machine will reset\n");
}
static int dw_wdt_open(struct inode *inode, struct file *filp)
{
if (test_and_set_bit(0, &dw_wdt.in_use))
return -EBUSY;
/* Make sure we don't get unloaded. */
__module_get(THIS_MODULE);
spin_lock(&dw_wdt.lock);
if (!dw_wdt_is_enabled()) {
/*
* The watchdog is not currently enabled. Set the timeout to
* the maximum and then start it.
*/
dw_wdt_set_top(DW_WDT_MAX_TOP);
writel(WDOG_CONTROL_REG_WDT_EN_MASK,
dw_wdt.regs + WDOG_CONTROL_REG_OFFSET);
}
dw_wdt_set_next_heartbeat();
spin_unlock(&dw_wdt.lock);
return nonseekable_open(inode, filp);
}
ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len,
loff_t *offset)
{
if (!len)
return 0;
if (!nowayout) {
size_t i;
dw_wdt.expect_close = 0;
for (i = 0; i < len; ++i) {
char c;
if (get_user(c, buf + i))
return -EFAULT;
if (c == 'V') {
dw_wdt.expect_close = 1;
break;
}
}
}
dw_wdt_set_next_heartbeat();
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
return len;
}
static u32 dw_wdt_time_left(void)
{
return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) /
clk_get_rate(dw_wdt.clk);
}
static const struct watchdog_info dw_wdt_ident = {
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
WDIOF_MAGICCLOSE,
.identity = "Synopsys DesignWare Watchdog",
};
static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned long val;
int timeout;
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident,
sizeof(dw_wdt_ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, (int *)arg);
case WDIOC_KEEPALIVE:
dw_wdt_set_next_heartbeat();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(val, (int __user *)arg))
return -EFAULT;
timeout = dw_wdt_set_top(val);
return put_user(timeout , (int __user *)arg);
case WDIOC_GETTIMEOUT:
return put_user(dw_wdt_get_top(), (int __user *)arg);
case WDIOC_GETTIMELEFT:
/* Get the time left until expiry. */
if (get_user(val, (int __user *)arg))
return -EFAULT;
return put_user(dw_wdt_time_left(), (int __user *)arg);
default:
return -ENOTTY;
}
}
static int dw_wdt_release(struct inode *inode, struct file *filp)
{
clear_bit(0, &dw_wdt.in_use);
if (!dw_wdt.expect_close) {
del_timer(&dw_wdt.timer);
if (!nowayout)
pr_crit("unexpected close, system will reboot soon\n");
else
pr_crit("watchdog cannot be disabled, system will reboot soon\n");
}
dw_wdt.expect_close = 0;
return 0;
}
#ifdef CONFIG_PM
static int dw_wdt_suspend(struct device *dev)
{
clk_disable(dw_wdt.clk);
return 0;
}
static int dw_wdt_resume(struct device *dev)
{
int err = clk_enable(dw_wdt.clk);
if (err)
return err;
dw_wdt_keepalive();
return 0;
}
static const struct dev_pm_ops dw_wdt_pm_ops = {
.suspend = dw_wdt_suspend,
.resume = dw_wdt_resume,
};
#endif /* CONFIG_PM */
static const struct file_operations wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.open = dw_wdt_open,
.write = dw_wdt_write,
.unlocked_ioctl = dw_wdt_ioctl,
.release = dw_wdt_release
};
static struct miscdevice dw_wdt_miscdev = {
.fops = &wdt_fops,
.name = "watchdog",
.minor = WATCHDOG_MINOR,
};
static int __devinit dw_wdt_drv_probe(struct platform_device *pdev)
{
int ret;
struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!mem)
return -EINVAL;
if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem),
"dw_wdt"))
return -ENOMEM;
dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
if (!dw_wdt.regs)
return -ENOMEM;
dw_wdt.clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(dw_wdt.clk))
return PTR_ERR(dw_wdt.clk);
ret = clk_enable(dw_wdt.clk);
if (ret)
goto out_put_clk;
spin_lock_init(&dw_wdt.lock);
ret = misc_register(&dw_wdt_miscdev);
if (ret)
goto out_disable_clk;
dw_wdt_set_next_heartbeat();
setup_timer(&dw_wdt.timer, dw_wdt_ping, 0);
mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT);
return 0;
out_disable_clk:
clk_disable(dw_wdt.clk);
out_put_clk:
clk_put(dw_wdt.clk);
return ret;
}
static int __devexit dw_wdt_drv_remove(struct platform_device *pdev)
{
misc_deregister(&dw_wdt_miscdev);
clk_disable(dw_wdt.clk);
clk_put(dw_wdt.clk);
return 0;
}
static struct platform_driver dw_wdt_driver = {
.probe = dw_wdt_drv_probe,
.remove = __devexit_p(dw_wdt_drv_remove),
.driver = {
.name = "dw_wdt",
.owner = THIS_MODULE,
#ifdef CONFIG_PM
.pm = &dw_wdt_pm_ops,
#endif /* CONFIG_PM */
},
};
static int __init dw_wdt_watchdog_init(void)
{
return platform_driver_register(&dw_wdt_driver);
}
module_init(dw_wdt_watchdog_init);
static void __exit dw_wdt_watchdog_exit(void)
{
platform_driver_unregister(&dw_wdt_driver);
}
module_exit(dw_wdt_watchdog_exit);
MODULE_AUTHOR("Jamie Iles");
MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#endif /* CONFIG_HPWDT_NMI_DECODING */ #endif /* CONFIG_HPWDT_NMI_DECODING */
#define HPWDT_VERSION "1.2.0" #define HPWDT_VERSION "1.3.0"
#define SECS_TO_TICKS(secs) ((secs) * 1000 / 128) #define SECS_TO_TICKS(secs) ((secs) * 1000 / 128)
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000) #define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535) #define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
...@@ -87,6 +87,19 @@ struct smbios_cru64_info { ...@@ -87,6 +87,19 @@ struct smbios_cru64_info {
}; };
#define SMBIOS_CRU64_INFORMATION 212 #define SMBIOS_CRU64_INFORMATION 212
/* type 219 */
struct smbios_proliant_info {
u8 type;
u8 byte_length;
u16 handle;
u32 power_features;
u32 omega_features;
u32 reserved;
u32 misc_features;
};
#define SMBIOS_ICRU_INFORMATION 219
struct cmn_registers { struct cmn_registers {
union { union {
struct { struct {
...@@ -132,6 +145,7 @@ struct cmn_registers { ...@@ -132,6 +145,7 @@ struct cmn_registers {
static unsigned int hpwdt_nmi_decoding; static unsigned int hpwdt_nmi_decoding;
static unsigned int allow_kdump; static unsigned int allow_kdump;
static unsigned int priority; /* hpwdt at end of die_notify list */ static unsigned int priority; /* hpwdt at end of die_notify list */
static unsigned int is_icru;
static DEFINE_SPINLOCK(rom_lock); static DEFINE_SPINLOCK(rom_lock);
static void *cru_rom_addr; static void *cru_rom_addr;
static struct cmn_registers cmn_regs; static struct cmn_registers cmn_regs;
...@@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason, ...@@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,
goto out; goto out;
spin_lock_irqsave(&rom_lock, rom_pl); spin_lock_irqsave(&rom_lock, rom_pl);
if (!die_nmi_called) if (!die_nmi_called && !is_icru)
asminline_call(&cmn_regs, cru_rom_addr); asminline_call(&cmn_regs, cru_rom_addr);
die_nmi_called = 1; die_nmi_called = 1;
spin_unlock_irqrestore(&rom_lock, rom_pl); spin_unlock_irqrestore(&rom_lock, rom_pl);
if (cmn_regs.u1.ral == 0) { if (!is_icru) {
printk(KERN_WARNING "hpwdt: An NMI occurred, " if (cmn_regs.u1.ral == 0) {
"but unable to determine source.\n"); printk(KERN_WARNING "hpwdt: An NMI occurred, "
} else { "but unable to determine source.\n");
if (allow_kdump) }
hpwdt_stop();
panic("An NMI occurred, please see the Integrated "
"Management Log for details.\n");
} }
if (allow_kdump)
hpwdt_stop();
panic("An NMI occurred, please see the Integrated "
"Management Log for details.\n");
out: out:
return NOTIFY_OK; return NOTIFY_OK;
} }
...@@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev) ...@@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
} }
#endif /* CONFIG_X86_LOCAL_APIC */ #endif /* CONFIG_X86_LOCAL_APIC */
/*
* dmi_find_icru
*
* Routine Description:
* This function checks whether or not we are on an iCRU-based server.
* This check is independent of architecture and needs to be made for
* any ProLiant system.
*/
static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy)
{
struct smbios_proliant_info *smbios_proliant_ptr;
if (dm->type == SMBIOS_ICRU_INFORMATION) {
smbios_proliant_ptr = (struct smbios_proliant_info *) dm;
if (smbios_proliant_ptr->misc_features & 0x01)
is_icru = 1;
}
}
static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev) static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)
{ {
int retval; int retval;
/* /*
* We need to map the ROM to get the CRU service. * On typical CRU-based systems we need to map that service in
* For 32 bit Operating Systems we need to go through the 32 Bit * the BIOS. For 32 bit Operating Systems we need to go through
* BIOS Service Directory * the 32 Bit BIOS Service Directory. For 64 bit Operating
* For 64 bit Operating Systems we get that service through SMBIOS. * Systems we get that service through SMBIOS.
*
* On systems that support the new iCRU service all we need to
* do is call dmi_walk to get the supported flag value and skip
* the old cru detect code.
*/ */
retval = detect_cru_service(); dmi_walk(dmi_find_icru, NULL);
if (retval < 0) { if (!is_icru) {
dev_warn(&dev->dev,
"Unable to detect the %d Bit CRU Service.\n", /*
HPWDT_ARCH); * We need to map the ROM to get the CRU service.
return retval; * For 32 bit Operating Systems we need to go through the 32 Bit
} * BIOS Service Directory
* For 64 bit Operating Systems we get that service through SMBIOS.
*/
retval = detect_cru_service();
if (retval < 0) {
dev_warn(&dev->dev,
"Unable to detect the %d Bit CRU Service.\n",
HPWDT_ARCH);
return retval;
}
/* /*
* We know this is the only CRU call we need to make so lets keep as * We know this is the only CRU call we need to make so lets keep as
* few instructions as possible once the NMI comes in. * few instructions as possible once the NMI comes in.
*/ */
cmn_regs.u1.rah = 0x0D; cmn_regs.u1.rah = 0x0D;
cmn_regs.u1.ral = 0x02; cmn_regs.u1.ral = 0x02;
}
/* /*
* If the priority is set to 1, then we will be put first on the * If the priority is set to 1, then we will be put first on the
......
This diff is collapsed.
...@@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) ...@@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)
} }
} }
static const struct of_device_id imx2_wdt_dt_ids[] = {
{ .compatible = "fsl,imx21-wdt", },
{ /* sentinel */ }
};
static struct platform_driver imx2_wdt_driver = { static struct platform_driver imx2_wdt_driver = {
.remove = __exit_p(imx2_wdt_remove), .remove = __exit_p(imx2_wdt_remove),
.shutdown = imx2_wdt_shutdown, .shutdown = imx2_wdt_shutdown,
.driver = { .driver = {
.name = DRIVER_NAME, .name = DRIVER_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = imx2_wdt_dt_ids,
}, },
}; };
......
...@@ -28,10 +28,10 @@ ...@@ -28,10 +28,10 @@
#include <linux/notifier.h> #include <linux/notifier.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/pci.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/ioport.h>
#define NAME "it8712f_wdt" #define NAME "it8712f_wdt"
...@@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close"); ...@@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
static unsigned long wdt_open; static unsigned long wdt_open;
static unsigned expect_close; static unsigned expect_close;
static spinlock_t io_lock;
static unsigned char revision; static unsigned char revision;
/* Dog Food address - We use the game port address */ /* Dog Food address - We use the game port address */
...@@ -121,20 +120,26 @@ static inline void superio_select(int ldn) ...@@ -121,20 +120,26 @@ static inline void superio_select(int ldn)
outb(ldn, VAL); outb(ldn, VAL);
} }
static inline void superio_enter(void) static inline int superio_enter(void)
{ {
spin_lock(&io_lock); /*
* Try to reserve REG and REG + 1 for exclusive access.
*/
if (!request_muxed_region(REG, 2, NAME))
return -EBUSY;
outb(0x87, REG); outb(0x87, REG);
outb(0x01, REG); outb(0x01, REG);
outb(0x55, REG); outb(0x55, REG);
outb(0x55, REG); outb(0x55, REG);
return 0;
} }
static inline void superio_exit(void) static inline void superio_exit(void)
{ {
outb(0x02, REG); outb(0x02, REG);
outb(0x02, VAL); outb(0x02, VAL);
spin_unlock(&io_lock); release_region(REG, 2);
} }
static inline void it8712f_wdt_ping(void) static inline void it8712f_wdt_ping(void)
...@@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void) ...@@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void)
return 0; return 0;
} }
static void it8712f_wdt_enable(void) static int it8712f_wdt_enable(void)
{ {
int ret = superio_enter();
if (ret)
return ret;
printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); printk(KERN_DEBUG NAME ": enabling watchdog timer\n");
superio_enter();
superio_select(LDN_GPIO); superio_select(LDN_GPIO);
superio_outb(wdt_control_reg, WDT_CONTROL); superio_outb(wdt_control_reg, WDT_CONTROL);
...@@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void) ...@@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void)
superio_exit(); superio_exit();
it8712f_wdt_ping(); it8712f_wdt_ping();
return 0;
} }
static void it8712f_wdt_disable(void) static int it8712f_wdt_disable(void)
{ {
printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); int ret = superio_enter();
if (ret)
return ret;
superio_enter(); printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
superio_select(LDN_GPIO); superio_select(LDN_GPIO);
superio_outb(0, WDT_CONFIG); superio_outb(0, WDT_CONFIG);
...@@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void) ...@@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void)
superio_outb(0, WDT_TIMEOUT); superio_outb(0, WDT_TIMEOUT);
superio_exit(); superio_exit();
return 0;
} }
static int it8712f_wdt_notify(struct notifier_block *this, static int it8712f_wdt_notify(struct notifier_block *this,
...@@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
WDIOF_MAGICCLOSE, WDIOF_MAGICCLOSE,
}; };
int value; int value;
int ret;
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
...@@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
return -EFAULT; return -EFAULT;
return 0; return 0;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
superio_enter(); ret = superio_enter();
if (ret)
return ret;
superio_select(LDN_GPIO); superio_select(LDN_GPIO);
value = it8712f_wdt_get_status(); value = it8712f_wdt_get_status();
...@@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
if (value > (max_units * 60)) if (value > (max_units * 60))
return -EINVAL; return -EINVAL;
margin = value; margin = value;
superio_enter(); ret = superio_enter();
if (ret)
return ret;
superio_select(LDN_GPIO); superio_select(LDN_GPIO);
it8712f_wdt_update_margin(); it8712f_wdt_update_margin();
...@@ -299,10 +317,14 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd, ...@@ -299,10 +317,14 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
static int it8712f_wdt_open(struct inode *inode, struct file *file) static int it8712f_wdt_open(struct inode *inode, struct file *file)
{ {
int ret;
/* only allow one at a time */ /* only allow one at a time */
if (test_and_set_bit(0, &wdt_open)) if (test_and_set_bit(0, &wdt_open))
return -EBUSY; return -EBUSY;
it8712f_wdt_enable();
ret = it8712f_wdt_enable();
if (ret)
return ret;
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
} }
...@@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file) ...@@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file)
": watchdog device closed unexpectedly, will not" ": watchdog device closed unexpectedly, will not"
" disable the watchdog timer\n"); " disable the watchdog timer\n");
} else if (!nowayout) { } else if (!nowayout) {
it8712f_wdt_disable(); if (it8712f_wdt_disable())
printk(KERN_WARNING NAME "Watchdog disable failed\n");
} }
expect_close = 0; expect_close = 0;
clear_bit(0, &wdt_open); clear_bit(0, &wdt_open);
...@@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address) ...@@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address)
{ {
int err = -ENODEV; int err = -ENODEV;
int chip_type; int chip_type;
int ret = superio_enter();
if (ret)
return ret;
superio_enter();
chip_type = superio_inw(DEVID); chip_type = superio_inw(DEVID);
if (chip_type != IT8712F_DEVID) if (chip_type != IT8712F_DEVID)
goto exit; goto exit;
...@@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void) ...@@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void)
{ {
int err = 0; int err = 0;
spin_lock_init(&io_lock);
if (it8712f_wdt_find(&address)) if (it8712f_wdt_find(&address))
return -ENODEV; return -ENODEV;
...@@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void) ...@@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void)
return -EBUSY; return -EBUSY;
} }
it8712f_wdt_disable(); err = it8712f_wdt_disable();
if (err) {
printk(KERN_ERR NAME ": unable to disable watchdog timer.\n");
goto out;
}
err = register_reboot_notifier(&it8712f_wdt_notifier); err = register_reboot_notifier(&it8712f_wdt_notifier);
if (err) { if (err) {
......
...@@ -137,7 +137,6 @@ ...@@ -137,7 +137,6 @@
static unsigned int base, gpact, ciract, max_units, chip_type; static unsigned int base, gpact, ciract, max_units, chip_type;
static unsigned long wdt_status; static unsigned long wdt_status;
static DEFINE_SPINLOCK(spinlock);
static int nogameport = DEFAULT_NOGAMEPORT; static int nogameport = DEFAULT_NOGAMEPORT;
static int exclusive = DEFAULT_EXCLUSIVE; static int exclusive = DEFAULT_EXCLUSIVE;
...@@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default=" ...@@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
/* Superio Chip */ /* Superio Chip */
static inline void superio_enter(void) static inline int superio_enter(void)
{ {
/*
* Try to reserve REG and REG + 1 for exclusive access.
*/
if (!request_muxed_region(REG, 2, WATCHDOG_NAME))
return -EBUSY;
outb(0x87, REG); outb(0x87, REG);
outb(0x01, REG); outb(0x01, REG);
outb(0x55, REG); outb(0x55, REG);
outb(0x55, REG); outb(0x55, REG);
return 0;
} }
static inline void superio_exit(void) static inline void superio_exit(void)
{ {
outb(0x02, REG); outb(0x02, REG);
outb(0x02, VAL); outb(0x02, VAL);
release_region(REG, 2);
} }
static inline void superio_select(int ldn) static inline void superio_select(int ldn)
...@@ -255,12 +262,11 @@ static void wdt_keepalive(void) ...@@ -255,12 +262,11 @@ static void wdt_keepalive(void)
set_bit(WDTS_KEEPALIVE, &wdt_status); set_bit(WDTS_KEEPALIVE, &wdt_status);
} }
static void wdt_start(void) static int wdt_start(void)
{ {
unsigned long flags; int ret = superio_enter();
if (ret)
spin_lock_irqsave(&spinlock, flags); return ret;
superio_enter();
superio_select(GPIO); superio_select(GPIO);
if (test_bit(WDTS_USE_GP, &wdt_status)) if (test_bit(WDTS_USE_GP, &wdt_status))
...@@ -270,15 +276,15 @@ static void wdt_start(void) ...@@ -270,15 +276,15 @@ static void wdt_start(void)
wdt_update_timeout(); wdt_update_timeout();
superio_exit(); superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
return 0;
} }
static void wdt_stop(void) static int wdt_stop(void)
{ {
unsigned long flags; int ret = superio_enter();
if (ret)
spin_lock_irqsave(&spinlock, flags); return ret;
superio_enter();
superio_select(GPIO); superio_select(GPIO);
superio_outb(0x00, WDTCTRL); superio_outb(0x00, WDTCTRL);
...@@ -288,7 +294,7 @@ static void wdt_stop(void) ...@@ -288,7 +294,7 @@ static void wdt_stop(void)
superio_outb(0x00, WDTVALMSB); superio_outb(0x00, WDTVALMSB);
superio_exit(); superio_exit();
spin_unlock_irqrestore(&spinlock, flags); return 0;
} }
/** /**
...@@ -303,8 +309,6 @@ static void wdt_stop(void) ...@@ -303,8 +309,6 @@ static void wdt_stop(void)
static int wdt_set_timeout(int t) static int wdt_set_timeout(int t)
{ {
unsigned long flags;
if (t < 1 || t > max_units * 60) if (t < 1 || t > max_units * 60)
return -EINVAL; return -EINVAL;
...@@ -313,14 +317,15 @@ static int wdt_set_timeout(int t) ...@@ -313,14 +317,15 @@ static int wdt_set_timeout(int t)
else else
timeout = t; timeout = t;
spin_lock_irqsave(&spinlock, flags);
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
superio_enter(); int ret = superio_enter();
if (ret)
return ret;
superio_select(GPIO); superio_select(GPIO);
wdt_update_timeout(); wdt_update_timeout();
superio_exit(); superio_exit();
} }
spin_unlock_irqrestore(&spinlock, flags);
return 0; return 0;
} }
...@@ -339,12 +344,12 @@ static int wdt_set_timeout(int t) ...@@ -339,12 +344,12 @@ static int wdt_set_timeout(int t)
static int wdt_get_status(int *status) static int wdt_get_status(int *status)
{ {
unsigned long flags;
*status = 0; *status = 0;
if (testmode) { if (testmode) {
spin_lock_irqsave(&spinlock, flags); int ret = superio_enter();
superio_enter(); if (ret)
return ret;
superio_select(GPIO); superio_select(GPIO);
if (superio_inb(WDTCTRL) & WDT_ZERO) { if (superio_inb(WDTCTRL) & WDT_ZERO) {
superio_outb(0x00, WDTCTRL); superio_outb(0x00, WDTCTRL);
...@@ -353,7 +358,6 @@ static int wdt_get_status(int *status) ...@@ -353,7 +358,6 @@ static int wdt_get_status(int *status)
} }
superio_exit(); superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
} }
if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status)) if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
*status |= WDIOF_KEEPALIVEPING; *status |= WDIOF_KEEPALIVEPING;
...@@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file) ...@@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file)
if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status)) if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))
return -EBUSY; return -EBUSY;
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
int ret;
if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status)) if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
__module_get(THIS_MODULE); __module_get(THIS_MODULE);
wdt_start();
ret = wdt_start();
if (ret) {
clear_bit(WDTS_LOCKED, &wdt_status);
clear_bit(WDTS_TIMER_RUN, &wdt_status);
clear_bit(WDTS_DEV_OPEN, &wdt_status);
return ret;
}
} }
return nonseekable_open(inode, file); return nonseekable_open(inode, file);
} }
...@@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file) ...@@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file)
{ {
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) {
wdt_stop(); int ret = wdt_stop();
if (ret) {
/*
* Stop failed. Just keep the watchdog alive
* and hope nothing bad happens.
*/
set_bit(WDTS_EXPECTED, &wdt_status);
wdt_keepalive();
return ret;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status); clear_bit(WDTS_TIMER_RUN, &wdt_status);
} else { } else {
wdt_keepalive(); wdt_keepalive();
...@@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
&ident, sizeof(ident)) ? -EFAULT : 0; &ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS: case WDIOC_GETSTATUS:
wdt_get_status(&status); rc = wdt_get_status(&status);
if (rc)
return rc;
return put_user(status, uarg.i); return put_user(status, uarg.i);
case WDIOC_GETBOOTSTATUS: case WDIOC_GETBOOTSTATUS:
...@@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (new_options) { switch (new_options) {
case WDIOS_DISABLECARD: case WDIOS_DISABLECARD:
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
wdt_stop(); rc = wdt_stop();
if (rc)
return rc;
}
clear_bit(WDTS_TIMER_RUN, &wdt_status); clear_bit(WDTS_TIMER_RUN, &wdt_status);
return 0; return 0;
case WDIOS_ENABLECARD: case WDIOS_ENABLECARD:
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
wdt_start(); rc = wdt_start();
if (rc) {
clear_bit(WDTS_TIMER_RUN, &wdt_status);
return rc;
}
}
return 0; return 0;
default: default:
...@@ -560,16 +591,17 @@ static int __init it87_wdt_init(void) ...@@ -560,16 +591,17 @@ static int __init it87_wdt_init(void)
int rc = 0; int rc = 0;
int try_gameport = !nogameport; int try_gameport = !nogameport;
u8 chip_rev; u8 chip_rev;
unsigned long flags; int gp_rreq_fail = 0;
wdt_status = 0; wdt_status = 0;
spin_lock_irqsave(&spinlock, flags); rc = superio_enter();
superio_enter(); if (rc)
return rc;
chip_type = superio_inw(CHIPID); chip_type = superio_inw(CHIPID);
chip_rev = superio_inb(CHIPREV) & 0x0f; chip_rev = superio_inb(CHIPREV) & 0x0f;
superio_exit(); superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
switch (chip_type) { switch (chip_type) {
case IT8702_ID: case IT8702_ID:
...@@ -603,8 +635,9 @@ static int __init it87_wdt_init(void) ...@@ -603,8 +635,9 @@ static int __init it87_wdt_init(void)
return -ENODEV; return -ENODEV;
} }
spin_lock_irqsave(&spinlock, flags); rc = superio_enter();
superio_enter(); if (rc)
return rc;
superio_select(GPIO); superio_select(GPIO);
superio_outb(WDT_TOV1, WDTCFG); superio_outb(WDT_TOV1, WDTCFG);
...@@ -620,21 +653,16 @@ static int __init it87_wdt_init(void) ...@@ -620,21 +653,16 @@ static int __init it87_wdt_init(void)
} }
gpact = superio_inb(ACTREG); gpact = superio_inb(ACTREG);
superio_outb(0x01, ACTREG); superio_outb(0x01, ACTREG);
superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
if (request_region(base, 1, WATCHDOG_NAME)) if (request_region(base, 1, WATCHDOG_NAME))
set_bit(WDTS_USE_GP, &wdt_status); set_bit(WDTS_USE_GP, &wdt_status);
else else
rc = -EIO; gp_rreq_fail = 1;
} else {
superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
} }
/* If we haven't Gameport support, try to get CIR support */ /* If we haven't Gameport support, try to get CIR support */
if (!test_bit(WDTS_USE_GP, &wdt_status)) { if (!test_bit(WDTS_USE_GP, &wdt_status)) {
if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
if (rc == -EIO) if (gp_rreq_fail)
printk(KERN_ERR PFX printk(KERN_ERR PFX
"I/O Address 0x%04x and 0x%04x" "I/O Address 0x%04x and 0x%04x"
" already in use\n", base, CIR_BASE); " already in use\n", base, CIR_BASE);
...@@ -646,21 +674,16 @@ static int __init it87_wdt_init(void) ...@@ -646,21 +674,16 @@ static int __init it87_wdt_init(void)
goto err_out; goto err_out;
} }
base = CIR_BASE; base = CIR_BASE;
spin_lock_irqsave(&spinlock, flags);
superio_enter();
superio_select(CIR); superio_select(CIR);
superio_outw(base, BASEREG); superio_outw(base, BASEREG);
superio_outb(0x00, CIR_ILS); superio_outb(0x00, CIR_ILS);
ciract = superio_inb(ACTREG); ciract = superio_inb(ACTREG);
superio_outb(0x01, ACTREG); superio_outb(0x01, ACTREG);
if (rc == -EIO) { if (gp_rreq_fail) {
superio_select(GAMEPORT); superio_select(GAMEPORT);
superio_outb(gpact, ACTREG); superio_outb(gpact, ACTREG);
} }
superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
} }
if (timeout < 1 || timeout > max_units * 60) { if (timeout < 1 || timeout > max_units * 60) {
...@@ -704,6 +727,7 @@ static int __init it87_wdt_init(void) ...@@ -704,6 +727,7 @@ static int __init it87_wdt_init(void)
"nogameport=%d)\n", chip_type, chip_rev, timeout, "nogameport=%d)\n", chip_type, chip_rev, timeout,
nowayout, testmode, exclusive, nogameport); nowayout, testmode, exclusive, nogameport);
superio_exit();
return 0; return 0;
err_out_reboot: err_out_reboot:
...@@ -711,49 +735,37 @@ static int __init it87_wdt_init(void) ...@@ -711,49 +735,37 @@ static int __init it87_wdt_init(void)
err_out_region: err_out_region:
release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8); release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
if (!test_bit(WDTS_USE_GP, &wdt_status)) { if (!test_bit(WDTS_USE_GP, &wdt_status)) {
spin_lock_irqsave(&spinlock, flags);
superio_enter();
superio_select(CIR); superio_select(CIR);
superio_outb(ciract, ACTREG); superio_outb(ciract, ACTREG);
superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
} }
err_out: err_out:
if (try_gameport) { if (try_gameport) {
spin_lock_irqsave(&spinlock, flags);
superio_enter();
superio_select(GAMEPORT); superio_select(GAMEPORT);
superio_outb(gpact, ACTREG); superio_outb(gpact, ACTREG);
superio_exit();
spin_unlock_irqrestore(&spinlock, flags);
} }
superio_exit();
return rc; return rc;
} }
static void __exit it87_wdt_exit(void) static void __exit it87_wdt_exit(void)
{ {
unsigned long flags; if (superio_enter() == 0) {
int nolock; superio_select(GPIO);
superio_outb(0x00, WDTCTRL);
nolock = !spin_trylock_irqsave(&spinlock, flags); superio_outb(0x00, WDTCFG);
superio_enter(); superio_outb(0x00, WDTVALLSB);
superio_select(GPIO); if (max_units > 255)
superio_outb(0x00, WDTCTRL); superio_outb(0x00, WDTVALMSB);
superio_outb(0x00, WDTCFG); if (test_bit(WDTS_USE_GP, &wdt_status)) {
superio_outb(0x00, WDTVALLSB); superio_select(GAMEPORT);
if (max_units > 255) superio_outb(gpact, ACTREG);
superio_outb(0x00, WDTVALMSB); } else {
if (test_bit(WDTS_USE_GP, &wdt_status)) { superio_select(CIR);
superio_select(GAMEPORT); superio_outb(ciract, ACTREG);
superio_outb(gpact, ACTREG); }
} else { superio_exit();
superio_select(CIR);
superio_outb(ciract, ACTREG);
} }
superio_exit();
if (!nolock)
spin_unlock_irqrestore(&spinlock, flags);
misc_deregister(&wdt_miscdev); misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier); unregister_reboot_notifier(&wdt_notifier);
......
...@@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev) ...@@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev)
return 0; return 0;
} }
#ifdef CONFIG_PM
static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg)
{
struct mpcore_wdt *wdt = platform_get_drvdata(dev);
mpcore_wdt_stop(wdt); /* Turn the WDT off */
return 0;
}
static int mpcore_wdt_resume(struct platform_device *dev)
{
struct mpcore_wdt *wdt = platform_get_drvdata(dev);
/* re-activate timer */
if (test_bit(0, &wdt->timer_alive))
mpcore_wdt_start(wdt);
return 0;
}
#else
#define mpcore_wdt_suspend NULL
#define mpcore_wdt_resume NULL
#endif
/* work with hotplug and coldplug */ /* work with hotplug and coldplug */
MODULE_ALIAS("platform:mpcore_wdt"); MODULE_ALIAS("platform:mpcore_wdt");
static struct platform_driver mpcore_wdt_driver = { static struct platform_driver mpcore_wdt_driver = {
.probe = mpcore_wdt_probe, .probe = mpcore_wdt_probe,
.remove = __devexit_p(mpcore_wdt_remove), .remove = __devexit_p(mpcore_wdt_remove),
.suspend = mpcore_wdt_suspend,
.resume = mpcore_wdt_resume,
.shutdown = mpcore_wdt_shutdown, .shutdown = mpcore_wdt_shutdown,
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -225,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev) ...@@ -225,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
ret = misc_register(&mtx1_wdt_misc); ret = misc_register(&mtx1_wdt_misc);
if (ret < 0) { if (ret < 0) {
printk(KERN_ERR " mtx-1_wdt : failed to register\n"); dev_err(&pdev->dev, "failed to register\n");
return ret; return ret;
} }
mtx1_wdt_start(); mtx1_wdt_start();
printk(KERN_INFO "MTX-1 Watchdog driver\n"); dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");
return 0; return 0;
} }
......
This diff is collapsed.
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
#define IO_DEFAULT 0x2E /* Address used on Portwell Boards */ #define IO_DEFAULT 0x2E /* Address used on Portwell Boards */
static int io = IO_DEFAULT; static int io = IO_DEFAULT;
static int swc_base_addr = -1;
static int timeout = DEFAULT_TIMEOUT; /* timeout value */ static int timeout = DEFAULT_TIMEOUT; /* timeout value */
static unsigned long timer_enabled; /* is the timer enabled? */ static unsigned long timer_enabled; /* is the timer enabled? */
...@@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void) ...@@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void)
/* Read SWC I/O base address */ /* Read SWC I/O base address */
static inline unsigned int pc87413_get_swc_base(void) static void pc87413_get_swc_base_addr(void)
{ {
unsigned int swc_base_addr = 0;
unsigned char addr_l, addr_h = 0; unsigned char addr_l, addr_h = 0;
/* Step 3: Read SWC I/O Base Address */ /* Step 3: Read SWC I/O Base Address */
...@@ -136,12 +136,11 @@ static inline unsigned int pc87413_get_swc_base(void) ...@@ -136,12 +136,11 @@ static inline unsigned int pc87413_get_swc_base(void)
"Read SWC I/O Base Address: low %d, high %d, res %d\n", "Read SWC I/O Base Address: low %d, high %d, res %d\n",
addr_l, addr_h, swc_base_addr); addr_l, addr_h, swc_base_addr);
#endif #endif
return swc_base_addr;
} }
/* Select Bank 3 of SWC */ /* Select Bank 3 of SWC */
static inline void pc87413_swc_bank3(unsigned int swc_base_addr) static inline void pc87413_swc_bank3(void)
{ {
/* Step 4: Select Bank3 of SWC */ /* Step 4: Select Bank3 of SWC */
outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f);
...@@ -152,8 +151,7 @@ static inline void pc87413_swc_bank3(unsigned int swc_base_addr) ...@@ -152,8 +151,7 @@ static inline void pc87413_swc_bank3(unsigned int swc_base_addr)
/* Set watchdog timeout to x minutes */ /* Set watchdog timeout to x minutes */
static inline void pc87413_programm_wdto(unsigned int swc_base_addr, static inline void pc87413_programm_wdto(char pc87413_time)
char pc87413_time)
{ {
/* Step 5: Programm WDTO, Twd. */ /* Step 5: Programm WDTO, Twd. */
outb_p(pc87413_time, swc_base_addr + WDTO); outb_p(pc87413_time, swc_base_addr + WDTO);
...@@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr, ...@@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr,
/* Enable WDEN */ /* Enable WDEN */
static inline void pc87413_enable_wden(unsigned int swc_base_addr) static inline void pc87413_enable_wden(void)
{ {
/* Step 6: Enable WDEN */ /* Step 6: Enable WDEN */
outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL);
...@@ -174,7 +172,7 @@ static inline void pc87413_enable_wden(unsigned int swc_base_addr) ...@@ -174,7 +172,7 @@ static inline void pc87413_enable_wden(unsigned int swc_base_addr)
} }
/* Enable SW_WD_TREN */ /* Enable SW_WD_TREN */
static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) static inline void pc87413_enable_sw_wd_tren(void)
{ {
/* Enable SW_WD_TREN */ /* Enable SW_WD_TREN */
outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG);
...@@ -185,7 +183,7 @@ static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) ...@@ -185,7 +183,7 @@ static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr)
/* Disable SW_WD_TREN */ /* Disable SW_WD_TREN */
static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) static inline void pc87413_disable_sw_wd_tren(void)
{ {
/* Disable SW_WD_TREN */ /* Disable SW_WD_TREN */
outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG);
...@@ -196,7 +194,7 @@ static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) ...@@ -196,7 +194,7 @@ static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr)
/* Enable SW_WD_TRG */ /* Enable SW_WD_TRG */
static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) static inline void pc87413_enable_sw_wd_trg(void)
{ {
/* Enable SW_WD_TRG */ /* Enable SW_WD_TRG */
outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL);
...@@ -207,7 +205,7 @@ static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) ...@@ -207,7 +205,7 @@ static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr)
/* Disable SW_WD_TRG */ /* Disable SW_WD_TRG */
static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) static inline void pc87413_disable_sw_wd_trg(void)
{ {
/* Disable SW_WD_TRG */ /* Disable SW_WD_TRG */
outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL);
...@@ -222,18 +220,13 @@ static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) ...@@ -222,18 +220,13 @@ static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr)
static void pc87413_enable(void) static void pc87413_enable(void)
{ {
unsigned int swc_base_addr;
spin_lock(&io_lock); spin_lock(&io_lock);
pc87413_select_wdt_out(); pc87413_swc_bank3();
pc87413_enable_swc(); pc87413_programm_wdto(timeout);
swc_base_addr = pc87413_get_swc_base(); pc87413_enable_wden();
pc87413_swc_bank3(swc_base_addr); pc87413_enable_sw_wd_tren();
pc87413_programm_wdto(swc_base_addr, timeout); pc87413_enable_sw_wd_trg();
pc87413_enable_wden(swc_base_addr);
pc87413_enable_sw_wd_tren(swc_base_addr);
pc87413_enable_sw_wd_trg(swc_base_addr);
spin_unlock(&io_lock); spin_unlock(&io_lock);
} }
...@@ -242,17 +235,12 @@ static void pc87413_enable(void) ...@@ -242,17 +235,12 @@ static void pc87413_enable(void)
static void pc87413_disable(void) static void pc87413_disable(void)
{ {
unsigned int swc_base_addr;
spin_lock(&io_lock); spin_lock(&io_lock);
pc87413_select_wdt_out(); pc87413_swc_bank3();
pc87413_enable_swc(); pc87413_disable_sw_wd_tren();
swc_base_addr = pc87413_get_swc_base(); pc87413_disable_sw_wd_trg();
pc87413_swc_bank3(swc_base_addr); pc87413_programm_wdto(0);
pc87413_disable_sw_wd_tren(swc_base_addr);
pc87413_disable_sw_wd_trg(swc_base_addr);
pc87413_programm_wdto(swc_base_addr, 0);
spin_unlock(&io_lock); spin_unlock(&io_lock);
} }
...@@ -261,20 +249,15 @@ static void pc87413_disable(void) ...@@ -261,20 +249,15 @@ static void pc87413_disable(void)
static void pc87413_refresh(void) static void pc87413_refresh(void)
{ {
unsigned int swc_base_addr;
spin_lock(&io_lock); spin_lock(&io_lock);
pc87413_select_wdt_out(); pc87413_swc_bank3();
pc87413_enable_swc(); pc87413_disable_sw_wd_tren();
swc_base_addr = pc87413_get_swc_base(); pc87413_disable_sw_wd_trg();
pc87413_swc_bank3(swc_base_addr); pc87413_programm_wdto(timeout);
pc87413_disable_sw_wd_tren(swc_base_addr); pc87413_enable_wden();
pc87413_disable_sw_wd_trg(swc_base_addr); pc87413_enable_sw_wd_tren();
pc87413_programm_wdto(swc_base_addr, timeout); pc87413_enable_sw_wd_trg();
pc87413_enable_wden(swc_base_addr);
pc87413_enable_sw_wd_tren(swc_base_addr);
pc87413_enable_sw_wd_trg(swc_base_addr);
spin_unlock(&io_lock); spin_unlock(&io_lock);
} }
...@@ -528,7 +511,8 @@ static int __init pc87413_init(void) ...@@ -528,7 +511,8 @@ static int __init pc87413_init(void)
printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n", printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",
WDT_INDEX_IO_PORT); WDT_INDEX_IO_PORT);
/* request_region(io, 2, "pc87413"); */ if (!request_muxed_region(io, 2, MODNAME))
return -EBUSY;
ret = register_reboot_notifier(&pc87413_notifier); ret = register_reboot_notifier(&pc87413_notifier);
if (ret != 0) { if (ret != 0) {
...@@ -541,12 +525,32 @@ static int __init pc87413_init(void) ...@@ -541,12 +525,32 @@ static int __init pc87413_init(void)
printk(KERN_ERR PFX printk(KERN_ERR PFX
"cannot register miscdev on minor=%d (err=%d)\n", "cannot register miscdev on minor=%d (err=%d)\n",
WATCHDOG_MINOR, ret); WATCHDOG_MINOR, ret);
unregister_reboot_notifier(&pc87413_notifier); goto reboot_unreg;
return ret;
} }
printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout);
pc87413_select_wdt_out();
pc87413_enable_swc();
pc87413_get_swc_base_addr();
if (!request_region(swc_base_addr, 0x20, MODNAME)) {
printk(KERN_ERR PFX
"cannot request SWC region at 0x%x\n", swc_base_addr);
ret = -EBUSY;
goto misc_unreg;
}
pc87413_enable(); pc87413_enable();
release_region(io, 2);
return 0; return 0;
misc_unreg:
misc_deregister(&pc87413_miscdev);
reboot_unreg:
unregister_reboot_notifier(&pc87413_notifier);
release_region(io, 2);
return ret;
} }
/** /**
...@@ -569,7 +573,7 @@ static void __exit pc87413_exit(void) ...@@ -569,7 +573,7 @@ static void __exit pc87413_exit(void)
misc_deregister(&pc87413_miscdev); misc_deregister(&pc87413_miscdev);
unregister_reboot_notifier(&pc87413_notifier); unregister_reboot_notifier(&pc87413_notifier);
/* release_region(io, 2); */ release_region(swc_base_addr, 0x20);
printk(KERN_INFO MODNAME " watchdog component driver removed.\n"); printk(KERN_INFO MODNAME " watchdog component driver removed.\n");
} }
......
...@@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev) ...@@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev)
#define s3c2410wdt_resume NULL #define s3c2410wdt_resume NULL
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
#ifdef CONFIG_OF
static const struct of_device_id s3c2410_wdt_match[] = {
{ .compatible = "samsung,s3c2410-wdt" },
{},
};
MODULE_DEVICE_TABLE(of, s3c2410_wdt_match);
#else
#define s3c2410_wdt_match NULL
#endif
static struct platform_driver s3c2410wdt_driver = { static struct platform_driver s3c2410wdt_driver = {
.probe = s3c2410wdt_probe, .probe = s3c2410wdt_probe,
...@@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = { ...@@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = {
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = "s3c2410-wdt", .name = "s3c2410-wdt",
.of_match_table = s3c2410_wdt_match,
}, },
}; };
......
...@@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev) ...@@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev)
sch311x_wdt_stop(); sch311x_wdt_stop();
} }
#define sch311x_wdt_suspend NULL
#define sch311x_wdt_resume NULL
static struct platform_driver sch311x_wdt_driver = { static struct platform_driver sch311x_wdt_driver = {
.probe = sch311x_wdt_probe, .probe = sch311x_wdt_probe,
.remove = __devexit_p(sch311x_wdt_remove), .remove = __devexit_p(sch311x_wdt_remove),
.shutdown = sch311x_wdt_shutdown, .shutdown = sch311x_wdt_shutdown,
.suspend = sch311x_wdt_suspend,
.resume = sch311x_wdt_resume,
.driver = { .driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.name = DRV_NAME, .name = DRV_NAME,
......
...@@ -134,6 +134,8 @@ static void wdt_enable(void) ...@@ -134,6 +134,8 @@ static void wdt_enable(void)
writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL); writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
writel(LOCK, wdt->base + WDTLOCK); writel(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */
readl(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock); spin_unlock(&wdt->lock);
} }
...@@ -144,9 +146,10 @@ static void wdt_disable(void) ...@@ -144,9 +146,10 @@ static void wdt_disable(void)
writel(UNLOCK, wdt->base + WDTLOCK); writel(UNLOCK, wdt->base + WDTLOCK);
writel(0, wdt->base + WDTCONTROL); writel(0, wdt->base + WDTCONTROL);
writel(0, wdt->base + WDTLOAD);
writel(LOCK, wdt->base + WDTLOCK); writel(LOCK, wdt->base + WDTLOCK);
/* Flush posted writes. */
readl(wdt->base + WDTLOCK);
spin_unlock(&wdt->lock); spin_unlock(&wdt->lock);
} }
......
/*
* watchdog_core.c
*
* (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
* All Rights Reserved.
*
* (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
*
* This source code is part of the generic code that can be used
* by all the watchdog timer drivers.
*
* Based on source code of the following authors:
* Matt Domsch <Matt_Domsch@dell.com>,
* Rob Radez <rob@osinvestor.com>,
* Rusty Lynch <rusty@linux.co.intel.com>
* Satyam Sharma <satyam@infradead.org>
* Randy Dunlap <randy.dunlap@oracle.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.
*
* Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
* admit liability nor provide warranty for any of this software.
* This material is provided "AS-IS" and at no charge.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h> /* For EXPORT_SYMBOL/module stuff/... */
#include <linux/types.h> /* For standard types */
#include <linux/errno.h> /* For the -ENODEV/... values */
#include <linux/kernel.h> /* For printk/panic/... */
#include <linux/watchdog.h> /* For watchdog specific items */
#include <linux/init.h> /* For __init/__exit/... */
#include "watchdog_dev.h" /* For watchdog_dev_register/... */
/**
* watchdog_register_device() - register a watchdog device
* @wdd: watchdog device
*
* Register a watchdog device with the kernel so that the
* watchdog timer can be accessed from userspace.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
int watchdog_register_device(struct watchdog_device *wdd)
{
int ret;
if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
return -EINVAL;
/* Mandatory operations need to be supported */
if (wdd->ops->start == NULL || wdd->ops->stop == NULL)
return -EINVAL;
/*
* Check that we have valid min and max timeout values, if
* not reset them both to 0 (=not used or unknown)
*/
if (wdd->min_timeout > wdd->max_timeout) {
pr_info("Invalid min and max timeout values, resetting to 0!\n");
wdd->min_timeout = 0;
wdd->max_timeout = 0;
}
/*
* Note: now that all watchdog_device data has been verified, we
* will not check this anymore in other functions. If data gets
* corrupted in a later stage then we expect a kernel panic!
*/
/* We only support 1 watchdog device via the /dev/watchdog interface */
ret = watchdog_dev_register(wdd);
if (ret) {
pr_err("error registering /dev/watchdog (err=%d).\n", ret);
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(watchdog_register_device);
/**
* watchdog_unregister_device() - unregister a watchdog device
* @wdd: watchdog device to unregister
*
* Unregister a watchdog device that was previously successfully
* registered with watchdog_register_device().
*/
void watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
if (wdd == NULL)
return;
ret = watchdog_dev_unregister(wdd);
if (ret)
pr_err("error unregistering /dev/watchdog (err=%d).\n", ret);
}
EXPORT_SYMBOL_GPL(watchdog_unregister_device);
MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
MODULE_DESCRIPTION("WatchDog Timer Driver Core");
MODULE_LICENSE("GPL");
This diff is collapsed.
/*
* watchdog_core.h
*
* (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
* All Rights Reserved.
*
* (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
*
* This source code is part of the generic code that can be used
* by all the watchdog timer drivers.
*
* Based on source code of the following authors:
* Matt Domsch <Matt_Domsch@dell.com>,
* Rob Radez <rob@osinvestor.com>,
* Rusty Lynch <rusty@linux.co.intel.com>
* Satyam Sharma <satyam@infradead.org>
* Randy Dunlap <randy.dunlap@oracle.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.
*
* Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
* admit liability nor provide warranty for any of this software.
* This material is provided "AS-IS" and at no charge.
*/
/*
* Functions/procedures to be called by the core
*/
int watchdog_dev_register(struct watchdog_device *);
int watchdog_dev_unregister(struct watchdog_device *);
...@@ -59,6 +59,84 @@ struct watchdog_info { ...@@ -59,6 +59,84 @@ struct watchdog_info {
#define WATCHDOG_NOWAYOUT 0 #define WATCHDOG_NOWAYOUT 0
#endif #endif
struct watchdog_ops;
struct watchdog_device;
/** struct watchdog_ops - The watchdog-devices operations
*
* @owner: The module owner.
* @start: The routine for starting the watchdog device.
* @stop: The routine for stopping the watchdog device.
* @ping: The routine that sends a keepalive ping to the watchdog device.
* @status: The routine that shows the status of the watchdog device.
* @set_timeout:The routine for setting the watchdog devices timeout value.
* @ioctl: The routines that handles extra ioctl calls.
*
* The watchdog_ops structure contains a list of low-level operations
* that control a watchdog device. It also contains the module that owns
* these operations. The start and stop function are mandatory, all other
* functions are optonal.
*/
struct watchdog_ops {
struct module *owner;
/* mandatory operations */
int (*start)(struct watchdog_device *);
int (*stop)(struct watchdog_device *);
/* optional operations */
int (*ping)(struct watchdog_device *);
unsigned int (*status)(struct watchdog_device *);
int (*set_timeout)(struct watchdog_device *, unsigned int);
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};
/** struct watchdog_device - The structure that defines a watchdog device
*
* @info: Pointer to a watchdog_info structure.
* @ops: Pointer to the list of watchdog operations.
* @bootstatus: Status of the watchdog device at boot.
* @timeout: The watchdog devices timeout value.
* @min_timeout:The watchdog devices minimum timeout value.
* @max_timeout:The watchdog devices maximum timeout value.
* @driver-data:Pointer to the drivers private data.
* @status: Field that contains the devices internal status bits.
*
* The watchdog_device structure contains all information about a
* watchdog timer device.
*
* The driver-data field may not be accessed directly. It must be accessed
* via the watchdog_set_drvdata and watchdog_get_drvdata helpers.
*/
struct watchdog_device {
const struct watchdog_info *info;
const struct watchdog_ops *ops;
unsigned int bootstatus;
unsigned int timeout;
unsigned int min_timeout;
unsigned int max_timeout;
void *driver_data;
unsigned long status;
/* Bit numbers for status flags */
#define WDOG_ACTIVE 0 /* Is the watchdog running/active */
#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */
#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */
#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */
};
/* Use the following functions to manipulate watchdog driver specific data */
static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data)
{
wdd->driver_data = data;
}
static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)
{
return wdd->driver_data;
}
/* drivers/watchdog/core/watchdog_core.c */
extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* ifndef _LINUX_WATCHDOG_H */ #endif /* ifndef _LINUX_WATCHDOG_H */
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