forked from luck/tmp_suning_uos_patched
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() ...
This commit is contained in:
commit
2313bcdcc9
14
Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
Normal file
14
Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt
Normal file
@ -0,0 +1,14 @@
|
||||
* 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>;
|
||||
};
|
11
Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
Normal file
11
Documentation/devicetree/bindings/watchdog/samsung-wdt.txt
Normal file
@ -0,0 +1,11 @@
|
||||
* 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/
|
||||
- directory holding watchdog related example programs.
|
||||
watchdog-api.txt
|
||||
- description of the Linux Watchdog driver API.
|
||||
watchdog-kernel-api.txt
|
||||
- description of the Linux WatchDog Timer Driver Core kernel API.
|
||||
watchdog-parameters.txt
|
||||
- information on driver parameters (for drivers other than
|
||||
the ones that have driver-specific files here)
|
||||
|
162
Documentation/watchdog/watchdog-kernel-api.txt
Normal file
162
Documentation/watchdog/watchdog-kernel-api.txt
Normal file
@ -0,0 +1,162 @@
|
||||
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
|
||||
|
||||
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
|
||||
bool "Disable watchdog shutdown on close"
|
||||
help
|
||||
@ -186,6 +197,15 @@ config SA1100_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
tristate "MPcore watchdog"
|
||||
depends on HAVE_ARM_TWD
|
||||
@ -321,7 +341,7 @@ config MAX63XX_WATCHDOG
|
||||
|
||||
config IMX2_WDT
|
||||
tristate "IMX2+ Watchdog"
|
||||
depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5
|
||||
depends on IMX_HAVE_PLATFORM_IMX2_WDT
|
||||
help
|
||||
This is the driver for the hardware watchdog
|
||||
on the Freescale IMX2 and later processors.
|
||||
@ -879,6 +899,20 @@ config M54xx_WATCHDOG
|
||||
To compile this driver as a module, choose M here: the
|
||||
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
|
||||
|
||||
config ATH79_WDT
|
||||
|
@ -2,6 +2,10 @@
|
||||
# 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
|
||||
# watchdog-cards first, then the architecture specific watchdog
|
||||
# drivers and then the architecture independent "softdog" driver.
|
||||
@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
|
||||
obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o
|
||||
obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_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_EP93XX_WATCHDOG) += ep93xx_wdt.o
|
||||
obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
|
||||
@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
|
||||
# M68K Architecture
|
||||
obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o
|
||||
|
||||
# MicroBlaze Architecture
|
||||
obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o
|
||||
|
||||
# MIPS Architecture
|
||||
obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o
|
||||
obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
|
||||
|
@ -31,7 +31,7 @@
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include <mach/at91_wdt.h>
|
||||
#include "at91sam9_wdt.h"
|
||||
|
||||
#define DRV_NAME "AT91SAM9 Watchdog"
|
||||
|
||||
@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.remove = __exit_p(at91wdt_remove),
|
||||
.suspend = at91wdt_suspend,
|
||||
.resume = at91wdt_resume,
|
||||
.driver = {
|
||||
.name = "at91_wdt",
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* arch/arm/mach-at91/include/mach/at91_wdt.h
|
||||
* drivers/watchdog/at91sam9_wdt.h
|
||||
*
|
||||
* Copyright (C) 2007 Andrew Victor
|
||||
* Copyright (C) 2007 Atmel Corporation.
|
376
drivers/watchdog/dw_wdt.c
Normal file
376
drivers/watchdog/dw_wdt.c
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* 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 @@
|
||||
#include <asm/cacheflush.h>
|
||||
#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 TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
|
||||
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
|
||||
@ -87,6 +87,19 @@ struct smbios_cru64_info {
|
||||
};
|
||||
#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 {
|
||||
union {
|
||||
struct {
|
||||
@ -132,6 +145,7 @@ struct cmn_registers {
|
||||
static unsigned int hpwdt_nmi_decoding;
|
||||
static unsigned int allow_kdump;
|
||||
static unsigned int priority; /* hpwdt at end of die_notify list */
|
||||
static unsigned int is_icru;
|
||||
static DEFINE_SPINLOCK(rom_lock);
|
||||
static void *cru_rom_addr;
|
||||
static struct cmn_registers cmn_regs;
|
||||
@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&rom_lock, rom_pl);
|
||||
if (!die_nmi_called)
|
||||
if (!die_nmi_called && !is_icru)
|
||||
asminline_call(&cmn_regs, cru_rom_addr);
|
||||
die_nmi_called = 1;
|
||||
spin_unlock_irqrestore(&rom_lock, rom_pl);
|
||||
if (cmn_regs.u1.ral == 0) {
|
||||
printk(KERN_WARNING "hpwdt: An NMI occurred, "
|
||||
"but unable to determine source.\n");
|
||||
} else {
|
||||
if (allow_kdump)
|
||||
hpwdt_stop();
|
||||
panic("An NMI occurred, please see the Integrated "
|
||||
"Management Log for details.\n");
|
||||
if (!is_icru) {
|
||||
if (cmn_regs.u1.ral == 0) {
|
||||
printk(KERN_WARNING "hpwdt: An NMI occurred, "
|
||||
"but unable to determine source.\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (allow_kdump)
|
||||
hpwdt_stop();
|
||||
panic("An NMI occurred, please see the Integrated "
|
||||
"Management Log for details.\n");
|
||||
|
||||
out:
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)
|
||||
}
|
||||
#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)
|
||||
{
|
||||
int retval;
|
||||
|
||||
/*
|
||||
* We need to map the ROM to get the CRU service.
|
||||
* 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.
|
||||
* On typical CRU-based systems we need to map that service in
|
||||
* the BIOS. 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.
|
||||
*
|
||||
* 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();
|
||||
if (retval < 0) {
|
||||
dev_warn(&dev->dev,
|
||||
"Unable to detect the %d Bit CRU Service.\n",
|
||||
HPWDT_ARCH);
|
||||
return retval;
|
||||
}
|
||||
dmi_walk(dmi_find_icru, NULL);
|
||||
if (!is_icru) {
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
cmn_regs.u1.rah = 0x0D;
|
||||
cmn_regs.u1.ral = 0x02;
|
||||
/*
|
||||
* We need to map the ROM to get the CRU service.
|
||||
* 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
|
||||
* few instructions as possible once the NMI comes in.
|
||||
*/
|
||||
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
|
||||
|
@ -120,72 +120,12 @@ enum iTCO_chipsets {
|
||||
TCO_3420, /* 3420 */
|
||||
TCO_3450, /* 3450 */
|
||||
TCO_EP80579, /* EP80579 */
|
||||
TCO_CPT1, /* Cougar Point */
|
||||
TCO_CPT2, /* Cougar Point Desktop */
|
||||
TCO_CPT3, /* Cougar Point Mobile */
|
||||
TCO_CPT4, /* Cougar Point */
|
||||
TCO_CPT5, /* Cougar Point */
|
||||
TCO_CPT6, /* Cougar Point */
|
||||
TCO_CPT7, /* Cougar Point */
|
||||
TCO_CPT8, /* Cougar Point */
|
||||
TCO_CPT9, /* Cougar Point */
|
||||
TCO_CPT10, /* Cougar Point */
|
||||
TCO_CPT11, /* Cougar Point */
|
||||
TCO_CPT12, /* Cougar Point */
|
||||
TCO_CPT13, /* Cougar Point */
|
||||
TCO_CPT14, /* Cougar Point */
|
||||
TCO_CPT15, /* Cougar Point */
|
||||
TCO_CPT16, /* Cougar Point */
|
||||
TCO_CPT17, /* Cougar Point */
|
||||
TCO_CPT18, /* Cougar Point */
|
||||
TCO_CPT19, /* Cougar Point */
|
||||
TCO_CPT20, /* Cougar Point */
|
||||
TCO_CPT21, /* Cougar Point */
|
||||
TCO_CPT22, /* Cougar Point */
|
||||
TCO_CPT23, /* Cougar Point */
|
||||
TCO_CPT24, /* Cougar Point */
|
||||
TCO_CPT25, /* Cougar Point */
|
||||
TCO_CPT26, /* Cougar Point */
|
||||
TCO_CPT27, /* Cougar Point */
|
||||
TCO_CPT28, /* Cougar Point */
|
||||
TCO_CPT29, /* Cougar Point */
|
||||
TCO_CPT30, /* Cougar Point */
|
||||
TCO_CPT31, /* Cougar Point */
|
||||
TCO_PBG1, /* Patsburg */
|
||||
TCO_PBG2, /* Patsburg */
|
||||
TCO_CPT, /* Cougar Point */
|
||||
TCO_CPTD, /* Cougar Point Desktop */
|
||||
TCO_CPTM, /* Cougar Point Mobile */
|
||||
TCO_PBG, /* Patsburg */
|
||||
TCO_DH89XXCC, /* DH89xxCC */
|
||||
TCO_PPT0, /* Panther Point */
|
||||
TCO_PPT1, /* Panther Point */
|
||||
TCO_PPT2, /* Panther Point */
|
||||
TCO_PPT3, /* Panther Point */
|
||||
TCO_PPT4, /* Panther Point */
|
||||
TCO_PPT5, /* Panther Point */
|
||||
TCO_PPT6, /* Panther Point */
|
||||
TCO_PPT7, /* Panther Point */
|
||||
TCO_PPT8, /* Panther Point */
|
||||
TCO_PPT9, /* Panther Point */
|
||||
TCO_PPT10, /* Panther Point */
|
||||
TCO_PPT11, /* Panther Point */
|
||||
TCO_PPT12, /* Panther Point */
|
||||
TCO_PPT13, /* Panther Point */
|
||||
TCO_PPT14, /* Panther Point */
|
||||
TCO_PPT15, /* Panther Point */
|
||||
TCO_PPT16, /* Panther Point */
|
||||
TCO_PPT17, /* Panther Point */
|
||||
TCO_PPT18, /* Panther Point */
|
||||
TCO_PPT19, /* Panther Point */
|
||||
TCO_PPT20, /* Panther Point */
|
||||
TCO_PPT21, /* Panther Point */
|
||||
TCO_PPT22, /* Panther Point */
|
||||
TCO_PPT23, /* Panther Point */
|
||||
TCO_PPT24, /* Panther Point */
|
||||
TCO_PPT25, /* Panther Point */
|
||||
TCO_PPT26, /* Panther Point */
|
||||
TCO_PPT27, /* Panther Point */
|
||||
TCO_PPT28, /* Panther Point */
|
||||
TCO_PPT29, /* Panther Point */
|
||||
TCO_PPT30, /* Panther Point */
|
||||
TCO_PPT31, /* Panther Point */
|
||||
TCO_PPT, /* Panther Point */
|
||||
};
|
||||
|
||||
static struct {
|
||||
@ -244,83 +184,14 @@ static struct {
|
||||
{"3450", 2},
|
||||
{"EP80579", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Cougar Point", 2},
|
||||
{"Patsburg", 2},
|
||||
{"Cougar Point Desktop", 2},
|
||||
{"Cougar Point Mobile", 2},
|
||||
{"Patsburg", 2},
|
||||
{"DH89xxCC", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{"Panther Point", 2},
|
||||
{NULL, 0}
|
||||
};
|
||||
|
||||
#define ITCO_PCI_DEVICE(dev, data) \
|
||||
.vendor = PCI_VENDOR_ID_INTEL, \
|
||||
.device = dev, \
|
||||
.subvendor = PCI_ANY_ID, \
|
||||
.subdevice = PCI_ANY_ID, \
|
||||
.class = 0, \
|
||||
.class_mask = 0, \
|
||||
.driver_data = data
|
||||
|
||||
/*
|
||||
* This data only exists for exporting the supported PCI ids
|
||||
* via MODULE_DEVICE_TABLE. We do not actually register a
|
||||
@ -328,138 +199,138 @@ static struct {
|
||||
* functions that probably will be registered by other drivers.
|
||||
*/
|
||||
static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = {
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0, TCO_ICH)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0, TCO_ICH0)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0, TCO_ICH2)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10, TCO_ICH2M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0, TCO_ICH3)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12, TCO_ICH3M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0, TCO_ICH4)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12, TCO_ICH4M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0, TCO_CICH)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0, TCO_ICH5)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1, TCO_6300ESB)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0, TCO_ICH6)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1, TCO_ICH6M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2, TCO_ICH6W)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2671, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2672, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2673, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2674, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2675, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2676, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2677, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2678, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x2679, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267a, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267b, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267c, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267d, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267e, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(0x267f, TCO_631XESB)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0, TCO_ICH7)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30, TCO_ICH7DH)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1, TCO_ICH7M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31, TCO_ICH7MDH)},
|
||||
{ ITCO_PCI_DEVICE(0x27bc, TCO_NM10)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0, TCO_ICH8)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2, TCO_ICH8DH)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3, TCO_ICH8DO)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4, TCO_ICH8M)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1, TCO_ICH8ME)},
|
||||
{ ITCO_PCI_DEVICE(0x2918, TCO_ICH9)},
|
||||
{ ITCO_PCI_DEVICE(0x2916, TCO_ICH9R)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2, TCO_ICH9DH)},
|
||||
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4, TCO_ICH9DO)},
|
||||
{ ITCO_PCI_DEVICE(0x2919, TCO_ICH9M)},
|
||||
{ ITCO_PCI_DEVICE(0x2917, TCO_ICH9ME)},
|
||||
{ ITCO_PCI_DEVICE(0x3a18, TCO_ICH10)},
|
||||
{ ITCO_PCI_DEVICE(0x3a16, TCO_ICH10R)},
|
||||
{ ITCO_PCI_DEVICE(0x3a1a, TCO_ICH10D)},
|
||||
{ ITCO_PCI_DEVICE(0x3a14, TCO_ICH10DO)},
|
||||
{ ITCO_PCI_DEVICE(0x3b00, TCO_PCH)},
|
||||
{ ITCO_PCI_DEVICE(0x3b01, TCO_PCHM)},
|
||||
{ ITCO_PCI_DEVICE(0x3b02, TCO_P55)},
|
||||
{ ITCO_PCI_DEVICE(0x3b03, TCO_PM55)},
|
||||
{ ITCO_PCI_DEVICE(0x3b06, TCO_H55)},
|
||||
{ ITCO_PCI_DEVICE(0x3b07, TCO_QM57)},
|
||||
{ ITCO_PCI_DEVICE(0x3b08, TCO_H57)},
|
||||
{ ITCO_PCI_DEVICE(0x3b09, TCO_HM55)},
|
||||
{ ITCO_PCI_DEVICE(0x3b0a, TCO_Q57)},
|
||||
{ ITCO_PCI_DEVICE(0x3b0b, TCO_HM57)},
|
||||
{ ITCO_PCI_DEVICE(0x3b0d, TCO_PCHMSFF)},
|
||||
{ ITCO_PCI_DEVICE(0x3b0f, TCO_QS57)},
|
||||
{ ITCO_PCI_DEVICE(0x3b12, TCO_3400)},
|
||||
{ ITCO_PCI_DEVICE(0x3b14, TCO_3420)},
|
||||
{ ITCO_PCI_DEVICE(0x3b16, TCO_3450)},
|
||||
{ ITCO_PCI_DEVICE(0x5031, TCO_EP80579)},
|
||||
{ ITCO_PCI_DEVICE(0x1c41, TCO_CPT1)},
|
||||
{ ITCO_PCI_DEVICE(0x1c42, TCO_CPT2)},
|
||||
{ ITCO_PCI_DEVICE(0x1c43, TCO_CPT3)},
|
||||
{ ITCO_PCI_DEVICE(0x1c44, TCO_CPT4)},
|
||||
{ ITCO_PCI_DEVICE(0x1c45, TCO_CPT5)},
|
||||
{ ITCO_PCI_DEVICE(0x1c46, TCO_CPT6)},
|
||||
{ ITCO_PCI_DEVICE(0x1c47, TCO_CPT7)},
|
||||
{ ITCO_PCI_DEVICE(0x1c48, TCO_CPT8)},
|
||||
{ ITCO_PCI_DEVICE(0x1c49, TCO_CPT9)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4a, TCO_CPT10)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4b, TCO_CPT11)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4c, TCO_CPT12)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4d, TCO_CPT13)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4e, TCO_CPT14)},
|
||||
{ ITCO_PCI_DEVICE(0x1c4f, TCO_CPT15)},
|
||||
{ ITCO_PCI_DEVICE(0x1c50, TCO_CPT16)},
|
||||
{ ITCO_PCI_DEVICE(0x1c51, TCO_CPT17)},
|
||||
{ ITCO_PCI_DEVICE(0x1c52, TCO_CPT18)},
|
||||
{ ITCO_PCI_DEVICE(0x1c53, TCO_CPT19)},
|
||||
{ ITCO_PCI_DEVICE(0x1c54, TCO_CPT20)},
|
||||
{ ITCO_PCI_DEVICE(0x1c55, TCO_CPT21)},
|
||||
{ ITCO_PCI_DEVICE(0x1c56, TCO_CPT22)},
|
||||
{ ITCO_PCI_DEVICE(0x1c57, TCO_CPT23)},
|
||||
{ ITCO_PCI_DEVICE(0x1c58, TCO_CPT24)},
|
||||
{ ITCO_PCI_DEVICE(0x1c59, TCO_CPT25)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5a, TCO_CPT26)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5b, TCO_CPT27)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5c, TCO_CPT28)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5d, TCO_CPT29)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5e, TCO_CPT30)},
|
||||
{ ITCO_PCI_DEVICE(0x1c5f, TCO_CPT31)},
|
||||
{ ITCO_PCI_DEVICE(0x1d40, TCO_PBG1)},
|
||||
{ ITCO_PCI_DEVICE(0x1d41, TCO_PBG2)},
|
||||
{ ITCO_PCI_DEVICE(0x2310, TCO_DH89XXCC)},
|
||||
{ ITCO_PCI_DEVICE(0x1e40, TCO_PPT0)},
|
||||
{ ITCO_PCI_DEVICE(0x1e41, TCO_PPT1)},
|
||||
{ ITCO_PCI_DEVICE(0x1e42, TCO_PPT2)},
|
||||
{ ITCO_PCI_DEVICE(0x1e43, TCO_PPT3)},
|
||||
{ ITCO_PCI_DEVICE(0x1e44, TCO_PPT4)},
|
||||
{ ITCO_PCI_DEVICE(0x1e45, TCO_PPT5)},
|
||||
{ ITCO_PCI_DEVICE(0x1e46, TCO_PPT6)},
|
||||
{ ITCO_PCI_DEVICE(0x1e47, TCO_PPT7)},
|
||||
{ ITCO_PCI_DEVICE(0x1e48, TCO_PPT8)},
|
||||
{ ITCO_PCI_DEVICE(0x1e49, TCO_PPT9)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4a, TCO_PPT10)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4b, TCO_PPT11)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4c, TCO_PPT12)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4d, TCO_PPT13)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4e, TCO_PPT14)},
|
||||
{ ITCO_PCI_DEVICE(0x1e4f, TCO_PPT15)},
|
||||
{ ITCO_PCI_DEVICE(0x1e50, TCO_PPT16)},
|
||||
{ ITCO_PCI_DEVICE(0x1e51, TCO_PPT17)},
|
||||
{ ITCO_PCI_DEVICE(0x1e52, TCO_PPT18)},
|
||||
{ ITCO_PCI_DEVICE(0x1e53, TCO_PPT19)},
|
||||
{ ITCO_PCI_DEVICE(0x1e54, TCO_PPT20)},
|
||||
{ ITCO_PCI_DEVICE(0x1e55, TCO_PPT21)},
|
||||
{ ITCO_PCI_DEVICE(0x1e56, TCO_PPT22)},
|
||||
{ ITCO_PCI_DEVICE(0x1e57, TCO_PPT23)},
|
||||
{ ITCO_PCI_DEVICE(0x1e58, TCO_PPT24)},
|
||||
{ ITCO_PCI_DEVICE(0x1e59, TCO_PPT25)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5a, TCO_PPT26)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5b, TCO_PPT27)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5c, TCO_PPT28)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5d, TCO_PPT29)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5e, TCO_PPT30)},
|
||||
{ ITCO_PCI_DEVICE(0x1e5f, TCO_PPT31)},
|
||||
{ PCI_VDEVICE(INTEL, 0x2410), TCO_ICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0},
|
||||
{ PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2},
|
||||
{ PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3},
|
||||
{ PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M},
|
||||
{ PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4},
|
||||
{ PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2450), TCO_CICH},
|
||||
{ PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5},
|
||||
{ PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6},
|
||||
{ PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W},
|
||||
{ PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH},
|
||||
{ PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10},
|
||||
{ PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8},
|
||||
{ PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9},
|
||||
{ PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R},
|
||||
{ PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH},
|
||||
{ PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M},
|
||||
{ PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D},
|
||||
{ PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b02), TCO_P55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b06), TCO_H55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b08), TCO_H57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b12), TCO_3400},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b14), TCO_3420},
|
||||
{ PCI_VDEVICE(INTEL, 0x3b16), TCO_3450},
|
||||
{ PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG},
|
||||
{ PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT},
|
||||
{ PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},
|
||||
{ 0, }, /* End of list */
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl);
|
||||
@ -1052,15 +923,10 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)
|
||||
iTCO_wdt_stop();
|
||||
}
|
||||
|
||||
#define iTCO_wdt_suspend NULL
|
||||
#define iTCO_wdt_resume NULL
|
||||
|
||||
static struct platform_driver iTCO_wdt_driver = {
|
||||
.probe = iTCO_wdt_probe,
|
||||
.remove = __devexit_p(iTCO_wdt_remove),
|
||||
.shutdown = iTCO_wdt_shutdown,
|
||||
.suspend = iTCO_wdt_suspend,
|
||||
.resume = iTCO_wdt_resume,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
|
@ -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 = {
|
||||
.remove = __exit_p(imx2_wdt_remove),
|
||||
.shutdown = imx2_wdt_shutdown,
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = imx2_wdt_dt_ids,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -28,10 +28,10 @@
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
#define NAME "it8712f_wdt"
|
||||
|
||||
@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
|
||||
|
||||
static unsigned long wdt_open;
|
||||
static unsigned expect_close;
|
||||
static spinlock_t io_lock;
|
||||
static unsigned char revision;
|
||||
|
||||
/* Dog Food address - We use the game port address */
|
||||
@ -121,20 +120,26 @@ static inline void superio_select(int ldn)
|
||||
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(0x01, REG);
|
||||
outb(0x55, REG);
|
||||
outb(0x55, REG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void superio_exit(void)
|
||||
{
|
||||
outb(0x02, REG);
|
||||
outb(0x02, VAL);
|
||||
spin_unlock(&io_lock);
|
||||
release_region(REG, 2);
|
||||
}
|
||||
|
||||
static inline void it8712f_wdt_ping(void)
|
||||
@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void)
|
||||
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");
|
||||
superio_enter();
|
||||
superio_select(LDN_GPIO);
|
||||
|
||||
superio_outb(wdt_control_reg, WDT_CONTROL);
|
||||
@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void)
|
||||
superio_exit();
|
||||
|
||||
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_outb(0, WDT_CONFIG);
|
||||
@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void)
|
||||
superio_outb(0, WDT_TIMEOUT);
|
||||
|
||||
superio_exit();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int it8712f_wdt_notify(struct notifier_block *this,
|
||||
@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
WDIOF_MAGICCLOSE,
|
||||
};
|
||||
int value;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
return -EFAULT;
|
||||
return 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
superio_enter();
|
||||
ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
superio_select(LDN_GPIO);
|
||||
|
||||
value = it8712f_wdt_get_status();
|
||||
@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
if (value > (max_units * 60))
|
||||
return -EINVAL;
|
||||
margin = value;
|
||||
superio_enter();
|
||||
ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
superio_select(LDN_GPIO);
|
||||
|
||||
it8712f_wdt_update_margin();
|
||||
@ -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)
|
||||
{
|
||||
int ret;
|
||||
/* only allow one at a time */
|
||||
if (test_and_set_bit(0, &wdt_open))
|
||||
return -EBUSY;
|
||||
it8712f_wdt_enable();
|
||||
|
||||
ret = it8712f_wdt_enable();
|
||||
if (ret)
|
||||
return ret;
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file)
|
||||
": watchdog device closed unexpectedly, will not"
|
||||
" disable the watchdog timer\n");
|
||||
} else if (!nowayout) {
|
||||
it8712f_wdt_disable();
|
||||
if (it8712f_wdt_disable())
|
||||
printk(KERN_WARNING NAME "Watchdog disable failed\n");
|
||||
}
|
||||
expect_close = 0;
|
||||
clear_bit(0, &wdt_open);
|
||||
@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
int chip_type;
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_enter();
|
||||
chip_type = superio_inw(DEVID);
|
||||
if (chip_type != IT8712F_DEVID)
|
||||
goto exit;
|
||||
@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
spin_lock_init(&io_lock);
|
||||
|
||||
if (it8712f_wdt_find(&address))
|
||||
return -ENODEV;
|
||||
|
||||
@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void)
|
||||
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);
|
||||
if (err) {
|
||||
|
@ -137,7 +137,6 @@
|
||||
|
||||
static unsigned int base, gpact, ciract, max_units, chip_type;
|
||||
static unsigned long wdt_status;
|
||||
static DEFINE_SPINLOCK(spinlock);
|
||||
|
||||
static int nogameport = DEFAULT_NOGAMEPORT;
|
||||
static int exclusive = DEFAULT_EXCLUSIVE;
|
||||
@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="
|
||||
|
||||
/* 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(0x01, REG);
|
||||
outb(0x55, REG);
|
||||
outb(0x55, REG);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void superio_exit(void)
|
||||
{
|
||||
outb(0x02, REG);
|
||||
outb(0x02, VAL);
|
||||
release_region(REG, 2);
|
||||
}
|
||||
|
||||
static inline void superio_select(int ldn)
|
||||
@ -255,12 +262,11 @@ static void wdt_keepalive(void)
|
||||
set_bit(WDTS_KEEPALIVE, &wdt_status);
|
||||
}
|
||||
|
||||
static void wdt_start(void)
|
||||
static int wdt_start(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
if (test_bit(WDTS_USE_GP, &wdt_status))
|
||||
@ -270,15 +276,15 @@ static void wdt_start(void)
|
||||
wdt_update_timeout();
|
||||
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void wdt_stop(void)
|
||||
static int wdt_stop(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
@ -288,7 +294,7 @@ static void wdt_stop(void)
|
||||
superio_outb(0x00, WDTVALMSB);
|
||||
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -303,8 +309,6 @@ static void wdt_stop(void)
|
||||
|
||||
static int wdt_set_timeout(int t)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (t < 1 || t > max_units * 60)
|
||||
return -EINVAL;
|
||||
|
||||
@ -313,14 +317,15 @@ static int wdt_set_timeout(int t)
|
||||
else
|
||||
timeout = t;
|
||||
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
|
||||
superio_enter();
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
wdt_update_timeout();
|
||||
superio_exit();
|
||||
}
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -339,12 +344,12 @@ static int wdt_set_timeout(int t)
|
||||
|
||||
static int wdt_get_status(int *status)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
*status = 0;
|
||||
if (testmode) {
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
int ret = superio_enter();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
superio_select(GPIO);
|
||||
if (superio_inb(WDTCTRL) & WDT_ZERO) {
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
@ -353,7 +358,6 @@ static int wdt_get_status(int *status)
|
||||
}
|
||||
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
}
|
||||
if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))
|
||||
*status |= WDIOF_KEEPALIVEPING;
|
||||
@ -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))
|
||||
return -EBUSY;
|
||||
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
|
||||
int ret;
|
||||
if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))
|
||||
__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);
|
||||
}
|
||||
@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_bit(WDTS_TIMER_RUN, &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);
|
||||
} else {
|
||||
wdt_keepalive();
|
||||
@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
&ident, sizeof(ident)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
wdt_get_status(&status);
|
||||
rc = wdt_get_status(&status);
|
||||
if (rc)
|
||||
return rc;
|
||||
return put_user(status, uarg.i);
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
|
||||
switch (new_options) {
|
||||
case WDIOS_DISABLECARD:
|
||||
if (test_bit(WDTS_TIMER_RUN, &wdt_status))
|
||||
wdt_stop();
|
||||
if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {
|
||||
rc = wdt_stop();
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
clear_bit(WDTS_TIMER_RUN, &wdt_status);
|
||||
return 0;
|
||||
|
||||
case WDIOS_ENABLECARD:
|
||||
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status))
|
||||
wdt_start();
|
||||
if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) {
|
||||
rc = wdt_start();
|
||||
if (rc) {
|
||||
clear_bit(WDTS_TIMER_RUN, &wdt_status);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
default:
|
||||
@ -560,16 +591,17 @@ static int __init it87_wdt_init(void)
|
||||
int rc = 0;
|
||||
int try_gameport = !nogameport;
|
||||
u8 chip_rev;
|
||||
unsigned long flags;
|
||||
int gp_rreq_fail = 0;
|
||||
|
||||
wdt_status = 0;
|
||||
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
rc = superio_enter();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
chip_type = superio_inw(CHIPID);
|
||||
chip_rev = superio_inb(CHIPREV) & 0x0f;
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
|
||||
switch (chip_type) {
|
||||
case IT8702_ID:
|
||||
@ -603,8 +635,9 @@ static int __init it87_wdt_init(void)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
rc = superio_enter();
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
superio_select(GPIO);
|
||||
superio_outb(WDT_TOV1, WDTCFG);
|
||||
@ -620,21 +653,16 @@ static int __init it87_wdt_init(void)
|
||||
}
|
||||
gpact = superio_inb(ACTREG);
|
||||
superio_outb(0x01, ACTREG);
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
if (request_region(base, 1, WATCHDOG_NAME))
|
||||
set_bit(WDTS_USE_GP, &wdt_status);
|
||||
else
|
||||
rc = -EIO;
|
||||
} else {
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
gp_rreq_fail = 1;
|
||||
}
|
||||
|
||||
/* If we haven't Gameport support, try to get CIR support */
|
||||
if (!test_bit(WDTS_USE_GP, &wdt_status)) {
|
||||
if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) {
|
||||
if (rc == -EIO)
|
||||
if (gp_rreq_fail)
|
||||
printk(KERN_ERR PFX
|
||||
"I/O Address 0x%04x and 0x%04x"
|
||||
" already in use\n", base, CIR_BASE);
|
||||
@ -646,21 +674,16 @@ static int __init it87_wdt_init(void)
|
||||
goto err_out;
|
||||
}
|
||||
base = CIR_BASE;
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
|
||||
superio_select(CIR);
|
||||
superio_outw(base, BASEREG);
|
||||
superio_outb(0x00, CIR_ILS);
|
||||
ciract = superio_inb(ACTREG);
|
||||
superio_outb(0x01, ACTREG);
|
||||
if (rc == -EIO) {
|
||||
if (gp_rreq_fail) {
|
||||
superio_select(GAMEPORT);
|
||||
superio_outb(gpact, ACTREG);
|
||||
}
|
||||
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
}
|
||||
|
||||
if (timeout < 1 || timeout > max_units * 60) {
|
||||
@ -704,6 +727,7 @@ static int __init it87_wdt_init(void)
|
||||
"nogameport=%d)\n", chip_type, chip_rev, timeout,
|
||||
nowayout, testmode, exclusive, nogameport);
|
||||
|
||||
superio_exit();
|
||||
return 0;
|
||||
|
||||
err_out_reboot:
|
||||
@ -711,49 +735,37 @@ static int __init it87_wdt_init(void)
|
||||
err_out_region:
|
||||
release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);
|
||||
if (!test_bit(WDTS_USE_GP, &wdt_status)) {
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
superio_select(CIR);
|
||||
superio_outb(ciract, ACTREG);
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
}
|
||||
err_out:
|
||||
if (try_gameport) {
|
||||
spin_lock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
superio_select(GAMEPORT);
|
||||
superio_outb(gpact, ACTREG);
|
||||
superio_exit();
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
}
|
||||
|
||||
superio_exit();
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void __exit it87_wdt_exit(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int nolock;
|
||||
|
||||
nolock = !spin_trylock_irqsave(&spinlock, flags);
|
||||
superio_enter();
|
||||
superio_select(GPIO);
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
superio_outb(0x00, WDTCFG);
|
||||
superio_outb(0x00, WDTVALLSB);
|
||||
if (max_units > 255)
|
||||
superio_outb(0x00, WDTVALMSB);
|
||||
if (test_bit(WDTS_USE_GP, &wdt_status)) {
|
||||
superio_select(GAMEPORT);
|
||||
superio_outb(gpact, ACTREG);
|
||||
} else {
|
||||
superio_select(CIR);
|
||||
superio_outb(ciract, ACTREG);
|
||||
if (superio_enter() == 0) {
|
||||
superio_select(GPIO);
|
||||
superio_outb(0x00, WDTCTRL);
|
||||
superio_outb(0x00, WDTCFG);
|
||||
superio_outb(0x00, WDTVALLSB);
|
||||
if (max_units > 255)
|
||||
superio_outb(0x00, WDTVALMSB);
|
||||
if (test_bit(WDTS_USE_GP, &wdt_status)) {
|
||||
superio_select(GAMEPORT);
|
||||
superio_outb(gpact, ACTREG);
|
||||
} else {
|
||||
superio_select(CIR);
|
||||
superio_outb(ciract, ACTREG);
|
||||
}
|
||||
superio_exit();
|
||||
}
|
||||
superio_exit();
|
||||
if (!nolock)
|
||||
spin_unlock_irqrestore(&spinlock, flags);
|
||||
|
||||
misc_deregister(&wdt_miscdev);
|
||||
unregister_reboot_notifier(&wdt_notifier);
|
||||
|
@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev)
|
||||
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 */
|
||||
MODULE_ALIAS("platform:mpcore_wdt");
|
||||
|
||||
static struct platform_driver mpcore_wdt_driver = {
|
||||
.probe = mpcore_wdt_probe,
|
||||
.remove = __devexit_p(mpcore_wdt_remove),
|
||||
.suspend = mpcore_wdt_suspend,
|
||||
.resume = mpcore_wdt_resume,
|
||||
.shutdown = mpcore_wdt_shutdown,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -225,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev)
|
||||
|
||||
ret = misc_register(&mtx1_wdt_misc);
|
||||
if (ret < 0) {
|
||||
printk(KERN_ERR " mtx-1_wdt : failed to register\n");
|
||||
dev_err(&pdev->dev, "failed to register\n");
|
||||
return ret;
|
||||
}
|
||||
mtx1_wdt_start();
|
||||
printk(KERN_INFO "MTX-1 Watchdog driver\n");
|
||||
dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
433
drivers/watchdog/of_xilinx_wdt.c
Normal file
433
drivers/watchdog/of_xilinx_wdt.c
Normal file
@ -0,0 +1,433 @@
|
||||
/*
|
||||
* of_xilinx_wdt.c 1.01 A Watchdog Device Driver for Xilinx xps_timebase_wdt
|
||||
*
|
||||
* (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
|
||||
*
|
||||
* -----------------------
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* -----------------------
|
||||
* 30-May-2011 Alejandro Cabrera <aldaya@gmail.com>
|
||||
* - If "xlnx,wdt-enable-once" wasn't found on device tree the
|
||||
* module will use CONFIG_WATCHDOG_NOWAYOUT
|
||||
* - If the device tree parameters ("clock-frequency" and
|
||||
* "xlnx,wdt-interval") wasn't found the driver won't
|
||||
* know the wdt reset interval
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
/* Register offsets for the Wdt device */
|
||||
#define XWT_TWCSR0_OFFSET 0x0 /* Control/Status Register0 */
|
||||
#define XWT_TWCSR1_OFFSET 0x4 /* Control/Status Register1 */
|
||||
#define XWT_TBR_OFFSET 0x8 /* Timebase Register Offset */
|
||||
|
||||
/* Control/Status Register Masks */
|
||||
#define XWT_CSR0_WRS_MASK 0x00000008 /* Reset status */
|
||||
#define XWT_CSR0_WDS_MASK 0x00000004 /* Timer state */
|
||||
#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
|
||||
|
||||
/* Control/Status Register 0/1 bits */
|
||||
#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
|
||||
|
||||
/* SelfTest constants */
|
||||
#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
|
||||
#define XWT_TIMER_FAILED 0xFFFFFFFF
|
||||
|
||||
#define WATCHDOG_NAME "Xilinx Watchdog"
|
||||
#define PFX WATCHDOG_NAME ": "
|
||||
|
||||
struct xwdt_device {
|
||||
struct resource res;
|
||||
void __iomem *base;
|
||||
u32 nowayout;
|
||||
u32 wdt_interval;
|
||||
u32 boot_status;
|
||||
};
|
||||
|
||||
static struct xwdt_device xdev;
|
||||
|
||||
static u32 timeout;
|
||||
static u32 control_status_reg;
|
||||
static u8 expect_close;
|
||||
static u8 no_timeout;
|
||||
static unsigned long driver_open;
|
||||
|
||||
static DEFINE_SPINLOCK(spinlock);
|
||||
|
||||
static void xwdt_start(void)
|
||||
{
|
||||
spin_lock(&spinlock);
|
||||
|
||||
/* Clean previous status and enable the watchdog timer */
|
||||
control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
|
||||
control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
|
||||
|
||||
iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
|
||||
xdev.base + XWT_TWCSR0_OFFSET);
|
||||
|
||||
iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET);
|
||||
|
||||
spin_unlock(&spinlock);
|
||||
}
|
||||
|
||||
static void xwdt_stop(void)
|
||||
{
|
||||
spin_lock(&spinlock);
|
||||
|
||||
control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
|
||||
|
||||
iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
|
||||
xdev.base + XWT_TWCSR0_OFFSET);
|
||||
|
||||
iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET);
|
||||
|
||||
spin_unlock(&spinlock);
|
||||
printk(KERN_INFO PFX "Stopped!\n");
|
||||
}
|
||||
|
||||
static void xwdt_keepalive(void)
|
||||
{
|
||||
spin_lock(&spinlock);
|
||||
|
||||
control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
|
||||
control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
|
||||
iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET);
|
||||
|
||||
spin_unlock(&spinlock);
|
||||
}
|
||||
|
||||
static void xwdt_get_status(int *status)
|
||||
{
|
||||
int new_status;
|
||||
|
||||
spin_lock(&spinlock);
|
||||
|
||||
control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET);
|
||||
new_status = ((control_status_reg &
|
||||
(XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0);
|
||||
spin_unlock(&spinlock);
|
||||
|
||||
*status = 0;
|
||||
if (new_status & 1)
|
||||
*status |= WDIOF_CARDRESET;
|
||||
}
|
||||
|
||||
static u32 xwdt_selftest(void)
|
||||
{
|
||||
int i;
|
||||
u32 timer_value1;
|
||||
u32 timer_value2;
|
||||
|
||||
spin_lock(&spinlock);
|
||||
|
||||
timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET);
|
||||
timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
|
||||
|
||||
for (i = 0;
|
||||
((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
|
||||
(timer_value2 == timer_value1)); i++) {
|
||||
timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET);
|
||||
}
|
||||
|
||||
spin_unlock(&spinlock);
|
||||
|
||||
if (timer_value2 != timer_value1)
|
||||
return ~XWT_TIMER_FAILED;
|
||||
else
|
||||
return XWT_TIMER_FAILED;
|
||||
}
|
||||
|
||||
static int xwdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* Only one process can handle the wdt at a time */
|
||||
if (test_and_set_bit(0, &driver_open))
|
||||
return -EBUSY;
|
||||
|
||||
/* Make sure that the module are always loaded...*/
|
||||
if (xdev.nowayout)
|
||||
__module_get(THIS_MODULE);
|
||||
|
||||
xwdt_start();
|
||||
printk(KERN_INFO PFX "Started...\n");
|
||||
|
||||
return nonseekable_open(inode, file);
|
||||
}
|
||||
|
||||
static int xwdt_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (expect_close == 42) {
|
||||
xwdt_stop();
|
||||
} else {
|
||||
printk(KERN_CRIT PFX
|
||||
"Unexpected close, not stopping watchdog!\n");
|
||||
xwdt_keepalive();
|
||||
}
|
||||
|
||||
clear_bit(0, &driver_open);
|
||||
expect_close = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* xwdt_write:
|
||||
* @file: file handle to the watchdog
|
||||
* @buf: buffer to write (unused as data does not matter here
|
||||
* @count: count of bytes
|
||||
* @ppos: pointer to the position to write. No seeks allowed
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive signal. Any
|
||||
* write of data will do, as we don't define content meaning.
|
||||
*/
|
||||
static ssize_t xwdt_write(struct file *file, const char __user *buf,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!xdev.nowayout) {
|
||||
size_t i;
|
||||
|
||||
/* In case it was set long ago */
|
||||
expect_close = 0;
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
|
||||
if (get_user(c, buf + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
expect_close = 42;
|
||||
}
|
||||
}
|
||||
xwdt_keepalive();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_MAGICCLOSE |
|
||||
WDIOF_KEEPALIVEPING,
|
||||
.firmware_version = 1,
|
||||
.identity = WATCHDOG_NAME,
|
||||
};
|
||||
|
||||
/*
|
||||
* xwdt_ioctl:
|
||||
* @file: file handle to the device
|
||||
* @cmd: watchdog command
|
||||
* @arg: argument pointer
|
||||
*
|
||||
* The watchdog API defines a common set of functions for all watchdogs
|
||||
* according to their available features.
|
||||
*/
|
||||
static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
int status;
|
||||
|
||||
union {
|
||||
struct watchdog_info __user *ident;
|
||||
int __user *i;
|
||||
} uarg;
|
||||
|
||||
uarg.i = (int __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(uarg.ident, &ident,
|
||||
sizeof(ident)) ? -EFAULT : 0;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(xdev.boot_status, uarg.i);
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
xwdt_get_status(&status);
|
||||
return put_user(status, uarg.i);
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
xwdt_keepalive();
|
||||
return 0;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
if (no_timeout)
|
||||
return -ENOTTY;
|
||||
else
|
||||
return put_user(timeout, uarg.i);
|
||||
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct file_operations xwdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = xwdt_write,
|
||||
.open = xwdt_open,
|
||||
.release = xwdt_release,
|
||||
.unlocked_ioctl = xwdt_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice xwdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &xwdt_fops,
|
||||
};
|
||||
|
||||
static int __devinit xwdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
u32 *tmptr;
|
||||
u32 *pfreq;
|
||||
|
||||
no_timeout = 0;
|
||||
|
||||
pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent,
|
||||
"clock-frequency", NULL);
|
||||
|
||||
if (pfreq == NULL) {
|
||||
printk(KERN_WARNING PFX
|
||||
"The watchdog clock frequency cannot be obtained!\n");
|
||||
no_timeout = 1;
|
||||
}
|
||||
|
||||
rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res);
|
||||
if (rc) {
|
||||
printk(KERN_WARNING PFX "invalid address!\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
tmptr = (u32 *)of_get_property(pdev->dev.of_node,
|
||||
"xlnx,wdt-interval", NULL);
|
||||
if (tmptr == NULL) {
|
||||
printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\""
|
||||
" not found in device tree!\n");
|
||||
no_timeout = 1;
|
||||
} else {
|
||||
xdev.wdt_interval = *tmptr;
|
||||
}
|
||||
|
||||
tmptr = (u32 *)of_get_property(pdev->dev.of_node,
|
||||
"xlnx,wdt-enable-once", NULL);
|
||||
if (tmptr == NULL) {
|
||||
printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\""
|
||||
" not found in device tree!\n");
|
||||
xdev.nowayout = WATCHDOG_NOWAYOUT;
|
||||
}
|
||||
|
||||
/*
|
||||
* Twice of the 2^wdt_interval / freq because the first wdt overflow is
|
||||
* ignored (interrupt), reset is only generated at second wdt overflow
|
||||
*/
|
||||
if (!no_timeout)
|
||||
timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq);
|
||||
|
||||
if (!request_mem_region(xdev.res.start,
|
||||
xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) {
|
||||
rc = -ENXIO;
|
||||
printk(KERN_ERR PFX "memory request failure!\n");
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1);
|
||||
if (xdev.base == NULL) {
|
||||
rc = -ENOMEM;
|
||||
printk(KERN_ERR PFX "ioremap failure!\n");
|
||||
goto release_mem;
|
||||
}
|
||||
|
||||
rc = xwdt_selftest();
|
||||
if (rc == XWT_TIMER_FAILED) {
|
||||
printk(KERN_ERR PFX "SelfTest routine error!\n");
|
||||
goto unmap_io;
|
||||
}
|
||||
|
||||
xwdt_get_status(&xdev.boot_status);
|
||||
|
||||
rc = misc_register(&xwdt_miscdev);
|
||||
if (rc) {
|
||||
printk(KERN_ERR PFX
|
||||
"cannot register miscdev on minor=%d (err=%d)\n",
|
||||
xwdt_miscdev.minor, rc);
|
||||
goto unmap_io;
|
||||
}
|
||||
|
||||
if (no_timeout)
|
||||
printk(KERN_INFO PFX
|
||||
"driver loaded (timeout=? sec, nowayout=%d)\n",
|
||||
xdev.nowayout);
|
||||
else
|
||||
printk(KERN_INFO PFX
|
||||
"driver loaded (timeout=%d sec, nowayout=%d)\n",
|
||||
timeout, xdev.nowayout);
|
||||
|
||||
expect_close = 0;
|
||||
clear_bit(0, &driver_open);
|
||||
|
||||
return 0;
|
||||
|
||||
unmap_io:
|
||||
iounmap(xdev.base);
|
||||
release_mem:
|
||||
release_mem_region(xdev.res.start, resource_size(&xdev.res));
|
||||
err_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit xwdt_remove(struct platform_device *dev)
|
||||
{
|
||||
misc_deregister(&xwdt_miscdev);
|
||||
iounmap(xdev.base);
|
||||
release_mem_region(xdev.res.start, resource_size(&xdev.res));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Match table for of_platform binding */
|
||||
static struct of_device_id __devinitdata xwdt_of_match[] = {
|
||||
{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, xwdt_of_match);
|
||||
|
||||
static struct platform_driver xwdt_driver = {
|
||||
.probe = xwdt_probe,
|
||||
.remove = __devexit_p(xwdt_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = WATCHDOG_NAME,
|
||||
.of_match_table = xwdt_of_match,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init xwdt_init(void)
|
||||
{
|
||||
return platform_driver_register(&xwdt_driver);
|
||||
}
|
||||
|
||||
static void __exit xwdt_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&xwdt_driver);
|
||||
}
|
||||
|
||||
module_init(xwdt_init);
|
||||
module_exit(xwdt_exit);
|
||||
|
||||
MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
|
||||
MODULE_DESCRIPTION("Xilinx Watchdog driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
|
@ -56,6 +56,7 @@
|
||||
#define IO_DEFAULT 0x2E /* Address used on Portwell Boards */
|
||||
|
||||
static int io = IO_DEFAULT;
|
||||
static int swc_base_addr = -1;
|
||||
|
||||
static int timeout = DEFAULT_TIMEOUT; /* timeout value */
|
||||
static unsigned long timer_enabled; /* is the timer enabled? */
|
||||
@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void)
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Step 3: Read SWC I/O Base Address */
|
||||
@ -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",
|
||||
addr_l, addr_h, swc_base_addr);
|
||||
#endif
|
||||
return swc_base_addr;
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
/* Set watchdog timeout to x minutes */
|
||||
|
||||
static inline void pc87413_programm_wdto(unsigned int swc_base_addr,
|
||||
char pc87413_time)
|
||||
static inline void pc87413_programm_wdto(char pc87413_time)
|
||||
{
|
||||
/* Step 5: Programm WDTO, Twd. */
|
||||
outb_p(pc87413_time, swc_base_addr + WDTO);
|
||||
@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr,
|
||||
|
||||
/* Enable WDEN */
|
||||
|
||||
static inline void pc87413_enable_wden(unsigned int swc_base_addr)
|
||||
static inline void pc87413_enable_wden(void)
|
||||
{
|
||||
/* Step 6: Enable WDEN */
|
||||
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)
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
|
||||
static void pc87413_enable(void)
|
||||
{
|
||||
unsigned int swc_base_addr;
|
||||
|
||||
spin_lock(&io_lock);
|
||||
|
||||
pc87413_select_wdt_out();
|
||||
pc87413_enable_swc();
|
||||
swc_base_addr = pc87413_get_swc_base();
|
||||
pc87413_swc_bank3(swc_base_addr);
|
||||
pc87413_programm_wdto(swc_base_addr, timeout);
|
||||
pc87413_enable_wden(swc_base_addr);
|
||||
pc87413_enable_sw_wd_tren(swc_base_addr);
|
||||
pc87413_enable_sw_wd_trg(swc_base_addr);
|
||||
pc87413_swc_bank3();
|
||||
pc87413_programm_wdto(timeout);
|
||||
pc87413_enable_wden();
|
||||
pc87413_enable_sw_wd_tren();
|
||||
pc87413_enable_sw_wd_trg();
|
||||
|
||||
spin_unlock(&io_lock);
|
||||
}
|
||||
@ -242,17 +235,12 @@ static void pc87413_enable(void)
|
||||
|
||||
static void pc87413_disable(void)
|
||||
{
|
||||
unsigned int swc_base_addr;
|
||||
|
||||
spin_lock(&io_lock);
|
||||
|
||||
pc87413_select_wdt_out();
|
||||
pc87413_enable_swc();
|
||||
swc_base_addr = pc87413_get_swc_base();
|
||||
pc87413_swc_bank3(swc_base_addr);
|
||||
pc87413_disable_sw_wd_tren(swc_base_addr);
|
||||
pc87413_disable_sw_wd_trg(swc_base_addr);
|
||||
pc87413_programm_wdto(swc_base_addr, 0);
|
||||
pc87413_swc_bank3();
|
||||
pc87413_disable_sw_wd_tren();
|
||||
pc87413_disable_sw_wd_trg();
|
||||
pc87413_programm_wdto(0);
|
||||
|
||||
spin_unlock(&io_lock);
|
||||
}
|
||||
@ -261,20 +249,15 @@ static void pc87413_disable(void)
|
||||
|
||||
static void pc87413_refresh(void)
|
||||
{
|
||||
unsigned int swc_base_addr;
|
||||
|
||||
spin_lock(&io_lock);
|
||||
|
||||
pc87413_select_wdt_out();
|
||||
pc87413_enable_swc();
|
||||
swc_base_addr = pc87413_get_swc_base();
|
||||
pc87413_swc_bank3(swc_base_addr);
|
||||
pc87413_disable_sw_wd_tren(swc_base_addr);
|
||||
pc87413_disable_sw_wd_trg(swc_base_addr);
|
||||
pc87413_programm_wdto(swc_base_addr, timeout);
|
||||
pc87413_enable_wden(swc_base_addr);
|
||||
pc87413_enable_sw_wd_tren(swc_base_addr);
|
||||
pc87413_enable_sw_wd_trg(swc_base_addr);
|
||||
pc87413_swc_bank3();
|
||||
pc87413_disable_sw_wd_tren();
|
||||
pc87413_disable_sw_wd_trg();
|
||||
pc87413_programm_wdto(timeout);
|
||||
pc87413_enable_wden();
|
||||
pc87413_enable_sw_wd_tren();
|
||||
pc87413_enable_sw_wd_trg();
|
||||
|
||||
spin_unlock(&io_lock);
|
||||
}
|
||||
@ -528,7 +511,8 @@ static int __init pc87413_init(void)
|
||||
printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",
|
||||
WDT_INDEX_IO_PORT);
|
||||
|
||||
/* request_region(io, 2, "pc87413"); */
|
||||
if (!request_muxed_region(io, 2, MODNAME))
|
||||
return -EBUSY;
|
||||
|
||||
ret = register_reboot_notifier(&pc87413_notifier);
|
||||
if (ret != 0) {
|
||||
@ -541,12 +525,32 @@ static int __init pc87413_init(void)
|
||||
printk(KERN_ERR PFX
|
||||
"cannot register miscdev on minor=%d (err=%d)\n",
|
||||
WATCHDOG_MINOR, ret);
|
||||
unregister_reboot_notifier(&pc87413_notifier);
|
||||
return ret;
|
||||
goto reboot_unreg;
|
||||
}
|
||||
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();
|
||||
|
||||
release_region(io, 2);
|
||||
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)
|
||||
|
||||
misc_deregister(&pc87413_miscdev);
|
||||
unregister_reboot_notifier(&pc87413_notifier);
|
||||
/* release_region(io, 2); */
|
||||
release_region(swc_base_addr, 0x20);
|
||||
|
||||
printk(KERN_INFO MODNAME " watchdog component driver removed.\n");
|
||||
}
|
||||
|
@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev)
|
||||
#define s3c2410wdt_resume NULL
|
||||
#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 = {
|
||||
.probe = s3c2410wdt_probe,
|
||||
@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "s3c2410-wdt",
|
||||
.of_match_table = s3c2410_wdt_match,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev)
|
||||
sch311x_wdt_stop();
|
||||
}
|
||||
|
||||
#define sch311x_wdt_suspend NULL
|
||||
#define sch311x_wdt_resume NULL
|
||||
|
||||
static struct platform_driver sch311x_wdt_driver = {
|
||||
.probe = sch311x_wdt_probe,
|
||||
.remove = __devexit_p(sch311x_wdt_remove),
|
||||
.shutdown = sch311x_wdt_shutdown,
|
||||
.suspend = sch311x_wdt_suspend,
|
||||
.resume = sch311x_wdt_resume,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = DRV_NAME,
|
||||
|
@ -134,6 +134,8 @@ static void wdt_enable(void)
|
||||
writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);
|
||||
writel(LOCK, wdt->base + WDTLOCK);
|
||||
|
||||
/* Flush posted writes. */
|
||||
readl(wdt->base + WDTLOCK);
|
||||
spin_unlock(&wdt->lock);
|
||||
}
|
||||
|
||||
@ -144,9 +146,10 @@ static void wdt_disable(void)
|
||||
|
||||
writel(UNLOCK, wdt->base + WDTLOCK);
|
||||
writel(0, wdt->base + WDTCONTROL);
|
||||
writel(0, wdt->base + WDTLOAD);
|
||||
writel(LOCK, wdt->base + WDTLOCK);
|
||||
|
||||
/* Flush posted writes. */
|
||||
readl(wdt->base + WDTLOCK);
|
||||
spin_unlock(&wdt->lock);
|
||||
}
|
||||
|
||||
|
111
drivers/watchdog/watchdog_core.c
Normal file
111
drivers/watchdog/watchdog_core.c
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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");
|
395
drivers/watchdog/watchdog_dev.c
Normal file
395
drivers/watchdog/watchdog_dev.c
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* watchdog_dev.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.
|
||||
*
|
||||
* This part of the generic code takes care of the following
|
||||
* misc device: /dev/watchdog.
|
||||
*
|
||||
* 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 module stuff/... */
|
||||
#include <linux/types.h> /* For standard types (like size_t) */
|
||||
#include <linux/errno.h> /* For the -ENODEV/... values */
|
||||
#include <linux/kernel.h> /* For printk/panic/... */
|
||||
#include <linux/fs.h> /* For file operations */
|
||||
#include <linux/watchdog.h> /* For watchdog specific items */
|
||||
#include <linux/miscdevice.h> /* For handling misc devices */
|
||||
#include <linux/init.h> /* For __init/__exit/... */
|
||||
#include <linux/uaccess.h> /* For copy_to_user/put_user/... */
|
||||
|
||||
/* make sure we only register one /dev/watchdog device */
|
||||
static unsigned long watchdog_dev_busy;
|
||||
/* the watchdog device behind /dev/watchdog */
|
||||
static struct watchdog_device *wdd;
|
||||
|
||||
/*
|
||||
* watchdog_ping: ping the watchdog.
|
||||
* @wddev: the watchdog device to ping
|
||||
*
|
||||
* If the watchdog has no own ping operation then it needs to be
|
||||
* restarted via the start operation. This wrapper function does
|
||||
* exactly that.
|
||||
* We only ping when the watchdog device is running.
|
||||
*/
|
||||
|
||||
static int watchdog_ping(struct watchdog_device *wddev)
|
||||
{
|
||||
if (test_bit(WDOG_ACTIVE, &wdd->status)) {
|
||||
if (wddev->ops->ping)
|
||||
return wddev->ops->ping(wddev); /* ping the watchdog */
|
||||
else
|
||||
return wddev->ops->start(wddev); /* restart watchdog */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_start: wrapper to start the watchdog.
|
||||
* @wddev: the watchdog device to start
|
||||
*
|
||||
* Start the watchdog if it is not active and mark it active.
|
||||
* This function returns zero on success or a negative errno code for
|
||||
* failure.
|
||||
*/
|
||||
|
||||
static int watchdog_start(struct watchdog_device *wddev)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (!test_bit(WDOG_ACTIVE, &wdd->status)) {
|
||||
err = wddev->ops->start(wddev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
set_bit(WDOG_ACTIVE, &wdd->status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_stop: wrapper to stop the watchdog.
|
||||
* @wddev: the watchdog device to stop
|
||||
*
|
||||
* Stop the watchdog if it is still active and unmark it active.
|
||||
* This function returns zero on success or a negative errno code for
|
||||
* failure.
|
||||
* If the 'nowayout' feature was set, the watchdog cannot be stopped.
|
||||
*/
|
||||
|
||||
static int watchdog_stop(struct watchdog_device *wddev)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
|
||||
if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) {
|
||||
pr_info("%s: nowayout prevents watchdog to be stopped!\n",
|
||||
wdd->info->identity);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (test_bit(WDOG_ACTIVE, &wdd->status)) {
|
||||
err = wddev->ops->stop(wddev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
clear_bit(WDOG_ACTIVE, &wdd->status);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_write: writes to the watchdog.
|
||||
* @file: file from VFS
|
||||
* @data: user address of data
|
||||
* @len: length of data
|
||||
* @ppos: pointer to the file offset
|
||||
*
|
||||
* A write to a watchdog device is defined as a keepalive ping.
|
||||
* Writing the magic 'V' sequence allows the next close to turn
|
||||
* off the watchdog (if 'nowayout' is not set).
|
||||
*/
|
||||
|
||||
static ssize_t watchdog_write(struct file *file, const char __user *data,
|
||||
size_t len, loff_t *ppos)
|
||||
{
|
||||
size_t i;
|
||||
char c;
|
||||
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Note: just in case someone wrote the magic character
|
||||
* five months ago...
|
||||
*/
|
||||
clear_bit(WDOG_ALLOW_RELEASE, &wdd->status);
|
||||
|
||||
/* scan to see whether or not we got the magic character */
|
||||
for (i = 0; i != len; i++) {
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
set_bit(WDOG_ALLOW_RELEASE, &wdd->status);
|
||||
}
|
||||
|
||||
/* someone wrote to us, so we send the watchdog a keepalive ping */
|
||||
watchdog_ping(wdd);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_ioctl: handle the different ioctl's for the watchdog device.
|
||||
* @file: file handle to the device
|
||||
* @cmd: watchdog command
|
||||
* @arg: argument pointer
|
||||
*
|
||||
* The watchdog API defines a common set of functions for all watchdogs
|
||||
* according to their available features.
|
||||
*/
|
||||
|
||||
static long watchdog_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int __user *p = argp;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
if (wdd->ops->ioctl) {
|
||||
err = wdd->ops->ioctl(wdd, cmd, arg);
|
||||
if (err != -ENOIOCTLCMD)
|
||||
return err;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
return copy_to_user(argp, wdd->info,
|
||||
sizeof(struct watchdog_info)) ? -EFAULT : 0;
|
||||
case WDIOC_GETSTATUS:
|
||||
val = wdd->ops->status ? wdd->ops->status(wdd) : 0;
|
||||
return put_user(val, p);
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
return put_user(wdd->bootstatus, p);
|
||||
case WDIOC_SETOPTIONS:
|
||||
if (get_user(val, p))
|
||||
return -EFAULT;
|
||||
if (val & WDIOS_DISABLECARD) {
|
||||
err = watchdog_stop(wdd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
if (val & WDIOS_ENABLECARD) {
|
||||
err = watchdog_start(wdd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
case WDIOC_KEEPALIVE:
|
||||
if (!(wdd->info->options & WDIOF_KEEPALIVEPING))
|
||||
return -EOPNOTSUPP;
|
||||
watchdog_ping(wdd);
|
||||
return 0;
|
||||
case WDIOC_SETTIMEOUT:
|
||||
if ((wdd->ops->set_timeout == NULL) ||
|
||||
!(wdd->info->options & WDIOF_SETTIMEOUT))
|
||||
return -EOPNOTSUPP;
|
||||
if (get_user(val, p))
|
||||
return -EFAULT;
|
||||
if ((wdd->max_timeout != 0) &&
|
||||
(val < wdd->min_timeout || val > wdd->max_timeout))
|
||||
return -EINVAL;
|
||||
err = wdd->ops->set_timeout(wdd, val);
|
||||
if (err < 0)
|
||||
return err;
|
||||
wdd->timeout = val;
|
||||
/* If the watchdog is active then we send a keepalive ping
|
||||
* to make sure that the watchdog keep's running (and if
|
||||
* possible that it takes the new timeout) */
|
||||
watchdog_ping(wdd);
|
||||
/* Fall */
|
||||
case WDIOC_GETTIMEOUT:
|
||||
/* timeout == 0 means that we don't know the timeout */
|
||||
if (wdd->timeout == 0)
|
||||
return -EOPNOTSUPP;
|
||||
return put_user(wdd->timeout, p);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_open: open the /dev/watchdog device.
|
||||
* @inode: inode of device
|
||||
* @file: file handle to device
|
||||
*
|
||||
* When the /dev/watchdog device gets opened, we start the watchdog.
|
||||
* Watch out: the /dev/watchdog device is single open, so we make sure
|
||||
* it can only be opened once.
|
||||
*/
|
||||
|
||||
static int watchdog_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
|
||||
/* the watchdog is single open! */
|
||||
if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status))
|
||||
return -EBUSY;
|
||||
|
||||
/*
|
||||
* If the /dev/watchdog device is open, we don't want the module
|
||||
* to be unloaded.
|
||||
*/
|
||||
if (!try_module_get(wdd->ops->owner))
|
||||
goto out;
|
||||
|
||||
err = watchdog_start(wdd);
|
||||
if (err < 0)
|
||||
goto out_mod;
|
||||
|
||||
/* dev/watchdog is a virtual (and thus non-seekable) filesystem */
|
||||
return nonseekable_open(inode, file);
|
||||
|
||||
out_mod:
|
||||
module_put(wdd->ops->owner);
|
||||
out:
|
||||
clear_bit(WDOG_DEV_OPEN, &wdd->status);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_release: release the /dev/watchdog device.
|
||||
* @inode: inode of device
|
||||
* @file: file handle to device
|
||||
*
|
||||
* This is the code for when /dev/watchdog gets closed. We will only
|
||||
* stop the watchdog when we have received the magic char (and nowayout
|
||||
* was not set), else the watchdog will keep running.
|
||||
*/
|
||||
|
||||
static int watchdog_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
int err = -EBUSY;
|
||||
|
||||
/*
|
||||
* We only stop the watchdog if we received the magic character
|
||||
* or if WDIOF_MAGICCLOSE is not set. If nowayout was set then
|
||||
* watchdog_stop will fail.
|
||||
*/
|
||||
if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) ||
|
||||
!(wdd->info->options & WDIOF_MAGICCLOSE))
|
||||
err = watchdog_stop(wdd);
|
||||
|
||||
/* If the watchdog was not stopped, send a keepalive ping */
|
||||
if (err < 0) {
|
||||
pr_crit("%s: watchdog did not stop!\n", wdd->info->identity);
|
||||
watchdog_ping(wdd);
|
||||
}
|
||||
|
||||
/* Allow the owner module to be unloaded again */
|
||||
module_put(wdd->ops->owner);
|
||||
|
||||
/* make sure that /dev/watchdog can be re-opened */
|
||||
clear_bit(WDOG_DEV_OPEN, &wdd->status);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct file_operations watchdog_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.write = watchdog_write,
|
||||
.unlocked_ioctl = watchdog_ioctl,
|
||||
.open = watchdog_open,
|
||||
.release = watchdog_release,
|
||||
};
|
||||
|
||||
static struct miscdevice watchdog_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &watchdog_fops,
|
||||
};
|
||||
|
||||
/*
|
||||
* watchdog_dev_register:
|
||||
* @watchdog: watchdog device
|
||||
*
|
||||
* Register a watchdog device as /dev/watchdog. /dev/watchdog
|
||||
* is actually a miscdevice and thus we set it up like that.
|
||||
*/
|
||||
|
||||
int watchdog_dev_register(struct watchdog_device *watchdog)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Only one device can register for /dev/watchdog */
|
||||
if (test_and_set_bit(0, &watchdog_dev_busy)) {
|
||||
pr_err("only one watchdog can use /dev/watchdog.\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
wdd = watchdog;
|
||||
|
||||
err = misc_register(&watchdog_miscdev);
|
||||
if (err != 0) {
|
||||
pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n",
|
||||
watchdog->info->identity, WATCHDOG_MINOR, err);
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
wdd = NULL;
|
||||
clear_bit(0, &watchdog_dev_busy);
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* watchdog_dev_unregister:
|
||||
* @watchdog: watchdog device
|
||||
*
|
||||
* Deregister the /dev/watchdog device.
|
||||
*/
|
||||
|
||||
int watchdog_dev_unregister(struct watchdog_device *watchdog)
|
||||
{
|
||||
/* Check that a watchdog device was registered in the past */
|
||||
if (!test_bit(0, &watchdog_dev_busy) || !wdd)
|
||||
return -ENODEV;
|
||||
|
||||
/* We can only unregister the watchdog device that was registered */
|
||||
if (watchdog != wdd) {
|
||||
pr_err("%s: watchdog was not registered as /dev/watchdog.\n",
|
||||
watchdog->info->identity);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
misc_deregister(&watchdog_miscdev);
|
||||
wdd = NULL;
|
||||
clear_bit(0, &watchdog_dev_busy);
|
||||
return 0;
|
||||
}
|
33
drivers/watchdog/watchdog_dev.h
Normal file
33
drivers/watchdog/watchdog_dev.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 {
|
||||
#define WATCHDOG_NOWAYOUT 0
|
||||
#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 /* ifndef _LINUX_WATCHDOG_H */
|
||||
|
Loading…
Reference in New Issue
Block a user