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

* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog:
  watchdog: booke_wdt: clean up status messages
  watchdog: cleanup spaces before tabs
  watchdog: convert to DEFINE_PCI_DEVICE_TABLE
  watchdog: Xen watchdog driver
  watchdog: Intel SCU Watchdog Timer Driver for Moorestown and Medfield platforms.
  watchdog: jz4740_wdt - fix magic character checking
  watchdog: add JZ4740 watchdog driver
  watchdog: it87_wdt: Add support for IT8721F watchdog
  watchdog: hpwdt: build hpwdt as module by default with NMI_DECODING enabled
  watchdog: hpwdt: Fix a couple of typos
This commit is contained in:
Linus Torvalds 2011-03-17 17:09:29 -07:00
commit 4b0e976c66
42 changed files with 1498 additions and 92 deletions

View File

@ -30,6 +30,7 @@ extern struct platform_device jz4740_i2s_device;
extern struct platform_device jz4740_pcm_device; extern struct platform_device jz4740_pcm_device;
extern struct platform_device jz4740_codec_device; extern struct platform_device jz4740_codec_device;
extern struct platform_device jz4740_adc_device; extern struct platform_device jz4740_adc_device;
extern struct platform_device jz4740_wdt_device;
void jz4740_serial_device_register(void); void jz4740_serial_device_register(void);

View File

@ -289,3 +289,19 @@ void jz4740_serial_device_register(void)
platform_device_register(&jz4740_uart_device); platform_device_register(&jz4740_uart_device);
} }
/* Watchdog */
static struct resource jz4740_wdt_resources[] = {
{
.start = JZ4740_WDT_BASE_ADDR,
.end = JZ4740_WDT_BASE_ADDR + 0x10 - 1,
.flags = IORESOURCE_MEM,
},
};
struct platform_device jz4740_wdt_device = {
.name = "jz4740-wdt",
.id = -1,
.num_resources = ARRAY_SIZE(jz4740_wdt_resources),
.resource = jz4740_wdt_resources,
};

View File

@ -533,6 +533,16 @@ config I6300ESB_WDT
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called i6300esb. module will be called i6300esb.
config INTEL_SCU_WATCHDOG
bool "Intel SCU Watchdog for Mobile Platforms"
depends on WATCHDOG
depends on INTEL_SCU_IPC
---help---
Hardware driver for the watchdog time built into the Intel SCU
for Intel Mobile Platforms.
To compile this driver as a module, choose M here.
config ITCO_WDT config ITCO_WDT
tristate "Intel TCO Timer/Watchdog" tristate "Intel TCO Timer/Watchdog"
depends on (X86 || IA64) && PCI depends on (X86 || IA64) && PCI
@ -580,7 +590,7 @@ config IT87_WDT
depends on X86 && EXPERIMENTAL depends on X86 && EXPERIMENTAL
---help--- ---help---
This is the driver for the hardware watchdog on the ITE IT8702, This is the driver for the hardware watchdog on the ITE IT8702,
IT8712, IT8716, IT8718, IT8720, IT8726, IT8712 Super I/O chips. IT8712, IT8716, IT8718, IT8720, IT8721, IT8726 Super I/O chips.
This watchdog simply watches your kernel to make sure it doesn't This watchdog simply watches your kernel to make sure it doesn't
freeze, and if it does, it reboots your computer after a certain freeze, and if it does, it reboots your computer after a certain
amount of time. amount of time.
@ -589,18 +599,20 @@ config IT87_WDT
be called it87_wdt. be called it87_wdt.
config HP_WATCHDOG config HP_WATCHDOG
tristate "HP Proliant iLO2+ Hardware Watchdog Timer" tristate "HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on X86 depends on X86
default m
help help
A software monitoring watchdog and NMI sourcing driver. This driver A software monitoring watchdog and NMI sourcing driver. This driver
will detect lockups and provide a stack trace. This is a driver that will detect lockups and provide a stack trace. This is a driver that
will only load on a HP ProLiant system with a minimum of iLO2 support. will only load on an HP ProLiant system with a minimum of iLO2 support.
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called hpwdt. called hpwdt.
config HPWDT_NMI_DECODING config HPWDT_NMI_DECODING
bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer" bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
depends on HP_WATCHDOG depends on HP_WATCHDOG
default y
help help
When an NMI occurs this feature will make the necessary BIOS calls to When an NMI occurs this feature will make the necessary BIOS calls to
log the cause of the NMI. log the cause of the NMI.
@ -903,6 +915,12 @@ config INDYDOG
timer expired and no process has written to /dev/watchdog during timer expired and no process has written to /dev/watchdog during
that time. that time.
config JZ4740_WDT
tristate "Ingenic jz4740 SoC hardware watchdog"
depends on MACH_JZ4740
help
Hardware driver for the built-in watchdog timer on Ingenic jz4740 SoCs.
config WDT_MTX1 config WDT_MTX1
tristate "MTX-1 Hardware Watchdog" tristate "MTX-1 Hardware Watchdog"
depends on MIPS_MTX1 depends on MIPS_MTX1
@ -1111,6 +1129,16 @@ config WATCHDOG_RIO
# XTENSA Architecture # XTENSA Architecture
# Xen Architecture
config XEN_WDT
tristate "Xen Watchdog support"
depends on XEN
help
Say Y here to support the hypervisor watchdog capability provided
by Xen 4.0 and newer. The watchdog timeout period is normally one
minute but can be changed with a boot-time parameter.
# #
# ISA-based Watchdog Cards # ISA-based Watchdog Cards
# #

View File

@ -102,6 +102,7 @@ obj-$(CONFIG_W83877F_WDT) += w83877f_wdt.o
obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o
obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o
obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
# M32R Architecture # M32R Architecture
@ -114,6 +115,7 @@ obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o
obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o obj-$(CONFIG_BCM63XX_WDT) += bcm63xx_wdt.o
obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o
obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_INDYDOG) += indydog.o
obj-$(CONFIG_JZ4740_WDT) += jz4740_wdt.o
obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o
obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o
@ -148,6 +150,9 @@ obj-$(CONFIG_WATCHDOG_CP1XXX) += cpwd.o
# XTENSA Architecture # XTENSA Architecture
# Xen
obj-$(CONFIG_XEN_WDT) += xen_wdt.o
# Architecture Independant # Architecture Independant
obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o

View File

@ -301,7 +301,7 @@ static int ali_notify_sys(struct notifier_block *this,
* want to register another driver on the same PCI id. * want to register another driver on the same PCI id.
*/ */
static struct pci_device_id ali_pci_tbl[] __used = { static DEFINE_PCI_DEVICE_TABLE(ali_pci_tbl) __used = {
{ PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,}, { PCI_VENDOR_ID_AL, 0x1533, PCI_ANY_ID, PCI_ANY_ID,},
{ PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,}, { PCI_VENDOR_ID_AL, 0x1535, PCI_ANY_ID, PCI_ANY_ID,},
{ 0, }, { 0, },

View File

@ -430,7 +430,7 @@ err_out:
module_init(alim7101_wdt_init); module_init(alim7101_wdt_init);
module_exit(alim7101_wdt_unload); module_exit(alim7101_wdt_unload);
static struct pci_device_id alim7101_pci_tbl[] __devinitdata __used = { static DEFINE_PCI_DEVICE_TABLE(alim7101_pci_tbl) __used = {
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) }, { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533) },
{ PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) }, { PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101) },
{ } { }

View File

@ -4,7 +4,7 @@
* Author: Matthew McClintock * Author: Matthew McClintock
* Maintainer: Kumar Gala <galak@kernel.crashing.org> * Maintainer: Kumar Gala <galak@kernel.crashing.org>
* *
* Copyright 2005, 2008, 2010 Freescale Semiconductor Inc. * Copyright 2005, 2008, 2010-2011 Freescale Semiconductor Inc.
* *
* This program is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
@ -221,9 +221,8 @@ static int booke_wdt_open(struct inode *inode, struct file *file)
if (booke_wdt_enabled == 0) { if (booke_wdt_enabled == 0) {
booke_wdt_enabled = 1; booke_wdt_enabled = 1;
on_each_cpu(__booke_wdt_enable, NULL, 0); on_each_cpu(__booke_wdt_enable, NULL, 0);
printk(KERN_INFO pr_debug("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
"PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", period_to_sec(booke_wdt_period));
booke_wdt_period);
} }
spin_unlock(&booke_wdt_lock); spin_unlock(&booke_wdt_lock);
@ -240,6 +239,7 @@ static int booke_wdt_release(struct inode *inode, struct file *file)
*/ */
on_each_cpu(__booke_wdt_disable, NULL, 0); on_each_cpu(__booke_wdt_disable, NULL, 0);
booke_wdt_enabled = 0; booke_wdt_enabled = 0;
pr_debug("booke_wdt: watchdog disabled\n");
#endif #endif
clear_bit(0, &wdt_is_active); clear_bit(0, &wdt_is_active);
@ -271,21 +271,20 @@ static int __init booke_wdt_init(void)
{ {
int ret = 0; int ret = 0;
printk(KERN_INFO "PowerPC Book-E Watchdog Timer Loaded\n"); pr_info("booke_wdt: powerpc book-e watchdog driver loaded\n");
ident.firmware_version = cur_cpu_spec->pvr_value; ident.firmware_version = cur_cpu_spec->pvr_value;
ret = misc_register(&booke_wdt_miscdev); ret = misc_register(&booke_wdt_miscdev);
if (ret) { if (ret) {
printk(KERN_CRIT "Cannot register miscdev on minor=%d: %d\n", pr_err("booke_wdt: cannot register device (minor=%u, ret=%i)\n",
WATCHDOG_MINOR, ret); WATCHDOG_MINOR, ret);
return ret; return ret;
} }
spin_lock(&booke_wdt_lock); spin_lock(&booke_wdt_lock);
if (booke_wdt_enabled == 1) { if (booke_wdt_enabled == 1) {
printk(KERN_INFO pr_info("booke_wdt: watchdog enabled (timeout = %llu sec)\n",
"PowerPC Book-E Watchdog Timer Enabled (wdt_period=%d)\n", period_to_sec(booke_wdt_period));
booke_wdt_period);
on_each_cpu(__booke_wdt_enable, NULL, 0); on_each_cpu(__booke_wdt_enable, NULL, 0);
} }
spin_unlock(&booke_wdt_lock); spin_unlock(&booke_wdt_lock);

View File

@ -52,7 +52,7 @@ static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_timer_reg; static unsigned long __iomem *hpwdt_timer_reg;
static unsigned long __iomem *hpwdt_timer_con; static unsigned long __iomem *hpwdt_timer_con;
static struct pci_device_id hpwdt_devices[] = { static DEFINE_PCI_DEVICE_TABLE(hpwdt_devices) = {
{ PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */ { PCI_DEVICE(PCI_VENDOR_ID_COMPAQ, 0xB203) }, /* iLO2 */
{ PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */ { PCI_DEVICE(PCI_VENDOR_ID_HP, 0x3306) }, /* iLO3 */
{0}, /* terminate list */ {0}, /* terminate list */

View File

@ -334,7 +334,7 @@ static struct miscdevice esb_miscdev = {
/* /*
* Data for PCI driver interface * Data for PCI driver interface
*/ */
static struct pci_device_id esb_pci_tbl[] = { static DEFINE_PCI_DEVICE_TABLE(esb_pci_tbl) = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_9), },
{ 0, }, /* End of list */ { 0, }, /* End of list */
}; };

View File

@ -262,7 +262,7 @@ static struct {
* pci_driver, because the I/O Controller Hub has also other * pci_driver, because the I/O Controller Hub has also other
* functions that probably will be registered by other drivers. * functions that probably will be registered by other drivers.
*/ */
static struct pci_device_id iTCO_wdt_pci_tbl[] = { 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_82801AA_0, TCO_ICH)},
{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0, TCO_ICH0)}, { 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_0, TCO_ICH2)},

View File

@ -0,0 +1,572 @@
/*
* Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
* for Intel part #(s):
* - AF82MP20 PCH
*
* Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General
* Public License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* The full GNU General Public License is included in this
* distribution in the file called COPYING.
*
*/
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/fs.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/sfi.h>
#include <linux/types.h>
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/intel_scu_ipc.h>
#include <asm/apb_timer.h>
#include <asm/mrst.h>
#include "intel_scu_watchdog.h"
/* Bounds number of times we will retry loading time count */
/* This retry is a work around for a silicon bug. */
#define MAX_RETRY 16
#define IPC_SET_WATCHDOG_TIMER 0xF8
static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
module_param(timer_margin, int, 0);
MODULE_PARM_DESC(timer_margin,
"Watchdog timer margin"
"Time between interrupt and resetting the system"
"The range is from 1 to 160"
"This is the time for all keep alives to arrive");
static int timer_set = DEFAULT_TIME;
module_param(timer_set, int, 0);
MODULE_PARM_DESC(timer_set,
"Default Watchdog timer setting"
"Complete cycle time"
"The range is from 1 to 170"
"This is the time for all keep alives to arrive");
/* After watchdog device is closed, check force_boot. If:
* force_boot == 0, then force boot on next watchdog interrupt after close,
* force_boot == 1, then force boot immediately when device is closed.
*/
static int force_boot;
module_param(force_boot, int, 0);
MODULE_PARM_DESC(force_boot,
"A value of 1 means that the driver will reboot"
"the system immediately if the /dev/watchdog device is closed"
"A value of 0 means that when /dev/watchdog device is closed"
"the watchdog timer will be refreshed for one more interval"
"of length: timer_set. At the end of this interval, the"
"watchdog timer will reset the system."
);
/* there is only one device in the system now; this can be made into
* an array in the future if we have more than one device */
static struct intel_scu_watchdog_dev watchdog_device;
/* Forces restart, if force_reboot is set */
static void watchdog_fire(void)
{
if (force_boot) {
printk(KERN_CRIT PFX "Initiating system reboot.\n");
emergency_restart();
printk(KERN_CRIT PFX "Reboot didn't ?????\n");
}
else {
printk(KERN_CRIT PFX "Immediate Reboot Disabled\n");
printk(KERN_CRIT PFX
"System will reset when watchdog timer times out!\n");
}
}
static int check_timer_margin(int new_margin)
{
if ((new_margin < MIN_TIME_CYCLE) ||
(new_margin > MAX_TIME - timer_set)) {
pr_debug("Watchdog timer: value of new_margin %d is out of the range %d to %d\n",
new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
return -EINVAL;
}
return 0;
}
/*
* IPC operations
*/
static int watchdog_set_ipc(int soft_threshold, int threshold)
{
u32 *ipc_wbuf;
u8 cbuf[16] = { '\0' };
int ipc_ret = 0;
ipc_wbuf = (u32 *)&cbuf;
ipc_wbuf[0] = soft_threshold;
ipc_wbuf[1] = threshold;
ipc_ret = intel_scu_ipc_command(
IPC_SET_WATCHDOG_TIMER,
0,
ipc_wbuf,
2,
NULL,
0);
if (ipc_ret != 0)
pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
return ipc_ret;
};
/*
* Intel_SCU operations
*/
/* timer interrupt handler */
static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
{
int int_status;
int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
pr_debug("Watchdog timer: irq, int_status: %x\n", int_status);
if (int_status != 0)
return IRQ_NONE;
/* has the timer been started? If not, then this is spurious */
if (watchdog_device.timer_started == 0) {
pr_debug("Watchdog timer: spurious interrupt received\n");
return IRQ_HANDLED;
}
/* temporarily disable the timer */
iowrite32(0x00000002, watchdog_device.timer_control_addr);
/* set the timer to the threshold */
iowrite32(watchdog_device.threshold,
watchdog_device.timer_load_count_addr);
/* allow the timer to run */
iowrite32(0x00000003, watchdog_device.timer_control_addr);
return IRQ_HANDLED;
}
static int intel_scu_keepalive(void)
{
/* read eoi register - clears interrupt */
ioread32(watchdog_device.timer_clear_interrupt_addr);
/* temporarily disable the timer */
iowrite32(0x00000002, watchdog_device.timer_control_addr);
/* set the timer to the soft_threshold */
iowrite32(watchdog_device.soft_threshold,
watchdog_device.timer_load_count_addr);
/* allow the timer to run */
iowrite32(0x00000003, watchdog_device.timer_control_addr);
return 0;
}
static int intel_scu_stop(void)
{
iowrite32(0, watchdog_device.timer_control_addr);
return 0;
}
static int intel_scu_set_heartbeat(u32 t)
{
int ipc_ret;
int retry_count;
u32 soft_value;
u32 hw_pre_value;
u32 hw_value;
watchdog_device.timer_set = t;
watchdog_device.threshold =
timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
watchdog_device.soft_threshold =
(watchdog_device.timer_set - timer_margin)
* watchdog_device.timer_tbl_ptr->freq_hz;
pr_debug("Watchdog timer: set_heartbeat: timer freq is %d\n",
watchdog_device.timer_tbl_ptr->freq_hz);
pr_debug("Watchdog timer: set_heartbeat: timer_set is %x (hex)\n",
watchdog_device.timer_set);
pr_debug("Watchdog timer: set_hearbeat: timer_margin is %x (hex)\n",
timer_margin);
pr_debug("Watchdog timer: set_heartbeat: threshold is %x (hex)\n",
watchdog_device.threshold);
pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
watchdog_device.soft_threshold);
/* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
/* watchdog timing come out right. */
watchdog_device.threshold =
watchdog_device.threshold / FREQ_ADJUSTMENT;
watchdog_device.soft_threshold =
watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
/* temporarily disable the timer */
iowrite32(0x00000002, watchdog_device.timer_control_addr);
/* send the threshold and soft_threshold via IPC to the processor */
ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
watchdog_device.threshold);
if (ipc_ret != 0) {
/* Make sure the watchdog timer is stopped */
intel_scu_stop();
return ipc_ret;
}
/* Soft Threshold set loop. Early versions of silicon did */
/* not always set this count correctly. This loop checks */
/* the value and retries if it was not set correctly. */
retry_count = 0;
soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
do {
/* Make sure timer is stopped */
intel_scu_stop();
if (MAX_RETRY < retry_count++) {
/* Unable to set timer value */
pr_err("Watchdog timer: Unable to set timer\n");
return -ENODEV;
}
/* set the timer to the soft threshold */
iowrite32(watchdog_device.soft_threshold,
watchdog_device.timer_load_count_addr);
/* read count value before starting timer */
hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
hw_pre_value = hw_pre_value & 0xFFFF0000;
/* Start the timer */
iowrite32(0x00000003, watchdog_device.timer_control_addr);
/* read the value the time loaded into its count reg */
hw_value = ioread32(watchdog_device.timer_load_count_addr);
hw_value = hw_value & 0xFFFF0000;
} while (soft_value != hw_value);
watchdog_device.timer_started = 1;
return 0;
}
/*
* /dev/watchdog handling
*/
static int intel_scu_open(struct inode *inode, struct file *file)
{
/* Set flag to indicate that watchdog device is open */
if (test_and_set_bit(0, &watchdog_device.driver_open))
return -EBUSY;
/* Check for reopen of driver. Reopens are not allowed */
if (watchdog_device.driver_closed)
return -EPERM;
return nonseekable_open(inode, file);
}
static int intel_scu_release(struct inode *inode, struct file *file)
{
/*
* This watchdog should not be closed, after the timer
* is started with the WDIPC_SETTIMEOUT ioctl
* If force_boot is set watchdog_fire() will cause an
* immediate reset. If force_boot is not set, the watchdog
* timer is refreshed for one more interval. At the end
* of that interval, the watchdog timer will reset the system.
*/
if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
pr_debug("Watchdog timer: intel_scu_release, without open\n");
return -ENOTTY;
}
if (!watchdog_device.timer_started) {
/* Just close, since timer has not been started */
pr_debug("Watchdog timer: closed, without starting timer\n");
return 0;
}
printk(KERN_CRIT PFX
"Unexpected close of /dev/watchdog!\n");
/* Since the timer was started, prevent future reopens */
watchdog_device.driver_closed = 1;
/* Refresh the timer for one more interval */
intel_scu_keepalive();
/* Reboot system (if force_boot is set) */
watchdog_fire();
/* We should only reach this point if force_boot is not set */
return 0;
}
static ssize_t intel_scu_write(struct file *file,
char const *data,
size_t len,
loff_t *ppos)
{
if (watchdog_device.timer_started)
/* Watchdog already started, keep it alive */
intel_scu_keepalive();
else
/* Start watchdog with timer value set by init */
intel_scu_set_heartbeat(watchdog_device.timer_set);
return len;
}
static long intel_scu_ioctl(struct file *file,
unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;
u32 __user *p = argp;
u32 new_margin;
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING,
.firmware_version = 0, /* @todo Get from SCU via
ipc_get_scu_fw_version()? */
.identity = "Intel_SCU IOH Watchdog" /* len < 32 */
};
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp,
&ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
intel_scu_keepalive();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_margin, p))
return -EFAULT;
if (check_timer_margin(new_margin))
return -EINVAL;
if (intel_scu_set_heartbeat(new_margin))
return -EINVAL;
return 0;
case WDIOC_GETTIMEOUT:
return put_user(watchdog_device.soft_threshold, p);
default:
return -ENOTTY;
}
}
/*
* Notifier for system down
*/
static int intel_scu_notify_sys(struct notifier_block *this,
unsigned long code,
void *another_unused)
{
if (code == SYS_DOWN || code == SYS_HALT)
/* Turn off the watchdog timer. */
intel_scu_stop();
return NOTIFY_DONE;
}
/*
* Kernel Interfaces
*/
static const struct file_operations intel_scu_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = intel_scu_write,
.unlocked_ioctl = intel_scu_ioctl,
.open = intel_scu_open,
.release = intel_scu_release,
};
static int __init intel_scu_watchdog_init(void)
{
int ret;
u32 __iomem *tmp_addr;
/*
* We don't really need to check this as the SFI timer get will fail
* but if we do so we can exit with a clearer reason and no noise.
*
* If it isn't an intel MID device then it doesn't have this watchdog
*/
if (!mrst_identify_cpu())
return -ENODEV;
/* Check boot parameters to verify that their initial values */
/* are in range. */
/* Check value of timer_set boot parameter */
if ((timer_set < MIN_TIME_CYCLE) ||
(timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
pr_err("Watchdog timer: value of timer_set %x (hex) "
"is out of range from %x to %x (hex)\n",
timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
return -EINVAL;
}
/* Check value of timer_margin boot parameter */
if (check_timer_margin(timer_margin))
return -EINVAL;
watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
if (watchdog_device.timer_tbl_ptr == NULL) {
pr_debug("Watchdog timer - Intel SCU watchdog: timer is not available\n");
return -ENODEV;
}
/* make sure the timer exists */
if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does not have valid physical memory\n",
sfi_mtimer_num);
return -ENODEV;
}
if (watchdog_device.timer_tbl_ptr->irq == 0) {
pr_debug("Watchdog timer: timer %d invalid irq\n",
sfi_mtimer_num);
return -ENODEV;
}
tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
20);
if (tmp_addr == NULL) {
pr_debug("Watchdog timer: timer unable to ioremap\n");
return -ENOMEM;
}
watchdog_device.timer_load_count_addr = tmp_addr++;
watchdog_device.timer_current_value_addr = tmp_addr++;
watchdog_device.timer_control_addr = tmp_addr++;
watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
watchdog_device.timer_interrupt_status_addr = tmp_addr++;
/* Set the default time values in device structure */
watchdog_device.timer_set = timer_set;
watchdog_device.threshold =
timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
watchdog_device.soft_threshold =
(watchdog_device.timer_set - timer_margin)
* watchdog_device.timer_tbl_ptr->freq_hz;
watchdog_device.intel_scu_notifier.notifier_call =
intel_scu_notify_sys;
ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
if (ret) {
pr_err("Watchdog timer: cannot register notifier %d)\n", ret);
goto register_reboot_error;
}
watchdog_device.miscdev.minor = WATCHDOG_MINOR;
watchdog_device.miscdev.name = "watchdog";
watchdog_device.miscdev.fops = &intel_scu_fops;
ret = misc_register(&watchdog_device.miscdev);
if (ret) {
pr_err("Watchdog timer: cannot register miscdev %d err =%d\n",
WATCHDOG_MINOR, ret);
goto misc_register_error;
}
ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
watchdog_timer_interrupt,
IRQF_SHARED, "watchdog",
&watchdog_device.timer_load_count_addr);
if (ret) {
pr_err("Watchdog timer: error requesting irq %d\n", ret);
goto request_irq_error;
}
/* Make sure timer is disabled before returning */
intel_scu_stop();
return 0;
/* error cleanup */
request_irq_error:
misc_deregister(&watchdog_device.miscdev);
misc_register_error:
unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
register_reboot_error:
intel_scu_stop();
iounmap(watchdog_device.timer_load_count_addr);
return ret;
}
static void __exit intel_scu_watchdog_exit(void)
{
misc_deregister(&watchdog_device.miscdev);
unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
/* disable the timer */
iowrite32(0x00000002, watchdog_device.timer_control_addr);
iounmap(watchdog_device.timer_load_count_addr);
}
late_initcall(intel_scu_watchdog_init);
module_exit(intel_scu_watchdog_exit);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_VERSION(WDT_VER);

View File

@ -0,0 +1,66 @@
/*
* Intel_SCU 0.2: An Intel SCU IOH Based Watchdog Device
* for Intel part #(s):
* - AF82MP20 PCH
*
* Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General
* Public License as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
* The full GNU General Public License is included in this
* distribution in the file called COPYING.
*
*/
#ifndef __INTEL_SCU_WATCHDOG_H
#define __INTEL_SCU_WATCHDOG_H
#define PFX "Intel_SCU: "
#define WDT_VER "0.3"
/* minimum time between interrupts */
#define MIN_TIME_CYCLE 1
/* Time from warning to reboot is 2 seconds */
#define DEFAULT_SOFT_TO_HARD_MARGIN 2
#define MAX_TIME 170
#define DEFAULT_TIME 5
#define MAX_SOFT_TO_HARD_MARGIN (MAX_TIME-MIN_TIME_CYCLE)
/* Ajustment to clock tick frequency to make timing come out right */
#define FREQ_ADJUSTMENT 8
struct intel_scu_watchdog_dev {
ulong driver_open;
ulong driver_closed;
u32 timer_started;
u32 timer_set;
u32 threshold;
u32 soft_threshold;
u32 __iomem *timer_load_count_addr;
u32 __iomem *timer_current_value_addr;
u32 __iomem *timer_control_addr;
u32 __iomem *timer_clear_interrupt_addr;
u32 __iomem *timer_interrupt_status_addr;
struct sfi_timer_table_entry *timer_tbl_ptr;
struct notifier_block intel_scu_notifier;
struct miscdevice miscdev;
};
extern int sfi_mtimer_num;
/* extern struct sfi_timer_table_entry *sfi_get_mtmr(int hint); */
#endif /* __INTEL_SCU_WATCHDOG_H */

View File

@ -12,7 +12,7 @@
* http://www.ite.com.tw/ * http://www.ite.com.tw/
* *
* Support of the watchdog timers, which are available on * Support of the watchdog timers, which are available on
* IT8702, IT8712, IT8716, IT8718, IT8720 and IT8726. * IT8702, IT8712, IT8716, IT8718, IT8720, IT8721 and IT8726.
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
@ -45,7 +45,7 @@
#include <asm/system.h> #include <asm/system.h>
#define WATCHDOG_VERSION "1.13" #define WATCHDOG_VERSION "1.14"
#define WATCHDOG_NAME "IT87 WDT" #define WATCHDOG_NAME "IT87 WDT"
#define PFX WATCHDOG_NAME ": " #define PFX WATCHDOG_NAME ": "
#define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n"
@ -82,6 +82,7 @@
#define IT8716_ID 0x8716 #define IT8716_ID 0x8716
#define IT8718_ID 0x8718 #define IT8718_ID 0x8718
#define IT8720_ID 0x8720 #define IT8720_ID 0x8720
#define IT8721_ID 0x8721
#define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */ #define IT8726_ID 0x8726 /* the data sheet suggest wrongly 0x8716 */
/* GPIO Configuration Registers LDN=0x07 */ /* GPIO Configuration Registers LDN=0x07 */
@ -94,7 +95,7 @@
#define WDT_CIRINT 0x80 #define WDT_CIRINT 0x80
#define WDT_MOUSEINT 0x40 #define WDT_MOUSEINT 0x40
#define WDT_KYBINT 0x20 #define WDT_KYBINT 0x20
#define WDT_GAMEPORT 0x10 /* not in it8718, it8720 */ #define WDT_GAMEPORT 0x10 /* not in it8718, it8720, it8721 */
#define WDT_FORCE 0x02 #define WDT_FORCE 0x02
#define WDT_ZERO 0x01 #define WDT_ZERO 0x01
@ -102,7 +103,7 @@
#define WDT_TOV1 0x80 #define WDT_TOV1 0x80
#define WDT_KRST 0x40 #define WDT_KRST 0x40
#define WDT_TOVE 0x20 #define WDT_TOVE 0x20
#define WDT_PWROK 0x10 #define WDT_PWROK 0x10 /* not in it8721 */
#define WDT_INT_MASK 0x0f #define WDT_INT_MASK 0x0f
/* CIR Configuration Register LDN=0x0a */ /* CIR Configuration Register LDN=0x0a */
@ -134,7 +135,7 @@
#define WDTS_USE_GP 4 #define WDTS_USE_GP 4
#define WDTS_EXPECTED 5 #define WDTS_EXPECTED 5
static unsigned int base, gpact, ciract, max_units; static unsigned int base, gpact, ciract, max_units, chip_type;
static unsigned long wdt_status; static unsigned long wdt_status;
static DEFINE_SPINLOCK(spinlock); static DEFINE_SPINLOCK(spinlock);
@ -215,7 +216,7 @@ static inline void superio_outw(int val, int reg)
/* Internal function, should be called after superio_select(GPIO) */ /* Internal function, should be called after superio_select(GPIO) */
static void wdt_update_timeout(void) static void wdt_update_timeout(void)
{ {
unsigned char cfg = WDT_KRST | WDT_PWROK; unsigned char cfg = WDT_KRST;
int tm = timeout; int tm = timeout;
if (testmode) if (testmode)
@ -226,6 +227,9 @@ static void wdt_update_timeout(void)
else else
tm /= 60; tm /= 60;
if (chip_type != IT8721_ID)
cfg |= WDT_PWROK;
superio_outb(cfg, WDTCFG); superio_outb(cfg, WDTCFG);
superio_outb(tm, WDTVALLSB); superio_outb(tm, WDTVALLSB);
if (max_units > 255) if (max_units > 255)
@ -555,7 +559,6 @@ static int __init it87_wdt_init(void)
{ {
int rc = 0; int rc = 0;
int try_gameport = !nogameport; int try_gameport = !nogameport;
u16 chip_type;
u8 chip_rev; u8 chip_rev;
unsigned long flags; unsigned long flags;
@ -581,6 +584,7 @@ static int __init it87_wdt_init(void)
break; break;
case IT8718_ID: case IT8718_ID:
case IT8720_ID: case IT8720_ID:
case IT8721_ID:
max_units = 65535; max_units = 65535;
try_gameport = 0; try_gameport = 0;
break; break;

View File

@ -0,0 +1,322 @@
/*
* Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net>
* JZ4740 Watchdog driver
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/watchdog.h>
#include <linux/init.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <asm/mach-jz4740/timer.h>
#define JZ_REG_WDT_TIMER_DATA 0x0
#define JZ_REG_WDT_COUNTER_ENABLE 0x4
#define JZ_REG_WDT_TIMER_COUNTER 0x8
#define JZ_REG_WDT_TIMER_CONTROL 0xC
#define JZ_WDT_CLOCK_PCLK 0x1
#define JZ_WDT_CLOCK_RTC 0x2
#define JZ_WDT_CLOCK_EXT 0x4
#define WDT_IN_USE 0
#define WDT_OK_TO_CLOSE 1
#define JZ_WDT_CLOCK_DIV_SHIFT 3
#define JZ_WDT_CLOCK_DIV_1 (0 << JZ_WDT_CLOCK_DIV_SHIFT)
#define JZ_WDT_CLOCK_DIV_4 (1 << JZ_WDT_CLOCK_DIV_SHIFT)
#define JZ_WDT_CLOCK_DIV_16 (2 << JZ_WDT_CLOCK_DIV_SHIFT)
#define JZ_WDT_CLOCK_DIV_64 (3 << JZ_WDT_CLOCK_DIV_SHIFT)
#define JZ_WDT_CLOCK_DIV_256 (4 << JZ_WDT_CLOCK_DIV_SHIFT)
#define JZ_WDT_CLOCK_DIV_1024 (5 << JZ_WDT_CLOCK_DIV_SHIFT)
#define DEFAULT_HEARTBEAT 5
#define MAX_HEARTBEAT 2048
static struct {
void __iomem *base;
struct resource *mem;
struct clk *rtc_clk;
unsigned long status;
} jz4740_wdt;
static int heartbeat = DEFAULT_HEARTBEAT;
static void jz4740_wdt_service(void)
{
writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
}
static void jz4740_wdt_set_heartbeat(int new_heartbeat)
{
unsigned int rtc_clk_rate;
unsigned int timeout_value;
unsigned short clock_div = JZ_WDT_CLOCK_DIV_1;
heartbeat = new_heartbeat;
rtc_clk_rate = clk_get_rate(jz4740_wdt.rtc_clk);
timeout_value = rtc_clk_rate * heartbeat;
while (timeout_value > 0xffff) {
if (clock_div == JZ_WDT_CLOCK_DIV_1024) {
/* Requested timeout too high;
* use highest possible value. */
timeout_value = 0xffff;
break;
}
timeout_value >>= 2;
clock_div += (1 << JZ_WDT_CLOCK_DIV_SHIFT);
}
writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
writew(clock_div, jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
writew((u16)timeout_value, jz4740_wdt.base + JZ_REG_WDT_TIMER_DATA);
writew(0x0, jz4740_wdt.base + JZ_REG_WDT_TIMER_COUNTER);
writew(clock_div | JZ_WDT_CLOCK_RTC,
jz4740_wdt.base + JZ_REG_WDT_TIMER_CONTROL);
writeb(0x1, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
}
static void jz4740_wdt_enable(void)
{
jz4740_timer_enable_watchdog();
jz4740_wdt_set_heartbeat(heartbeat);
}
static void jz4740_wdt_disable(void)
{
jz4740_timer_disable_watchdog();
writeb(0x0, jz4740_wdt.base + JZ_REG_WDT_COUNTER_ENABLE);
}
static int jz4740_wdt_open(struct inode *inode, struct file *file)
{
if (test_and_set_bit(WDT_IN_USE, &jz4740_wdt.status))
return -EBUSY;
jz4740_wdt_enable();
return nonseekable_open(inode, file);
}
static ssize_t jz4740_wdt_write(struct file *file, const char *data,
size_t len, loff_t *ppos)
{
if (len) {
size_t i;
clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
set_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status);
}
jz4740_wdt_service();
}
return len;
}
static const struct watchdog_info ident = {
.options = WDIOF_KEEPALIVEPING,
.identity = "jz4740 Watchdog",
};
static long jz4740_wdt_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = -ENOTTY;
int heartbeat_seconds;
switch (cmd) {
case WDIOC_GETSUPPORT:
ret = copy_to_user((struct watchdog_info *)arg, &ident,
sizeof(ident)) ? -EFAULT : 0;
break;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
ret = put_user(0, (int *)arg);
break;
case WDIOC_KEEPALIVE:
jz4740_wdt_service();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(heartbeat_seconds, (int __user *)arg))
return -EFAULT;
jz4740_wdt_set_heartbeat(heartbeat_seconds);
return 0;
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, (int *)arg);
default:
break;
}
return ret;
}
static int jz4740_wdt_release(struct inode *inode, struct file *file)
{
jz4740_wdt_service();
if (test_and_clear_bit(WDT_OK_TO_CLOSE, &jz4740_wdt.status))
jz4740_wdt_disable();
clear_bit(WDT_IN_USE, &jz4740_wdt.status);
return 0;
}
static const struct file_operations jz4740_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = jz4740_wdt_write,
.unlocked_ioctl = jz4740_wdt_ioctl,
.open = jz4740_wdt_open,
.release = jz4740_wdt_release,
};
static struct miscdevice jz4740_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &jz4740_wdt_fops,
};
static int __devinit jz4740_wdt_probe(struct platform_device *pdev)
{
int ret = 0, size;
struct resource *res;
struct device *dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(dev, "failed to get memory region resource\n");
return -ENXIO;
}
size = resource_size(res);
jz4740_wdt.mem = request_mem_region(res->start, size, pdev->name);
if (jz4740_wdt.mem == NULL) {
dev_err(dev, "failed to get memory region\n");
return -EBUSY;
}
jz4740_wdt.base = ioremap_nocache(res->start, size);
if (jz4740_wdt.base == NULL) {
dev_err(dev, "failed to map memory region\n");
ret = -EBUSY;
goto err_release_region;
}
jz4740_wdt.rtc_clk = clk_get(NULL, "rtc");
if (IS_ERR(jz4740_wdt.rtc_clk)) {
dev_err(dev, "cannot find RTC clock\n");
ret = PTR_ERR(jz4740_wdt.rtc_clk);
goto err_iounmap;
}
ret = misc_register(&jz4740_wdt_miscdev);
if (ret < 0) {
dev_err(dev, "cannot register misc device\n");
goto err_disable_clk;
}
return 0;
err_disable_clk:
clk_put(jz4740_wdt.rtc_clk);
err_iounmap:
iounmap(jz4740_wdt.base);
err_release_region:
release_mem_region(jz4740_wdt.mem->start,
resource_size(jz4740_wdt.mem));
return ret;
}
static int __devexit jz4740_wdt_remove(struct platform_device *pdev)
{
jz4740_wdt_disable();
misc_deregister(&jz4740_wdt_miscdev);
clk_put(jz4740_wdt.rtc_clk);
iounmap(jz4740_wdt.base);
jz4740_wdt.base = NULL;
release_mem_region(jz4740_wdt.mem->start,
resource_size(jz4740_wdt.mem));
jz4740_wdt.mem = NULL;
return 0;
}
static struct platform_driver jz4740_wdt_driver = {
.probe = jz4740_wdt_probe,
.remove = __devexit_p(jz4740_wdt_remove),
.driver = {
.name = "jz4740-wdt",
.owner = THIS_MODULE,
},
};
static int __init jz4740_wdt_init(void)
{
return platform_driver_register(&jz4740_wdt_driver);
}
module_init(jz4740_wdt_init);
static void __exit jz4740_wdt_exit(void)
{
platform_driver_unregister(&jz4740_wdt_driver);
}
module_exit(jz4740_wdt_exit);
MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("jz4740 Watchdog Driver");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat,
"Watchdog heartbeat period in seconds from 1 to "
__MODULE_STRING(MAX_HEARTBEAT) ", default "
__MODULE_STRING(DEFAULT_HEARTBEAT));
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_ALIAS("platform:jz4740-wdt");

View File

@ -289,7 +289,7 @@ static struct miscdevice nv_tco_miscdev = {
* register a pci_driver, because someone else might one day * register a pci_driver, because someone else might one day
* want to register another driver on the same PCI id. * want to register another driver on the same PCI id.
*/ */
static struct pci_device_id tco_pci_tbl[] = { static DEFINE_PCI_DEVICE_TABLE(tco_pci_tbl) = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_SMBUS,
PCI_ANY_ID, PCI_ANY_ID, }, PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS, { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_SMBUS,

View File

@ -817,7 +817,7 @@ static void __devexit pcipcwd_card_exit(struct pci_dev *pdev)
cards_found--; cards_found--;
} }
static struct pci_device_id pcipcwd_pci_tbl[] = { static DEFINE_PCI_DEVICE_TABLE(pcipcwd_pci_tbl) = {
{ PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD, { PCI_VENDOR_ID_QUICKLOGIC, PCI_DEVICE_ID_WATCHDOG_PCIPCWD,
PCI_ANY_ID, PCI_ANY_ID, }, PCI_ANY_ID, PCI_ANY_ID, },
{ 0 }, /* End of list */ { 0 }, /* End of list */

View File

@ -259,7 +259,7 @@ static struct miscdevice sp5100_tco_miscdev = {
* register a pci_driver, because someone else might * register a pci_driver, because someone else might
* want to register another driver on the same PCI id. * want to register another driver on the same PCI id.
*/ */
static struct pci_device_id sp5100_tco_pci_tbl[] = { static DEFINE_PCI_DEVICE_TABLE(sp5100_tco_pci_tbl) = {
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_ANY_ID,
PCI_ANY_ID, }, PCI_ANY_ID, },
{ 0, }, /* End of list */ { 0, }, /* End of list */

View File

@ -727,7 +727,7 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev)
} }
static struct pci_device_id wdtpci_pci_tbl[] = { static DEFINE_PCI_DEVICE_TABLE(wdtpci_pci_tbl) = {
{ {
.vendor = PCI_VENDOR_ID_ACCESSIO, .vendor = PCI_VENDOR_ID_ACCESSIO,
.device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM, .device = PCI_DEVICE_ID_ACCESSIO_WDG_CSM,

359
drivers/watchdog/xen_wdt.c Normal file
View File

@ -0,0 +1,359 @@
/*
* Xen Watchdog Driver
*
* (c) Copyright 2010 Novell, Inc.
*
* 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.
*/
#define DRV_NAME "wdt"
#define DRV_VERSION "0.01"
#define PFX DRV_NAME ": "
#include <linux/bug.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/hrtimer.h>
#include <linux/kernel.h>
#include <linux/ktime.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/uaccess.h>
#include <linux/watchdog.h>
#include <xen/xen.h>
#include <asm/xen/hypercall.h>
#include <xen/interface/sched.h>
static struct platform_device *platform_device;
static DEFINE_SPINLOCK(wdt_lock);
static struct sched_watchdog wdt;
static __kernel_time_t wdt_expires;
static bool is_active, expect_release;
#define WATCHDOG_TIMEOUT 60 /* in seconds */
static unsigned int timeout = WATCHDOG_TIMEOUT;
module_param(timeout, uint, S_IRUGO);
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds "
"(default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, S_IRUGO);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
"(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
static inline __kernel_time_t set_timeout(void)
{
wdt.timeout = timeout;
return ktime_to_timespec(ktime_get()).tv_sec + timeout;
}
static int xen_wdt_start(void)
{
__kernel_time_t expires;
int err;
spin_lock(&wdt_lock);
expires = set_timeout();
if (!wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
else
err = -EBUSY;
if (err > 0) {
wdt.id = err;
wdt_expires = expires;
err = 0;
} else
BUG_ON(!err);
spin_unlock(&wdt_lock);
return err;
}
static int xen_wdt_stop(void)
{
int err = 0;
spin_lock(&wdt_lock);
wdt.timeout = 0;
if (wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
if (!err)
wdt.id = 0;
spin_unlock(&wdt_lock);
return err;
}
static int xen_wdt_kick(void)
{
__kernel_time_t expires;
int err;
spin_lock(&wdt_lock);
expires = set_timeout();
if (wdt.id)
err = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wdt);
else
err = -ENXIO;
if (!err)
wdt_expires = expires;
spin_unlock(&wdt_lock);
return err;
}
static int xen_wdt_open(struct inode *inode, struct file *file)
{
int err;
/* /dev/watchdog can only be opened once */
if (xchg(&is_active, true))
return -EBUSY;
err = xen_wdt_start();
if (err == -EBUSY)
err = xen_wdt_kick();
return err ?: nonseekable_open(inode, file);
}
static int xen_wdt_release(struct inode *inode, struct file *file)
{
if (expect_release)
xen_wdt_stop();
else {
printk(KERN_CRIT PFX
"unexpected close, not stopping watchdog!\n");
xen_wdt_kick();
}
is_active = false;
expect_release = false;
return 0;
}
static ssize_t xen_wdt_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
/* See if we got the magic character 'V' and reload the timer */
if (len) {
if (!nowayout) {
size_t i;
/* in case it was set long ago */
expect_release = false;
/* scan to see whether or not we got the magic
character */
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
expect_release = true;
}
}
/* someone wrote to us, we should reload the timer */
xen_wdt_kick();
}
return len;
}
static long xen_wdt_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
int new_options, retval = -EINVAL;
int new_timeout;
int __user *argp = (void __user *)arg;
static const struct watchdog_info ident = {
.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
.firmware_version = 0,
.identity = DRV_NAME,
};
switch (cmd) {
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, argp);
case WDIOC_SETOPTIONS:
if (get_user(new_options, argp))
return -EFAULT;
if (new_options & WDIOS_DISABLECARD)
retval = xen_wdt_stop();
if (new_options & WDIOS_ENABLECARD) {
retval = xen_wdt_start();
if (retval == -EBUSY)
retval = xen_wdt_kick();
}
return retval;
case WDIOC_KEEPALIVE:
xen_wdt_kick();
return 0;
case WDIOC_SETTIMEOUT:
if (get_user(new_timeout, argp))
return -EFAULT;
if (!new_timeout)
return -EINVAL;
timeout = new_timeout;
xen_wdt_kick();
/* fall through */
case WDIOC_GETTIMEOUT:
return put_user(timeout, argp);
case WDIOC_GETTIMELEFT:
retval = wdt_expires - ktime_to_timespec(ktime_get()).tv_sec;
return put_user(retval, argp);
}
return -ENOTTY;
}
static const struct file_operations xen_wdt_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = xen_wdt_write,
.unlocked_ioctl = xen_wdt_ioctl,
.open = xen_wdt_open,
.release = xen_wdt_release,
};
static struct miscdevice xen_wdt_miscdev = {
.minor = WATCHDOG_MINOR,
.name = "watchdog",
.fops = &xen_wdt_fops,
};
static int __devinit xen_wdt_probe(struct platform_device *dev)
{
struct sched_watchdog wd = { .id = ~0 };
int ret = HYPERVISOR_sched_op(SCHEDOP_watchdog, &wd);
switch (ret) {
case -EINVAL:
if (!timeout) {
timeout = WATCHDOG_TIMEOUT;
printk(KERN_INFO PFX
"timeout value invalid, using %d\n", timeout);
}
ret = misc_register(&xen_wdt_miscdev);
if (ret) {
printk(KERN_ERR PFX
"cannot register miscdev on minor=%d (%d)\n",
WATCHDOG_MINOR, ret);
break;
}
printk(KERN_INFO PFX
"initialized (timeout=%ds, nowayout=%d)\n",
timeout, nowayout);
break;
case -ENOSYS:
printk(KERN_INFO PFX "not supported\n");
ret = -ENODEV;
break;
default:
printk(KERN_INFO PFX "bogus return value %d\n", ret);
break;
}
return ret;
}
static int __devexit xen_wdt_remove(struct platform_device *dev)
{
/* Stop the timer before we leave */
if (!nowayout)
xen_wdt_stop();
misc_deregister(&xen_wdt_miscdev);
return 0;
}
static void xen_wdt_shutdown(struct platform_device *dev)
{
xen_wdt_stop();
}
static int xen_wdt_suspend(struct platform_device *dev, pm_message_t state)
{
return xen_wdt_stop();
}
static int xen_wdt_resume(struct platform_device *dev)
{
return xen_wdt_start();
}
static struct platform_driver xen_wdt_driver = {
.probe = xen_wdt_probe,
.remove = __devexit_p(xen_wdt_remove),
.shutdown = xen_wdt_shutdown,
.suspend = xen_wdt_suspend,
.resume = xen_wdt_resume,
.driver = {
.owner = THIS_MODULE,
.name = DRV_NAME,
},
};
static int __init xen_wdt_init_module(void)
{
int err;
if (!xen_domain())
return -ENODEV;
printk(KERN_INFO PFX "Xen WatchDog Timer Driver v%s\n", DRV_VERSION);
err = platform_driver_register(&xen_wdt_driver);
if (err)
return err;
platform_device = platform_device_register_simple(DRV_NAME,
-1, NULL, 0);
if (IS_ERR(platform_device)) {
err = PTR_ERR(platform_device);
platform_driver_unregister(&xen_wdt_driver);
}
return err;
}
static void __exit xen_wdt_cleanup_module(void)
{
platform_device_unregister(platform_device);
platform_driver_unregister(&xen_wdt_driver);
printk(KERN_INFO PFX "module unloaded\n");
}
module_init(xen_wdt_init_module);
module_exit(xen_wdt_cleanup_module);
MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);

View File

@ -64,6 +64,39 @@ struct sched_poll {
}; };
DEFINE_GUEST_HANDLE_STRUCT(sched_poll); DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
/*
* Declare a shutdown for another domain. The main use of this function is
* in interpreting shutdown requests and reasons for fully-virtualized
* domains. A para-virtualized domain may use SCHEDOP_shutdown directly.
* @arg == pointer to sched_remote_shutdown structure.
*/
#define SCHEDOP_remote_shutdown 4
struct sched_remote_shutdown {
domid_t domain_id; /* Remote domain ID */
unsigned int reason; /* SHUTDOWN_xxx reason */
};
/*
* Latch a shutdown code, so that when the domain later shuts down it
* reports this code to the control tools.
* @arg == as for SCHEDOP_shutdown.
*/
#define SCHEDOP_shutdown_code 5
/*
* Setup, poke and destroy a domain watchdog timer.
* @arg == pointer to sched_watchdog structure.
* With id == 0, setup a domain watchdog timer to cause domain shutdown
* after timeout, returns watchdog id.
* With id != 0 and timeout == 0, destroy domain watchdog timer.
* With id != 0 and timeout != 0, poke watchdog timer and set new timeout.
*/
#define SCHEDOP_watchdog 6
struct sched_watchdog {
uint32_t id; /* watchdog ID */
uint32_t timeout; /* timeout */
};
/* /*
* Reason codes for SCHEDOP_shutdown. These may be interpreted by control * Reason codes for SCHEDOP_shutdown. These may be interpreted by control
* software to determine the appropriate action. For the most part, Xen does * software to determine the appropriate action. For the most part, Xen does
@ -73,5 +106,6 @@ DEFINE_GUEST_HANDLE_STRUCT(sched_poll);
#define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */ #define SHUTDOWN_reboot 1 /* Clean up, kill, and then restart. */
#define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */ #define SHUTDOWN_suspend 2 /* Clean up, save suspend info, kill. */
#define SHUTDOWN_crash 3 /* Tell controller we've crashed. */ #define SHUTDOWN_crash 3 /* Tell controller we've crashed. */
#define SHUTDOWN_watchdog 4 /* Restart because watchdog time expired. */
#endif /* __XEN_PUBLIC_SCHED_H__ */ #endif /* __XEN_PUBLIC_SCHED_H__ */