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:
commit
4b0e976c66
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
|
|
@ -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
|
||||||
#
|
#
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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, },
|
||||||
|
|
|
@ -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) },
|
||||||
{ }
|
{ }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)},
|
||||||
|
|
|
@ -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);
|
|
@ -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 */
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
|
@ -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,
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
|
@ -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__ */
|
||||||
|
|
Loading…
Reference in New Issue