diff options
| author | Takashi Iwai <tiwai@suse.de> | 2011-08-08 14:30:29 +0200 | 
|---|---|---|
| committer | Takashi Iwai <tiwai@suse.de> | 2011-08-08 14:30:29 +0200 | 
| commit | 0a2d31b62dba9b5b92a38c67c9cc42630513662a (patch) | |
| tree | f755d74ec85248de645e10c45ed1a2ed467530f6 /drivers/watchdog | |
| parent | 8039290a91c5dc4414093c086987a5d7738fe2fd (diff) | |
| parent | df944f66784e6d4f2f50739263a4947885d8b6ae (diff) | |
Merge branch 'fix/kconfig' into for-linus
Diffstat (limited to 'drivers/watchdog')
27 files changed, 1908 insertions, 494 deletions
| diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 022f9eb0b7bf..86b0735e6aa0 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -28,6 +28,14 @@ menuconfig WATCHDOG  if WATCHDOG +config WATCHDOG_CORE +	bool "WatchDog Timer Driver Core" +	---help--- +	  Say Y here if you want to use the new watchdog timer driver core. +	  This driver provides a framework for all watchdog timer drivers +	  and gives them the /dev/watchdog interface (and later also the +	  sysfs interface). +  config WATCHDOG_NOWAYOUT  	bool "Disable watchdog shutdown on close"  	help @@ -186,6 +194,15 @@ config SA1100_WATCHDOG  	  To compile this driver as a module, choose M here: the  	  module will be called sa1100_wdt. +config DW_WATCHDOG +	tristate "Synopsys DesignWare watchdog" +	depends on ARM && HAVE_CLK +	help +	  Say Y here if to include support for the Synopsys DesignWare +	  watchdog timer found in many ARM chips. +	  To compile this driver as a module, choose M here: the +	  module will be called dw_wdt. +  config MPCORE_WATCHDOG  	tristate "MPcore watchdog"  	depends on HAVE_ARM_TWD @@ -321,7 +338,7 @@ config MAX63XX_WATCHDOG  config IMX2_WDT  	tristate "IMX2+ Watchdog" -	depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5 +	depends on IMX_HAVE_PLATFORM_IMX2_WDT  	help  	  This is the driver for the hardware watchdog  	  on the Freescale IMX2 and later processors. @@ -535,8 +552,7 @@ config I6300ESB_WDT  config INTEL_SCU_WATCHDOG  	bool "Intel SCU Watchdog for Mobile Platforms" -	depends on WATCHDOG -	depends on INTEL_SCU_IPC +	depends on X86_MRST  	---help---  	  Hardware driver for the watchdog time built into the Intel SCU  	  for Intel Mobile Platforms. @@ -600,8 +616,7 @@ config IT87_WDT  config HP_WATCHDOG  	tristate "HP ProLiant iLO2+ Hardware Watchdog Timer" -	depends on X86 -	default m +	depends on X86 && PCI  	help  	  A software monitoring watchdog and NMI sourcing driver. This driver  	  will detect lockups and provide a stack trace. This is a driver that @@ -881,6 +896,20 @@ config M54xx_WATCHDOG  	  To compile this driver as a module, choose M here: the  	  module will be called m54xx_wdt. +# MicroBlaze Architecture + +config XILINX_WATCHDOG +	tristate "Xilinx Watchdog timer" +	depends on MICROBLAZE +	---help--- +	  Watchdog driver for the xps_timebase_wdt ip core. + +	  IMPORTANT: The xps_timebase_wdt parent must have the property +	  "clock-frequency" at device tree. + +	  To compile this driver as a module, choose M here: the +	  module will be called of_xilinx_wdt. +  # MIPS Architecture  config ATH79_WDT diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ed26f7094e47..55bd5740e910 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -2,6 +2,10 @@  # Makefile for the WatchDog device drivers.  # +# The WatchDog Timer Driver Core. +watchdog-objs	+= watchdog_core.o watchdog_dev.o +obj-$(CONFIG_WATCHDOG_CORE)	+= watchdog.o +  # Only one watchdog can succeed. We probe the ISA/PCI/USB based  # watchdog-cards first, then the architecture specific watchdog  # drivers and then the architecture independent "softdog" driver. @@ -37,6 +41,7 @@ obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o  obj-$(CONFIG_KS8695_WATCHDOG) += ks8695_wdt.o  obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o  obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o +obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o  obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o  obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o  obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o @@ -109,6 +114,9 @@ obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o  # M68K Architecture  obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o +# MicroBlaze Architecture +obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o +  # MIPS Architecture  obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o  obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o diff --git a/drivers/watchdog/at32ap700x_wdt.c b/drivers/watchdog/at32ap700x_wdt.c index 750bc5281d79..4ca5d40304b2 100644 --- a/drivers/watchdog/at32ap700x_wdt.c +++ b/drivers/watchdog/at32ap700x_wdt.c @@ -448,7 +448,7 @@ static void __exit at32_wdt_exit(void)  }  module_exit(at32_wdt_exit); -MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");  MODULE_DESCRIPTION("Watchdog driver for Atmel AT32AP700X");  MODULE_LICENSE("GPL");  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/at91sam9_wdt.c b/drivers/watchdog/at91sam9_wdt.c index eac26021e8da..87445b2d72a7 100644 --- a/drivers/watchdog/at91sam9_wdt.c +++ b/drivers/watchdog/at91sam9_wdt.c @@ -31,7 +31,7 @@  #include <linux/bitops.h>  #include <linux/uaccess.h> -#include <mach/at91_wdt.h> +#include "at91sam9_wdt.h"  #define DRV_NAME "AT91SAM9 Watchdog" @@ -284,27 +284,8 @@ static int __exit at91wdt_remove(struct platform_device *pdev)  	return res;  } -#ifdef CONFIG_PM - -static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message) -{ -	return 0; -} - -static int at91wdt_resume(struct platform_device *pdev) -{ -	return 0; -} - -#else -#define at91wdt_suspend	NULL -#define at91wdt_resume	NULL -#endif -  static struct platform_driver at91wdt_driver = {  	.remove		= __exit_p(at91wdt_remove), -	.suspend	= at91wdt_suspend, -	.resume		= at91wdt_resume,  	.driver		= {  		.name	= "at91_wdt",  		.owner	= THIS_MODULE, diff --git a/drivers/watchdog/at91sam9_wdt.h b/drivers/watchdog/at91sam9_wdt.h new file mode 100644 index 000000000000..757f9cab5c82 --- /dev/null +++ b/drivers/watchdog/at91sam9_wdt.h @@ -0,0 +1,37 @@ +/* + * drivers/watchdog/at91sam9_wdt.h + * + * Copyright (C) 2007 Andrew Victor + * Copyright (C) 2007 Atmel Corporation. + * + * Watchdog Timer (WDT) - System peripherals regsters. + * Based on AT91SAM9261 datasheet revision D. + * + * 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. + */ + +#ifndef AT91_WDT_H +#define AT91_WDT_H + +#define AT91_WDT_CR		(AT91_WDT + 0x00)	/* Watchdog Control Register */ +#define		AT91_WDT_WDRSTT		(1    << 0)		/* Restart */ +#define		AT91_WDT_KEY		(0xa5 << 24)		/* KEY Password */ + +#define AT91_WDT_MR		(AT91_WDT + 0x04)	/* Watchdog Mode Register */ +#define		AT91_WDT_WDV		(0xfff << 0)		/* Counter Value */ +#define		AT91_WDT_WDFIEN		(1     << 12)		/* Fault Interrupt Enable */ +#define		AT91_WDT_WDRSTEN	(1     << 13)		/* Reset Processor */ +#define		AT91_WDT_WDRPROC	(1     << 14)		/* Timer Restart */ +#define		AT91_WDT_WDDIS		(1     << 15)		/* Watchdog Disable */ +#define		AT91_WDT_WDD		(0xfff << 16)		/* Delta Value */ +#define		AT91_WDT_WDDBGHLT	(1     << 28)		/* Debug Halt */ +#define		AT91_WDT_WDIDLEHLT	(1     << 29)		/* Idle Halt */ + +#define AT91_WDT_SR		(AT91_WDT + 0x08)	/* Watchdog Status Register */ +#define		AT91_WDT_WDUNF		(1 << 0)		/* Watchdog Underflow */ +#define		AT91_WDT_WDERR		(1 << 1)		/* Watchdog Error */ + +#endif diff --git a/drivers/watchdog/dw_wdt.c b/drivers/watchdog/dw_wdt.c new file mode 100644 index 000000000000..f10f8c0abba4 --- /dev/null +++ b/drivers/watchdog/dw_wdt.c @@ -0,0 +1,376 @@ +/* + * Copyright 2010-2011 Picochip Ltd., Jamie Iles + * http://www.picochip.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * This file implements a driver for the Synopsys DesignWare watchdog device + * in the many ARM subsystems. The watchdog has 16 different timeout periods + * and these are a function of the input clock frequency. + * + * The DesignWare watchdog cannot be stopped once it has been started so we + * use a software timer to implement a ping that will keep the watchdog alive. + * If we receive an expected close for the watchdog then we keep the timer + * running, otherwise the timer is stopped and the watchdog will expire. + */ +#define pr_fmt(fmt) "dw_wdt: " fmt + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/fs.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/timer.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> + +#define WDOG_CONTROL_REG_OFFSET		    0x00 +#define WDOG_CONTROL_REG_WDT_EN_MASK	    0x01 +#define WDOG_TIMEOUT_RANGE_REG_OFFSET	    0x04 +#define WDOG_CURRENT_COUNT_REG_OFFSET	    0x08 +#define WDOG_COUNTER_RESTART_REG_OFFSET     0x0c +#define WDOG_COUNTER_RESTART_KICK_VALUE	    0x76 + +/* The maximum TOP (timeout period) value that can be set in the watchdog. */ +#define DW_WDT_MAX_TOP		15 + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " +		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +#define WDT_TIMEOUT		(HZ / 2) + +static struct { +	spinlock_t		lock; +	void __iomem		*regs; +	struct clk		*clk; +	unsigned long		in_use; +	unsigned long		next_heartbeat; +	struct timer_list	timer; +	int			expect_close; +} dw_wdt; + +static inline int dw_wdt_is_enabled(void) +{ +	return readl(dw_wdt.regs + WDOG_CONTROL_REG_OFFSET) & +		WDOG_CONTROL_REG_WDT_EN_MASK; +} + +static inline int dw_wdt_top_in_seconds(unsigned top) +{ +	/* +	 * There are 16 possible timeout values in 0..15 where the number of +	 * cycles is 2 ^ (16 + i) and the watchdog counts down. +	 */ +	return (1 << (16 + top)) / clk_get_rate(dw_wdt.clk); +} + +static int dw_wdt_get_top(void) +{ +	int top = readl(dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; + +	return dw_wdt_top_in_seconds(top); +} + +static inline void dw_wdt_set_next_heartbeat(void) +{ +	dw_wdt.next_heartbeat = jiffies + dw_wdt_get_top() * HZ; +} + +static int dw_wdt_set_top(unsigned top_s) +{ +	int i, top_val = DW_WDT_MAX_TOP; + +	/* +	 * Iterate over the timeout values until we find the closest match. We +	 * always look for >=. +	 */ +	for (i = 0; i <= DW_WDT_MAX_TOP; ++i) +		if (dw_wdt_top_in_seconds(i) >= top_s) { +			top_val = i; +			break; +		} + +	/* Set the new value in the watchdog. */ +	writel(top_val, dw_wdt.regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); + +	dw_wdt_set_next_heartbeat(); + +	return dw_wdt_top_in_seconds(top_val); +} + +static void dw_wdt_keepalive(void) +{ +	writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt.regs + +	       WDOG_COUNTER_RESTART_REG_OFFSET); +} + +static void dw_wdt_ping(unsigned long data) +{ +	if (time_before(jiffies, dw_wdt.next_heartbeat) || +	    (!nowayout && !dw_wdt.in_use)) { +		dw_wdt_keepalive(); +		mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); +	} else +		pr_crit("keepalive missed, machine will reset\n"); +} + +static int dw_wdt_open(struct inode *inode, struct file *filp) +{ +	if (test_and_set_bit(0, &dw_wdt.in_use)) +		return -EBUSY; + +	/* Make sure we don't get unloaded. */ +	__module_get(THIS_MODULE); + +	spin_lock(&dw_wdt.lock); +	if (!dw_wdt_is_enabled()) { +		/* +		 * The watchdog is not currently enabled. Set the timeout to +		 * the maximum and then start it. +		 */ +		dw_wdt_set_top(DW_WDT_MAX_TOP); +		writel(WDOG_CONTROL_REG_WDT_EN_MASK, +		       dw_wdt.regs + WDOG_CONTROL_REG_OFFSET); +	} + +	dw_wdt_set_next_heartbeat(); + +	spin_unlock(&dw_wdt.lock); + +	return nonseekable_open(inode, filp); +} + +ssize_t dw_wdt_write(struct file *filp, const char __user *buf, size_t len, +		     loff_t *offset) +{ +	if (!len) +		return 0; + +	if (!nowayout) { +		size_t i; + +		dw_wdt.expect_close = 0; + +		for (i = 0; i < len; ++i) { +			char c; + +			if (get_user(c, buf + i)) +				return -EFAULT; + +			if (c == 'V') { +				dw_wdt.expect_close = 1; +				break; +			} +		} +	} + +	dw_wdt_set_next_heartbeat(); +	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); + +	return len; +} + +static u32 dw_wdt_time_left(void) +{ +	return readl(dw_wdt.regs + WDOG_CURRENT_COUNT_REG_OFFSET) / +		clk_get_rate(dw_wdt.clk); +} + +static const struct watchdog_info dw_wdt_ident = { +	.options	= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | +			  WDIOF_MAGICCLOSE, +	.identity	= "Synopsys DesignWare Watchdog", +}; + +static long dw_wdt_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ +	unsigned long val; +	int timeout; + +	switch (cmd) { +	case WDIOC_GETSUPPORT: +		return copy_to_user((struct watchdog_info *)arg, &dw_wdt_ident, +				    sizeof(dw_wdt_ident)) ? -EFAULT : 0; + +	case WDIOC_GETSTATUS: +	case WDIOC_GETBOOTSTATUS: +		return put_user(0, (int *)arg); + +	case WDIOC_KEEPALIVE: +		dw_wdt_set_next_heartbeat(); +		return 0; + +	case WDIOC_SETTIMEOUT: +		if (get_user(val, (int __user *)arg)) +			return -EFAULT; +		timeout = dw_wdt_set_top(val); +		return put_user(timeout , (int __user *)arg); + +	case WDIOC_GETTIMEOUT: +		return put_user(dw_wdt_get_top(), (int __user *)arg); + +	case WDIOC_GETTIMELEFT: +		/* Get the time left until expiry. */ +		if (get_user(val, (int __user *)arg)) +			return -EFAULT; +		return put_user(dw_wdt_time_left(), (int __user *)arg); + +	default: +		return -ENOTTY; +	} +} + +static int dw_wdt_release(struct inode *inode, struct file *filp) +{ +	clear_bit(0, &dw_wdt.in_use); + +	if (!dw_wdt.expect_close) { +		del_timer(&dw_wdt.timer); + +		if (!nowayout) +			pr_crit("unexpected close, system will reboot soon\n"); +		else +			pr_crit("watchdog cannot be disabled, system will reboot soon\n"); +	} + +	dw_wdt.expect_close = 0; + +	return 0; +} + +#ifdef CONFIG_PM +static int dw_wdt_suspend(struct device *dev) +{ +	clk_disable(dw_wdt.clk); + +	return 0; +} + +static int dw_wdt_resume(struct device *dev) +{ +	int err = clk_enable(dw_wdt.clk); + +	if (err) +		return err; + +	dw_wdt_keepalive(); + +	return 0; +} + +static const struct dev_pm_ops dw_wdt_pm_ops = { +	.suspend	= dw_wdt_suspend, +	.resume		= dw_wdt_resume, +}; +#endif /* CONFIG_PM */ + +static const struct file_operations wdt_fops = { +	.owner		= THIS_MODULE, +	.llseek		= no_llseek, +	.open		= dw_wdt_open, +	.write		= dw_wdt_write, +	.unlocked_ioctl	= dw_wdt_ioctl, +	.release	= dw_wdt_release +}; + +static struct miscdevice dw_wdt_miscdev = { +	.fops		= &wdt_fops, +	.name		= "watchdog", +	.minor		= WATCHDOG_MINOR, +}; + +static int __devinit dw_wdt_drv_probe(struct platform_device *pdev) +{ +	int ret; +	struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + +	if (!mem) +		return -EINVAL; + +	if (!devm_request_mem_region(&pdev->dev, mem->start, resource_size(mem), +				     "dw_wdt")) +		return -ENOMEM; + +	dw_wdt.regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem)); +	if (!dw_wdt.regs) +		return -ENOMEM; + +	dw_wdt.clk = clk_get(&pdev->dev, NULL); +	if (IS_ERR(dw_wdt.clk)) +		return PTR_ERR(dw_wdt.clk); + +	ret = clk_enable(dw_wdt.clk); +	if (ret) +		goto out_put_clk; + +	spin_lock_init(&dw_wdt.lock); + +	ret = misc_register(&dw_wdt_miscdev); +	if (ret) +		goto out_disable_clk; + +	dw_wdt_set_next_heartbeat(); +	setup_timer(&dw_wdt.timer, dw_wdt_ping, 0); +	mod_timer(&dw_wdt.timer, jiffies + WDT_TIMEOUT); + +	return 0; + +out_disable_clk: +	clk_disable(dw_wdt.clk); +out_put_clk: +	clk_put(dw_wdt.clk); + +	return ret; +} + +static int __devexit dw_wdt_drv_remove(struct platform_device *pdev) +{ +	misc_deregister(&dw_wdt_miscdev); + +	clk_disable(dw_wdt.clk); +	clk_put(dw_wdt.clk); + +	return 0; +} + +static struct platform_driver dw_wdt_driver = { +	.probe		= dw_wdt_drv_probe, +	.remove		= __devexit_p(dw_wdt_drv_remove), +	.driver		= { +		.name	= "dw_wdt", +		.owner	= THIS_MODULE, +#ifdef CONFIG_PM +		.pm	= &dw_wdt_pm_ops, +#endif /* CONFIG_PM */ +	}, +}; + +static int __init dw_wdt_watchdog_init(void) +{ +	return platform_driver_register(&dw_wdt_driver); +} +module_init(dw_wdt_watchdog_init); + +static void __exit dw_wdt_watchdog_exit(void) +{ +	platform_driver_unregister(&dw_wdt_driver); +} +module_exit(dw_wdt_watchdog_exit); + +MODULE_AUTHOR("Jamie Iles"); +MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/gef_wdt.c b/drivers/watchdog/gef_wdt.c index 29a7cd4b90c8..b146082bd85a 100644 --- a/drivers/watchdog/gef_wdt.c +++ b/drivers/watchdog/gef_wdt.c @@ -329,4 +329,4 @@ MODULE_AUTHOR("Martyn Welch <martyn.welch@ge.com>");  MODULE_DESCRIPTION("GE watchdog driver");  MODULE_LICENSE("GPL");  MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); -MODULE_ALIAS("platform: gef_wdt"); +MODULE_ALIAS("platform:gef_wdt"); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index 8cb26855bfed..410fba45378d 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -36,7 +36,7 @@  #include <asm/cacheflush.h>  #endif /* CONFIG_HPWDT_NMI_DECODING */ -#define HPWDT_VERSION			"1.2.0" +#define HPWDT_VERSION			"1.3.0"  #define SECS_TO_TICKS(secs)		((secs) * 1000 / 128)  #define TICKS_TO_SECS(ticks)		((ticks) * 128 / 1000)  #define HPWDT_MAX_TIMER			TICKS_TO_SECS(65535) @@ -87,6 +87,19 @@ struct smbios_cru64_info {  };  #define SMBIOS_CRU64_INFORMATION	212 +/* type 219 */ +struct smbios_proliant_info { +	u8 type; +	u8 byte_length; +	u16 handle; +	u32 power_features; +	u32 omega_features; +	u32 reserved; +	u32 misc_features; +}; +#define SMBIOS_ICRU_INFORMATION		219 + +  struct cmn_registers {  	union {  		struct { @@ -132,6 +145,7 @@ struct cmn_registers {  static unsigned int hpwdt_nmi_decoding;  static unsigned int allow_kdump;  static unsigned int priority;		/* hpwdt at end of die_notify list */ +static unsigned int is_icru;  static DEFINE_SPINLOCK(rom_lock);  static void *cru_rom_addr;  static struct cmn_registers cmn_regs; @@ -476,19 +490,22 @@ static int hpwdt_pretimeout(struct notifier_block *nb, unsigned long ulReason,  		goto out;  	spin_lock_irqsave(&rom_lock, rom_pl); -	if (!die_nmi_called) +	if (!die_nmi_called && !is_icru)  		asminline_call(&cmn_regs, cru_rom_addr);  	die_nmi_called = 1;  	spin_unlock_irqrestore(&rom_lock, rom_pl); -	if (cmn_regs.u1.ral == 0) { -		printk(KERN_WARNING "hpwdt: An NMI occurred, " -			"but unable to determine source.\n"); -	} else { -		if (allow_kdump) -			hpwdt_stop(); -		panic("An NMI occurred, please see the Integrated " -			"Management Log for details.\n"); +	if (!is_icru) { +		if (cmn_regs.u1.ral == 0) { +			printk(KERN_WARNING "hpwdt: An NMI occurred, " +				"but unable to determine source.\n"); +		}  	} + +	if (allow_kdump) +		hpwdt_stop(); +	panic("An NMI occurred, please see the Integrated " +		"Management Log for details.\n"); +  out:  	return NOTIFY_OK;  } @@ -659,30 +676,63 @@ static void __devinit hpwdt_check_nmi_decoding(struct pci_dev *dev)  }  #endif /* CONFIG_X86_LOCAL_APIC */ +/* + *	dmi_find_icru + * + *	Routine Description: + *	This function checks whether or not we are on an iCRU-based server. + *	This check is independent of architecture and needs to be made for + *	any ProLiant system. + */ +static void __devinit dmi_find_icru(const struct dmi_header *dm, void *dummy) +{ +	struct smbios_proliant_info *smbios_proliant_ptr; + +	if (dm->type == SMBIOS_ICRU_INFORMATION) { +		smbios_proliant_ptr = (struct smbios_proliant_info *) dm; +		if (smbios_proliant_ptr->misc_features & 0x01) +			is_icru = 1; +	} +} +  static int __devinit hpwdt_init_nmi_decoding(struct pci_dev *dev)  {  	int retval;  	/* -	 * We need to map the ROM to get the CRU service. -	 * For 32 bit Operating Systems we need to go through the 32 Bit -	 * BIOS Service Directory -	 * For 64 bit Operating Systems we get that service through SMBIOS. +	 * On typical CRU-based systems we need to map that service in +	 * the BIOS. For 32 bit Operating Systems we need to go through +	 * the 32 Bit BIOS Service Directory. For 64 bit Operating +	 * Systems we get that service through SMBIOS. +	 * +	 * On systems that support the new iCRU service all we need to +	 * do is call dmi_walk to get the supported flag value and skip +	 * the old cru detect code.  	 */ -	retval = detect_cru_service(); -	if (retval < 0) { -		dev_warn(&dev->dev, -			"Unable to detect the %d Bit CRU Service.\n", -			HPWDT_ARCH); -		return retval; -	} +	dmi_walk(dmi_find_icru, NULL); +	if (!is_icru) { + +		/* +		* We need to map the ROM to get the CRU service. +		* For 32 bit Operating Systems we need to go through the 32 Bit +		* BIOS Service Directory +		* For 64 bit Operating Systems we get that service through SMBIOS. +		*/ +		retval = detect_cru_service(); +		if (retval < 0) { +			dev_warn(&dev->dev, +				"Unable to detect the %d Bit CRU Service.\n", +				HPWDT_ARCH); +			return retval; +		} -	/* -	 * We know this is the only CRU call we need to make so lets keep as -	 * few instructions as possible once the NMI comes in. -	 */ -	cmn_regs.u1.rah = 0x0D; -	cmn_regs.u1.ral = 0x02; +		/* +		* We know this is the only CRU call we need to make so lets keep as +		* few instructions as possible once the NMI comes in. +		*/ +		cmn_regs.u1.rah = 0x0D; +		cmn_regs.u1.ral = 0x02; +	}  	/*  	 * If the priority is set to 1, then we will be put first on the diff --git a/drivers/watchdog/iTCO_wdt.c b/drivers/watchdog/iTCO_wdt.c index 5fd020da7c55..751a591684da 100644 --- a/drivers/watchdog/iTCO_wdt.c +++ b/drivers/watchdog/iTCO_wdt.c @@ -120,72 +120,12 @@ enum iTCO_chipsets {  	TCO_3420,	/* 3420 */  	TCO_3450,	/* 3450 */  	TCO_EP80579,	/* EP80579 */ -	TCO_CPT1,	/* Cougar Point */ -	TCO_CPT2,	/* Cougar Point Desktop */ -	TCO_CPT3,	/* Cougar Point Mobile */ -	TCO_CPT4,	/* Cougar Point */ -	TCO_CPT5,	/* Cougar Point */ -	TCO_CPT6,	/* Cougar Point */ -	TCO_CPT7,	/* Cougar Point */ -	TCO_CPT8,	/* Cougar Point */ -	TCO_CPT9,	/* Cougar Point */ -	TCO_CPT10,	/* Cougar Point */ -	TCO_CPT11,	/* Cougar Point */ -	TCO_CPT12,	/* Cougar Point */ -	TCO_CPT13,	/* Cougar Point */ -	TCO_CPT14,	/* Cougar Point */ -	TCO_CPT15,	/* Cougar Point */ -	TCO_CPT16,	/* Cougar Point */ -	TCO_CPT17,	/* Cougar Point */ -	TCO_CPT18,	/* Cougar Point */ -	TCO_CPT19,	/* Cougar Point */ -	TCO_CPT20,	/* Cougar Point */ -	TCO_CPT21,	/* Cougar Point */ -	TCO_CPT22,	/* Cougar Point */ -	TCO_CPT23,	/* Cougar Point */ -	TCO_CPT24,	/* Cougar Point */ -	TCO_CPT25,	/* Cougar Point */ -	TCO_CPT26,	/* Cougar Point */ -	TCO_CPT27,	/* Cougar Point */ -	TCO_CPT28,	/* Cougar Point */ -	TCO_CPT29,	/* Cougar Point */ -	TCO_CPT30,	/* Cougar Point */ -	TCO_CPT31,	/* Cougar Point */ -	TCO_PBG1,	/* Patsburg */ -	TCO_PBG2,	/* Patsburg */ +	TCO_CPT,	/* Cougar Point */ +	TCO_CPTD,	/* Cougar Point Desktop */ +	TCO_CPTM,	/* Cougar Point Mobile */ +	TCO_PBG,	/* Patsburg */  	TCO_DH89XXCC,	/* DH89xxCC */ -	TCO_PPT0,	/* Panther Point */ -	TCO_PPT1,	/* Panther Point */ -	TCO_PPT2,	/* Panther Point */ -	TCO_PPT3,	/* Panther Point */ -	TCO_PPT4,	/* Panther Point */ -	TCO_PPT5,	/* Panther Point */ -	TCO_PPT6,	/* Panther Point */ -	TCO_PPT7,	/* Panther Point */ -	TCO_PPT8,	/* Panther Point */ -	TCO_PPT9,	/* Panther Point */ -	TCO_PPT10,	/* Panther Point */ -	TCO_PPT11,	/* Panther Point */ -	TCO_PPT12,	/* Panther Point */ -	TCO_PPT13,	/* Panther Point */ -	TCO_PPT14,	/* Panther Point */ -	TCO_PPT15,	/* Panther Point */ -	TCO_PPT16,	/* Panther Point */ -	TCO_PPT17,	/* Panther Point */ -	TCO_PPT18,	/* Panther Point */ -	TCO_PPT19,	/* Panther Point */ -	TCO_PPT20,	/* Panther Point */ -	TCO_PPT21,	/* Panther Point */ -	TCO_PPT22,	/* Panther Point */ -	TCO_PPT23,	/* Panther Point */ -	TCO_PPT24,	/* Panther Point */ -	TCO_PPT25,	/* Panther Point */ -	TCO_PPT26,	/* Panther Point */ -	TCO_PPT27,	/* Panther Point */ -	TCO_PPT28,	/* Panther Point */ -	TCO_PPT29,	/* Panther Point */ -	TCO_PPT30,	/* Panther Point */ -	TCO_PPT31,	/* Panther Point */ +	TCO_PPT,	/* Panther Point */  };  static struct { @@ -244,83 +184,14 @@ static struct {  	{"3450", 2},  	{"EP80579", 2},  	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Cougar Point", 2}, -	{"Patsburg", 2}, +	{"Cougar Point Desktop", 2}, +	{"Cougar Point Mobile", 2},  	{"Patsburg", 2},  	{"DH89xxCC", 2},  	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2}, -	{"Panther Point", 2},  	{NULL, 0}  }; -#define ITCO_PCI_DEVICE(dev, data) \ -	.vendor = PCI_VENDOR_ID_INTEL,	\ -	.device = dev,			\ -	.subvendor = PCI_ANY_ID,	\ -	.subdevice = PCI_ANY_ID,	\ -	.class = 0,			\ -	.class_mask = 0,		\ -	.driver_data = data -  /*   * This data only exists for exporting the supported PCI ids   * via MODULE_DEVICE_TABLE.  We do not actually register a @@ -328,138 +199,138 @@ static struct {   * functions that probably will be registered by other drivers.   */  static DEFINE_PCI_DEVICE_TABLE(iTCO_wdt_pci_tbl) = { -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AA_0,	TCO_ICH)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801AB_0,	TCO_ICH0)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_0,	TCO_ICH2)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801BA_10,	TCO_ICH2M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_0,	TCO_ICH3)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801CA_12,	TCO_ICH3M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_0,	TCO_ICH4)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801DB_12,	TCO_ICH4M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801E_0,		TCO_CICH)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_82801EB_0,	TCO_ICH5)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB_1,		TCO_6300ESB)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_0,		TCO_ICH6)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_1,		TCO_ICH6M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH6_2,		TCO_ICH6W)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ESB2_0,		TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2671,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2672,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2673,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2674,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2675,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2676,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2677,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2678,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x2679,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267a,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267b,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267c,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267d,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267e,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(0x267f,				TCO_631XESB)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_0,		TCO_ICH7)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_30,		TCO_ICH7DH)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_1,		TCO_ICH7M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH7_31,		TCO_ICH7MDH)}, -	{ ITCO_PCI_DEVICE(0x27bc,				TCO_NM10)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_0,		TCO_ICH8)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_2,		TCO_ICH8DH)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_3,		TCO_ICH8DO)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_4,		TCO_ICH8M)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH8_1,		TCO_ICH8ME)}, -	{ ITCO_PCI_DEVICE(0x2918,				TCO_ICH9)}, -	{ ITCO_PCI_DEVICE(0x2916,				TCO_ICH9R)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_2,		TCO_ICH9DH)}, -	{ ITCO_PCI_DEVICE(PCI_DEVICE_ID_INTEL_ICH9_4,		TCO_ICH9DO)}, -	{ ITCO_PCI_DEVICE(0x2919,				TCO_ICH9M)}, -	{ ITCO_PCI_DEVICE(0x2917,				TCO_ICH9ME)}, -	{ ITCO_PCI_DEVICE(0x3a18,				TCO_ICH10)}, -	{ ITCO_PCI_DEVICE(0x3a16,				TCO_ICH10R)}, -	{ ITCO_PCI_DEVICE(0x3a1a,				TCO_ICH10D)}, -	{ ITCO_PCI_DEVICE(0x3a14,				TCO_ICH10DO)}, -	{ ITCO_PCI_DEVICE(0x3b00,				TCO_PCH)}, -	{ ITCO_PCI_DEVICE(0x3b01,				TCO_PCHM)}, -	{ ITCO_PCI_DEVICE(0x3b02,				TCO_P55)}, -	{ ITCO_PCI_DEVICE(0x3b03,				TCO_PM55)}, -	{ ITCO_PCI_DEVICE(0x3b06,				TCO_H55)}, -	{ ITCO_PCI_DEVICE(0x3b07,				TCO_QM57)}, -	{ ITCO_PCI_DEVICE(0x3b08,				TCO_H57)}, -	{ ITCO_PCI_DEVICE(0x3b09,				TCO_HM55)}, -	{ ITCO_PCI_DEVICE(0x3b0a,				TCO_Q57)}, -	{ ITCO_PCI_DEVICE(0x3b0b,				TCO_HM57)}, -	{ ITCO_PCI_DEVICE(0x3b0d,				TCO_PCHMSFF)}, -	{ ITCO_PCI_DEVICE(0x3b0f,				TCO_QS57)}, -	{ ITCO_PCI_DEVICE(0x3b12,				TCO_3400)}, -	{ ITCO_PCI_DEVICE(0x3b14,				TCO_3420)}, -	{ ITCO_PCI_DEVICE(0x3b16,				TCO_3450)}, -	{ ITCO_PCI_DEVICE(0x5031,				TCO_EP80579)}, -	{ ITCO_PCI_DEVICE(0x1c41,				TCO_CPT1)}, -	{ ITCO_PCI_DEVICE(0x1c42,				TCO_CPT2)}, -	{ ITCO_PCI_DEVICE(0x1c43,				TCO_CPT3)}, -	{ ITCO_PCI_DEVICE(0x1c44,				TCO_CPT4)}, -	{ ITCO_PCI_DEVICE(0x1c45,				TCO_CPT5)}, -	{ ITCO_PCI_DEVICE(0x1c46,				TCO_CPT6)}, -	{ ITCO_PCI_DEVICE(0x1c47,				TCO_CPT7)}, -	{ ITCO_PCI_DEVICE(0x1c48,				TCO_CPT8)}, -	{ ITCO_PCI_DEVICE(0x1c49,				TCO_CPT9)}, -	{ ITCO_PCI_DEVICE(0x1c4a,				TCO_CPT10)}, -	{ ITCO_PCI_DEVICE(0x1c4b,				TCO_CPT11)}, -	{ ITCO_PCI_DEVICE(0x1c4c,				TCO_CPT12)}, -	{ ITCO_PCI_DEVICE(0x1c4d,				TCO_CPT13)}, -	{ ITCO_PCI_DEVICE(0x1c4e,				TCO_CPT14)}, -	{ ITCO_PCI_DEVICE(0x1c4f,				TCO_CPT15)}, -	{ ITCO_PCI_DEVICE(0x1c50,				TCO_CPT16)}, -	{ ITCO_PCI_DEVICE(0x1c51,				TCO_CPT17)}, -	{ ITCO_PCI_DEVICE(0x1c52,				TCO_CPT18)}, -	{ ITCO_PCI_DEVICE(0x1c53,				TCO_CPT19)}, -	{ ITCO_PCI_DEVICE(0x1c54,				TCO_CPT20)}, -	{ ITCO_PCI_DEVICE(0x1c55,				TCO_CPT21)}, -	{ ITCO_PCI_DEVICE(0x1c56,				TCO_CPT22)}, -	{ ITCO_PCI_DEVICE(0x1c57,				TCO_CPT23)}, -	{ ITCO_PCI_DEVICE(0x1c58,				TCO_CPT24)}, -	{ ITCO_PCI_DEVICE(0x1c59,				TCO_CPT25)}, -	{ ITCO_PCI_DEVICE(0x1c5a,				TCO_CPT26)}, -	{ ITCO_PCI_DEVICE(0x1c5b,				TCO_CPT27)}, -	{ ITCO_PCI_DEVICE(0x1c5c,				TCO_CPT28)}, -	{ ITCO_PCI_DEVICE(0x1c5d,				TCO_CPT29)}, -	{ ITCO_PCI_DEVICE(0x1c5e,				TCO_CPT30)}, -	{ ITCO_PCI_DEVICE(0x1c5f,				TCO_CPT31)}, -	{ ITCO_PCI_DEVICE(0x1d40,				TCO_PBG1)}, -	{ ITCO_PCI_DEVICE(0x1d41,				TCO_PBG2)}, -	{ ITCO_PCI_DEVICE(0x2310,				TCO_DH89XXCC)}, -	{ ITCO_PCI_DEVICE(0x1e40,				TCO_PPT0)}, -	{ ITCO_PCI_DEVICE(0x1e41,				TCO_PPT1)}, -	{ ITCO_PCI_DEVICE(0x1e42,				TCO_PPT2)}, -	{ ITCO_PCI_DEVICE(0x1e43,				TCO_PPT3)}, -	{ ITCO_PCI_DEVICE(0x1e44,				TCO_PPT4)}, -	{ ITCO_PCI_DEVICE(0x1e45,				TCO_PPT5)}, -	{ ITCO_PCI_DEVICE(0x1e46,				TCO_PPT6)}, -	{ ITCO_PCI_DEVICE(0x1e47,				TCO_PPT7)}, -	{ ITCO_PCI_DEVICE(0x1e48,				TCO_PPT8)}, -	{ ITCO_PCI_DEVICE(0x1e49,				TCO_PPT9)}, -	{ ITCO_PCI_DEVICE(0x1e4a,				TCO_PPT10)}, -	{ ITCO_PCI_DEVICE(0x1e4b,				TCO_PPT11)}, -	{ ITCO_PCI_DEVICE(0x1e4c,				TCO_PPT12)}, -	{ ITCO_PCI_DEVICE(0x1e4d,				TCO_PPT13)}, -	{ ITCO_PCI_DEVICE(0x1e4e,				TCO_PPT14)}, -	{ ITCO_PCI_DEVICE(0x1e4f,				TCO_PPT15)}, -	{ ITCO_PCI_DEVICE(0x1e50,				TCO_PPT16)}, -	{ ITCO_PCI_DEVICE(0x1e51,				TCO_PPT17)}, -	{ ITCO_PCI_DEVICE(0x1e52,				TCO_PPT18)}, -	{ ITCO_PCI_DEVICE(0x1e53,				TCO_PPT19)}, -	{ ITCO_PCI_DEVICE(0x1e54,				TCO_PPT20)}, -	{ ITCO_PCI_DEVICE(0x1e55,				TCO_PPT21)}, -	{ ITCO_PCI_DEVICE(0x1e56,				TCO_PPT22)}, -	{ ITCO_PCI_DEVICE(0x1e57,				TCO_PPT23)}, -	{ ITCO_PCI_DEVICE(0x1e58,				TCO_PPT24)}, -	{ ITCO_PCI_DEVICE(0x1e59,				TCO_PPT25)}, -	{ ITCO_PCI_DEVICE(0x1e5a,				TCO_PPT26)}, -	{ ITCO_PCI_DEVICE(0x1e5b,				TCO_PPT27)}, -	{ ITCO_PCI_DEVICE(0x1e5c,				TCO_PPT28)}, -	{ ITCO_PCI_DEVICE(0x1e5d,				TCO_PPT29)}, -	{ ITCO_PCI_DEVICE(0x1e5e,				TCO_PPT30)}, -	{ ITCO_PCI_DEVICE(0x1e5f,				TCO_PPT31)}, +	{ PCI_VDEVICE(INTEL, 0x2410), TCO_ICH}, +	{ PCI_VDEVICE(INTEL, 0x2420), TCO_ICH0}, +	{ PCI_VDEVICE(INTEL, 0x2440), TCO_ICH2}, +	{ PCI_VDEVICE(INTEL, 0x244c), TCO_ICH2M}, +	{ PCI_VDEVICE(INTEL, 0x2480), TCO_ICH3}, +	{ PCI_VDEVICE(INTEL, 0x248c), TCO_ICH3M}, +	{ PCI_VDEVICE(INTEL, 0x24c0), TCO_ICH4}, +	{ PCI_VDEVICE(INTEL, 0x24cc), TCO_ICH4M}, +	{ PCI_VDEVICE(INTEL, 0x2450), TCO_CICH}, +	{ PCI_VDEVICE(INTEL, 0x24d0), TCO_ICH5}, +	{ PCI_VDEVICE(INTEL, 0x25a1), TCO_6300ESB}, +	{ PCI_VDEVICE(INTEL, 0x2640), TCO_ICH6}, +	{ PCI_VDEVICE(INTEL, 0x2641), TCO_ICH6M}, +	{ PCI_VDEVICE(INTEL, 0x2642), TCO_ICH6W}, +	{ PCI_VDEVICE(INTEL, 0x2670), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2671), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2672), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2673), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2674), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2675), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2676), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2677), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2678), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x2679), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267a), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267b), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267c), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267d), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267e), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x267f), TCO_631XESB}, +	{ PCI_VDEVICE(INTEL, 0x27b8), TCO_ICH7}, +	{ PCI_VDEVICE(INTEL, 0x27b0), TCO_ICH7DH}, +	{ PCI_VDEVICE(INTEL, 0x27b9), TCO_ICH7M}, +	{ PCI_VDEVICE(INTEL, 0x27bd), TCO_ICH7MDH}, +	{ PCI_VDEVICE(INTEL, 0x27bc), TCO_NM10}, +	{ PCI_VDEVICE(INTEL, 0x2810), TCO_ICH8}, +	{ PCI_VDEVICE(INTEL, 0x2812), TCO_ICH8DH}, +	{ PCI_VDEVICE(INTEL, 0x2814), TCO_ICH8DO}, +	{ PCI_VDEVICE(INTEL, 0x2815), TCO_ICH8M}, +	{ PCI_VDEVICE(INTEL, 0x2811), TCO_ICH8ME}, +	{ PCI_VDEVICE(INTEL, 0x2918), TCO_ICH9}, +	{ PCI_VDEVICE(INTEL, 0x2916), TCO_ICH9R}, +	{ PCI_VDEVICE(INTEL, 0x2912), TCO_ICH9DH}, +	{ PCI_VDEVICE(INTEL, 0x2914), TCO_ICH9DO}, +	{ PCI_VDEVICE(INTEL, 0x2919), TCO_ICH9M}, +	{ PCI_VDEVICE(INTEL, 0x2917), TCO_ICH9ME}, +	{ PCI_VDEVICE(INTEL, 0x3a18), TCO_ICH10}, +	{ PCI_VDEVICE(INTEL, 0x3a16), TCO_ICH10R}, +	{ PCI_VDEVICE(INTEL, 0x3a1a), TCO_ICH10D}, +	{ PCI_VDEVICE(INTEL, 0x3a14), TCO_ICH10DO}, +	{ PCI_VDEVICE(INTEL, 0x3b00), TCO_PCH}, +	{ PCI_VDEVICE(INTEL, 0x3b01), TCO_PCHM}, +	{ PCI_VDEVICE(INTEL, 0x3b02), TCO_P55}, +	{ PCI_VDEVICE(INTEL, 0x3b03), TCO_PM55}, +	{ PCI_VDEVICE(INTEL, 0x3b06), TCO_H55}, +	{ PCI_VDEVICE(INTEL, 0x3b07), TCO_QM57}, +	{ PCI_VDEVICE(INTEL, 0x3b08), TCO_H57}, +	{ PCI_VDEVICE(INTEL, 0x3b09), TCO_HM55}, +	{ PCI_VDEVICE(INTEL, 0x3b0a), TCO_Q57}, +	{ PCI_VDEVICE(INTEL, 0x3b0b), TCO_HM57}, +	{ PCI_VDEVICE(INTEL, 0x3b0d), TCO_PCHMSFF}, +	{ PCI_VDEVICE(INTEL, 0x3b0f), TCO_QS57}, +	{ PCI_VDEVICE(INTEL, 0x3b12), TCO_3400}, +	{ PCI_VDEVICE(INTEL, 0x3b14), TCO_3420}, +	{ PCI_VDEVICE(INTEL, 0x3b16), TCO_3450}, +	{ PCI_VDEVICE(INTEL, 0x5031), TCO_EP80579}, +	{ PCI_VDEVICE(INTEL, 0x1c41), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c42), TCO_CPTD}, +	{ PCI_VDEVICE(INTEL, 0x1c43), TCO_CPTM}, +	{ PCI_VDEVICE(INTEL, 0x1c44), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c45), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c46), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c47), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c48), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c49), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4a), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4b), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4c), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4d), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4e), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c4f), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c50), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c51), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c52), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c53), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c54), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c55), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c56), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c57), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c58), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c59), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5a), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5b), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5c), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5d), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5e), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1c5f), TCO_CPT}, +	{ PCI_VDEVICE(INTEL, 0x1d40), TCO_PBG}, +	{ PCI_VDEVICE(INTEL, 0x1d41), TCO_PBG}, +	{ PCI_VDEVICE(INTEL, 0x2310), TCO_DH89XXCC}, +	{ PCI_VDEVICE(INTEL, 0x1e40), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e41), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e42), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e43), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e44), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e45), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e46), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e47), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e48), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e49), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4a), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4b), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4c), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4d), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4e), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e4f), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e50), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e51), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e52), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e53), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e54), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e55), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e56), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e57), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e58), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e59), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5a), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5b), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5c), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5d), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5e), TCO_PPT}, +	{ PCI_VDEVICE(INTEL, 0x1e5f), TCO_PPT},  	{ 0, },			/* End of list */  };  MODULE_DEVICE_TABLE(pci, iTCO_wdt_pci_tbl); @@ -1052,15 +923,10 @@ static void iTCO_wdt_shutdown(struct platform_device *dev)  	iTCO_wdt_stop();  } -#define iTCO_wdt_suspend NULL -#define iTCO_wdt_resume  NULL -  static struct platform_driver iTCO_wdt_driver = {  	.probe          = iTCO_wdt_probe,  	.remove         = __devexit_p(iTCO_wdt_remove),  	.shutdown       = iTCO_wdt_shutdown, -	.suspend        = iTCO_wdt_suspend, -	.resume         = iTCO_wdt_resume,  	.driver         = {  		.owner  = THIS_MODULE,  		.name   = DRV_NAME, diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 86f7cac1026c..b8ef2c6dca7c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev)  	}  } +static const struct of_device_id imx2_wdt_dt_ids[] = { +	{ .compatible = "fsl,imx21-wdt", }, +	{ /* sentinel */ } +}; +  static struct platform_driver imx2_wdt_driver = {  	.remove		= __exit_p(imx2_wdt_remove),  	.shutdown	= imx2_wdt_shutdown,  	.driver		= {  		.name	= DRIVER_NAME,  		.owner	= THIS_MODULE, +		.of_match_table = imx2_wdt_dt_ids,  	},  }; diff --git a/drivers/watchdog/intel_scu_watchdog.c b/drivers/watchdog/intel_scu_watchdog.c index 919bdd16136f..1abdc0454c54 100644 --- a/drivers/watchdog/intel_scu_watchdog.c +++ b/drivers/watchdog/intel_scu_watchdog.c @@ -42,9 +42,8 @@  #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 <linux/atomic.h>  #include <asm/intel_scu_ipc.h>  #include <asm/apb_timer.h>  #include <asm/mrst.h> diff --git a/drivers/watchdog/it8712f_wdt.c b/drivers/watchdog/it8712f_wdt.c index 6143f52ba6b8..8d2d8502d3e8 100644 --- a/drivers/watchdog/it8712f_wdt.c +++ b/drivers/watchdog/it8712f_wdt.c @@ -28,10 +28,10 @@  #include <linux/notifier.h>  #include <linux/reboot.h>  #include <linux/fs.h> -#include <linux/pci.h>  #include <linux/spinlock.h>  #include <linux/uaccess.h>  #include <linux/io.h> +#include <linux/ioport.h>  #define NAME "it8712f_wdt" @@ -51,7 +51,6 @@ MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");  static unsigned long wdt_open;  static unsigned expect_close; -static spinlock_t io_lock;  static unsigned char revision;  /* Dog Food address - We use the game port address */ @@ -121,20 +120,26 @@ static inline void superio_select(int ldn)  	outb(ldn, VAL);  } -static inline void superio_enter(void) +static inline int superio_enter(void)  { -	spin_lock(&io_lock); +	/* +	 * Try to reserve REG and REG + 1 for exclusive access. +	 */ +	if (!request_muxed_region(REG, 2, NAME)) +		return -EBUSY; +  	outb(0x87, REG);  	outb(0x01, REG);  	outb(0x55, REG);  	outb(0x55, REG); +	return 0;  }  static inline void superio_exit(void)  {  	outb(0x02, REG);  	outb(0x02, VAL); -	spin_unlock(&io_lock); +	release_region(REG, 2);  }  static inline void it8712f_wdt_ping(void) @@ -173,10 +178,13 @@ static int it8712f_wdt_get_status(void)  		return 0;  } -static void it8712f_wdt_enable(void) +static int it8712f_wdt_enable(void)  { +	int ret = superio_enter(); +	if (ret) +		return ret; +  	printk(KERN_DEBUG NAME ": enabling watchdog timer\n"); -	superio_enter();  	superio_select(LDN_GPIO);  	superio_outb(wdt_control_reg, WDT_CONTROL); @@ -186,13 +194,17 @@ static void it8712f_wdt_enable(void)  	superio_exit();  	it8712f_wdt_ping(); + +	return 0;  } -static void it8712f_wdt_disable(void) +static int it8712f_wdt_disable(void)  { -	printk(KERN_DEBUG NAME ": disabling watchdog timer\n"); +	int ret = superio_enter(); +	if (ret) +		return ret; -	superio_enter(); +	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");  	superio_select(LDN_GPIO);  	superio_outb(0, WDT_CONFIG); @@ -202,6 +214,7 @@ static void it8712f_wdt_disable(void)  	superio_outb(0, WDT_TIMEOUT);  	superio_exit(); +	return 0;  }  static int it8712f_wdt_notify(struct notifier_block *this, @@ -252,6 +265,7 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,  						WDIOF_MAGICCLOSE,  	};  	int value; +	int ret;  	switch (cmd) {  	case WDIOC_GETSUPPORT: @@ -259,7 +273,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,  			return -EFAULT;  		return 0;  	case WDIOC_GETSTATUS: -		superio_enter(); +		ret = superio_enter(); +		if (ret) +			return ret;  		superio_select(LDN_GPIO);  		value = it8712f_wdt_get_status(); @@ -280,7 +296,9 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,  		if (value > (max_units * 60))  			return -EINVAL;  		margin = value; -		superio_enter(); +		ret = superio_enter(); +		if (ret) +			return ret;  		superio_select(LDN_GPIO);  		it8712f_wdt_update_margin(); @@ -299,10 +317,14 @@ static long it8712f_wdt_ioctl(struct file *file, unsigned int cmd,  static int it8712f_wdt_open(struct inode *inode, struct file *file)  { +	int ret;  	/* only allow one at a time */  	if (test_and_set_bit(0, &wdt_open))  		return -EBUSY; -	it8712f_wdt_enable(); + +	ret = it8712f_wdt_enable(); +	if (ret) +		return ret;  	return nonseekable_open(inode, file);  } @@ -313,7 +335,8 @@ static int it8712f_wdt_release(struct inode *inode, struct file *file)  			": watchdog device closed unexpectedly, will not"  			" disable the watchdog timer\n");  	} else if (!nowayout) { -		it8712f_wdt_disable(); +		if (it8712f_wdt_disable()) +			printk(KERN_WARNING NAME "Watchdog disable failed\n");  	}  	expect_close = 0;  	clear_bit(0, &wdt_open); @@ -340,8 +363,10 @@ static int __init it8712f_wdt_find(unsigned short *address)  {  	int err = -ENODEV;  	int chip_type; +	int ret = superio_enter(); +	if (ret) +		return ret; -	superio_enter();  	chip_type = superio_inw(DEVID);  	if (chip_type != IT8712F_DEVID)  		goto exit; @@ -382,8 +407,6 @@ static int __init it8712f_wdt_init(void)  {  	int err = 0; -	spin_lock_init(&io_lock); -  	if (it8712f_wdt_find(&address))  		return -ENODEV; @@ -392,7 +415,11 @@ static int __init it8712f_wdt_init(void)  		return -EBUSY;  	} -	it8712f_wdt_disable(); +	err = it8712f_wdt_disable(); +	if (err) { +		printk(KERN_ERR NAME ": unable to disable watchdog timer.\n"); +		goto out; +	}  	err = register_reboot_notifier(&it8712f_wdt_notifier);  	if (err) { diff --git a/drivers/watchdog/it87_wdt.c b/drivers/watchdog/it87_wdt.c index b1bc72f9a209..a2d9a1266a23 100644 --- a/drivers/watchdog/it87_wdt.c +++ b/drivers/watchdog/it87_wdt.c @@ -137,7 +137,6 @@  static	unsigned int base, gpact, ciract, max_units, chip_type;  static	unsigned long wdt_status; -static	DEFINE_SPINLOCK(spinlock);  static	int nogameport = DEFAULT_NOGAMEPORT;  static	int exclusive  = DEFAULT_EXCLUSIVE; @@ -163,18 +162,26 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started, default="  /* Superio Chip */ -static inline void superio_enter(void) +static inline int superio_enter(void)  { +	/* +	 * Try to reserve REG and REG + 1 for exclusive access. +	 */ +	if (!request_muxed_region(REG, 2, WATCHDOG_NAME)) +		return -EBUSY; +  	outb(0x87, REG);  	outb(0x01, REG);  	outb(0x55, REG);  	outb(0x55, REG); +	return 0;  }  static inline void superio_exit(void)  {  	outb(0x02, REG);  	outb(0x02, VAL); +	release_region(REG, 2);  }  static inline void superio_select(int ldn) @@ -255,12 +262,11 @@ static void wdt_keepalive(void)  	set_bit(WDTS_KEEPALIVE, &wdt_status);  } -static void wdt_start(void) +static int wdt_start(void)  { -	unsigned long flags; - -	spin_lock_irqsave(&spinlock, flags); -	superio_enter(); +	int ret = superio_enter(); +	if (ret) +		return ret;  	superio_select(GPIO);  	if (test_bit(WDTS_USE_GP, &wdt_status)) @@ -270,15 +276,15 @@ static void wdt_start(void)  	wdt_update_timeout();  	superio_exit(); -	spin_unlock_irqrestore(&spinlock, flags); + +	return 0;  } -static void wdt_stop(void) +static int wdt_stop(void)  { -	unsigned long flags; - -	spin_lock_irqsave(&spinlock, flags); -	superio_enter(); +	int ret = superio_enter(); +	if (ret) +		return ret;  	superio_select(GPIO);  	superio_outb(0x00, WDTCTRL); @@ -288,7 +294,7 @@ static void wdt_stop(void)  		superio_outb(0x00, WDTVALMSB);  	superio_exit(); -	spin_unlock_irqrestore(&spinlock, flags); +	return 0;  }  /** @@ -303,8 +309,6 @@ static void wdt_stop(void)  static int wdt_set_timeout(int t)  { -	unsigned long flags; -  	if (t < 1 || t > max_units * 60)  		return -EINVAL; @@ -313,14 +317,15 @@ static int wdt_set_timeout(int t)  	else  		timeout = t; -	spin_lock_irqsave(&spinlock, flags);  	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { -		superio_enter(); +		int ret = superio_enter(); +		if (ret) +			return ret; +  		superio_select(GPIO);  		wdt_update_timeout();  		superio_exit();  	} -	spin_unlock_irqrestore(&spinlock, flags);  	return 0;  } @@ -339,12 +344,12 @@ static int wdt_set_timeout(int t)  static int wdt_get_status(int *status)  { -	unsigned long flags; -  	*status = 0;  	if (testmode) { -		spin_lock_irqsave(&spinlock, flags); -		superio_enter(); +		int ret = superio_enter(); +		if (ret) +			return ret; +  		superio_select(GPIO);  		if (superio_inb(WDTCTRL) & WDT_ZERO) {  			superio_outb(0x00, WDTCTRL); @@ -353,7 +358,6 @@ static int wdt_get_status(int *status)  		}  		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags);  	}  	if (test_and_clear_bit(WDTS_KEEPALIVE, &wdt_status))  		*status |= WDIOF_KEEPALIVEPING; @@ -379,9 +383,17 @@ static int wdt_open(struct inode *inode, struct file *file)  	if (exclusive && test_and_set_bit(WDTS_DEV_OPEN, &wdt_status))  		return -EBUSY;  	if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { +		int ret;  		if (nowayout && !test_and_set_bit(WDTS_LOCKED, &wdt_status))  			__module_get(THIS_MODULE); -		wdt_start(); + +		ret = wdt_start(); +		if (ret) { +			clear_bit(WDTS_LOCKED, &wdt_status); +			clear_bit(WDTS_TIMER_RUN, &wdt_status); +			clear_bit(WDTS_DEV_OPEN, &wdt_status); +			return ret; +		}  	}  	return nonseekable_open(inode, file);  } @@ -403,7 +415,16 @@ static int wdt_release(struct inode *inode, struct file *file)  {  	if (test_bit(WDTS_TIMER_RUN, &wdt_status)) {  		if (test_and_clear_bit(WDTS_EXPECTED, &wdt_status)) { -			wdt_stop(); +			int ret = wdt_stop(); +			if (ret) { +				/* +				 * Stop failed. Just keep the watchdog alive +				 * and hope nothing bad happens. +				 */ +				set_bit(WDTS_EXPECTED, &wdt_status); +				wdt_keepalive(); +				return ret; +			}  			clear_bit(WDTS_TIMER_RUN, &wdt_status);  		} else {  			wdt_keepalive(); @@ -484,7 +505,9 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  				    &ident, sizeof(ident)) ? -EFAULT : 0;  	case WDIOC_GETSTATUS: -		wdt_get_status(&status); +		rc = wdt_get_status(&status); +		if (rc) +			return rc;  		return put_user(status, uarg.i);  	case WDIOC_GETBOOTSTATUS: @@ -500,14 +523,22 @@ static long wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  		switch (new_options) {  		case WDIOS_DISABLECARD: -			if (test_bit(WDTS_TIMER_RUN, &wdt_status)) -				wdt_stop(); +			if (test_bit(WDTS_TIMER_RUN, &wdt_status)) { +				rc = wdt_stop(); +				if (rc) +					return rc; +			}  			clear_bit(WDTS_TIMER_RUN, &wdt_status);  			return 0;  		case WDIOS_ENABLECARD: -			if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) -				wdt_start(); +			if (!test_and_set_bit(WDTS_TIMER_RUN, &wdt_status)) { +				rc = wdt_start(); +				if (rc) { +					clear_bit(WDTS_TIMER_RUN, &wdt_status); +					return rc; +				} +			}  			return 0;  		default: @@ -560,16 +591,17 @@ static int __init it87_wdt_init(void)  	int rc = 0;  	int try_gameport = !nogameport;  	u8  chip_rev; -	unsigned long flags; +	int gp_rreq_fail = 0;  	wdt_status = 0; -	spin_lock_irqsave(&spinlock, flags); -	superio_enter(); +	rc = superio_enter(); +	if (rc) +		return rc; +  	chip_type = superio_inw(CHIPID);  	chip_rev  = superio_inb(CHIPREV) & 0x0f;  	superio_exit(); -	spin_unlock_irqrestore(&spinlock, flags);  	switch (chip_type) {  	case IT8702_ID: @@ -603,8 +635,9 @@ static int __init it87_wdt_init(void)  		return -ENODEV;  	} -	spin_lock_irqsave(&spinlock, flags); -	superio_enter(); +	rc = superio_enter(); +	if (rc) +		return rc;  	superio_select(GPIO);  	superio_outb(WDT_TOV1, WDTCFG); @@ -620,21 +653,16 @@ static int __init it87_wdt_init(void)  		}  		gpact = superio_inb(ACTREG);  		superio_outb(0x01, ACTREG); -		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags);  		if (request_region(base, 1, WATCHDOG_NAME))  			set_bit(WDTS_USE_GP, &wdt_status);  		else -			rc = -EIO; -	} else { -		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags); +			gp_rreq_fail = 1;  	}  	/* If we haven't Gameport support, try to get CIR support */  	if (!test_bit(WDTS_USE_GP, &wdt_status)) {  		if (!request_region(CIR_BASE, 8, WATCHDOG_NAME)) { -			if (rc == -EIO) +			if (gp_rreq_fail)  				printk(KERN_ERR PFX  					"I/O Address 0x%04x and 0x%04x"  					" already in use\n", base, CIR_BASE); @@ -646,21 +674,16 @@ static int __init it87_wdt_init(void)  			goto err_out;  		}  		base = CIR_BASE; -		spin_lock_irqsave(&spinlock, flags); -		superio_enter();  		superio_select(CIR);  		superio_outw(base, BASEREG);  		superio_outb(0x00, CIR_ILS);  		ciract = superio_inb(ACTREG);  		superio_outb(0x01, ACTREG); -		if (rc == -EIO) { +		if (gp_rreq_fail) {  			superio_select(GAMEPORT);  			superio_outb(gpact, ACTREG);  		} - -		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags);  	}  	if (timeout < 1 || timeout > max_units * 60) { @@ -704,6 +727,7 @@ static int __init it87_wdt_init(void)  		"nogameport=%d)\n", chip_type, chip_rev, timeout,  		nowayout, testmode, exclusive, nogameport); +	superio_exit();  	return 0;  err_out_reboot: @@ -711,49 +735,37 @@ err_out_reboot:  err_out_region:  	release_region(base, test_bit(WDTS_USE_GP, &wdt_status) ? 1 : 8);  	if (!test_bit(WDTS_USE_GP, &wdt_status)) { -		spin_lock_irqsave(&spinlock, flags); -		superio_enter();  		superio_select(CIR);  		superio_outb(ciract, ACTREG); -		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags);  	}  err_out:  	if (try_gameport) { -		spin_lock_irqsave(&spinlock, flags); -		superio_enter();  		superio_select(GAMEPORT);  		superio_outb(gpact, ACTREG); -		superio_exit(); -		spin_unlock_irqrestore(&spinlock, flags);  	} +	superio_exit();  	return rc;  }  static void __exit it87_wdt_exit(void)  { -	unsigned long flags; -	int nolock; - -	nolock = !spin_trylock_irqsave(&spinlock, flags); -	superio_enter(); -	superio_select(GPIO); -	superio_outb(0x00, WDTCTRL); -	superio_outb(0x00, WDTCFG); -	superio_outb(0x00, WDTVALLSB); -	if (max_units > 255) -		superio_outb(0x00, WDTVALMSB); -	if (test_bit(WDTS_USE_GP, &wdt_status)) { -		superio_select(GAMEPORT); -		superio_outb(gpact, ACTREG); -	} else { -		superio_select(CIR); -		superio_outb(ciract, ACTREG); +	if (superio_enter() == 0) { +		superio_select(GPIO); +		superio_outb(0x00, WDTCTRL); +		superio_outb(0x00, WDTCFG); +		superio_outb(0x00, WDTVALLSB); +		if (max_units > 255) +			superio_outb(0x00, WDTVALMSB); +		if (test_bit(WDTS_USE_GP, &wdt_status)) { +			superio_select(GAMEPORT); +			superio_outb(gpact, ACTREG); +		} else { +			superio_select(CIR); +			superio_outb(ciract, ACTREG); +		} +		superio_exit();  	} -	superio_exit(); -	if (!nolock) -		spin_unlock_irqrestore(&spinlock, flags);  	misc_deregister(&wdt_miscdev);  	unregister_reboot_notifier(&wdt_notifier); diff --git a/drivers/watchdog/mpcore_wdt.c b/drivers/watchdog/mpcore_wdt.c index 2b4af222b5f2..4dc31024d26c 100644 --- a/drivers/watchdog/mpcore_wdt.c +++ b/drivers/watchdog/mpcore_wdt.c @@ -407,12 +407,35 @@ static int __devexit mpcore_wdt_remove(struct platform_device *dev)  	return 0;  } +#ifdef CONFIG_PM +static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg) +{ +	struct mpcore_wdt *wdt = platform_get_drvdata(dev); +	mpcore_wdt_stop(wdt);		/* Turn the WDT off */ +	return 0; +} + +static int mpcore_wdt_resume(struct platform_device *dev) +{ +	struct mpcore_wdt *wdt = platform_get_drvdata(dev); +	/* re-activate timer */ +	if (test_bit(0, &wdt->timer_alive)) +		mpcore_wdt_start(wdt); +	return 0; +} +#else +#define mpcore_wdt_suspend	NULL +#define mpcore_wdt_resume	NULL +#endif +  /* work with hotplug and coldplug */  MODULE_ALIAS("platform:mpcore_wdt");  static struct platform_driver mpcore_wdt_driver = {  	.probe		= mpcore_wdt_probe,  	.remove		= __devexit_p(mpcore_wdt_remove), +	.suspend	= mpcore_wdt_suspend, +	.resume		= mpcore_wdt_resume,  	.shutdown	= mpcore_wdt_shutdown,  	.driver		= {  		.owner	= THIS_MODULE, diff --git a/drivers/watchdog/mtx-1_wdt.c b/drivers/watchdog/mtx-1_wdt.c index 1479dc4d6129..ac37bb82392c 100644 --- a/drivers/watchdog/mtx-1_wdt.c +++ b/drivers/watchdog/mtx-1_wdt.c @@ -66,23 +66,18 @@ static struct {  	int default_ticks;  	unsigned long inuse;  	unsigned gpio; -	int gstate; +	unsigned int gstate;  } mtx1_wdt_device;  static void mtx1_wdt_trigger(unsigned long unused)  { -	u32 tmp; -  	spin_lock(&mtx1_wdt_device.lock);  	if (mtx1_wdt_device.running)  		ticks--;  	/* toggle wdt gpio */ -	mtx1_wdt_device.gstate = ~mtx1_wdt_device.gstate; -	if (mtx1_wdt_device.gstate) -		gpio_direction_output(mtx1_wdt_device.gpio, 1); -	else -		gpio_direction_input(mtx1_wdt_device.gpio); +	mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate; +	gpio_set_value(mtx1_wdt_device.gpio, mtx1_wdt_device.gstate);  	if (mtx1_wdt_device.queue && ticks)  		mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL); @@ -105,7 +100,7 @@ static void mtx1_wdt_start(void)  	if (!mtx1_wdt_device.queue) {  		mtx1_wdt_device.queue = 1;  		mtx1_wdt_device.gstate = 1; -		gpio_direction_output(mtx1_wdt_device.gpio, 1); +		gpio_set_value(mtx1_wdt_device.gpio, 1);  		mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);  	}  	mtx1_wdt_device.running++; @@ -120,7 +115,7 @@ static int mtx1_wdt_stop(void)  	if (mtx1_wdt_device.queue) {  		mtx1_wdt_device.queue = 0;  		mtx1_wdt_device.gstate = 0; -		gpio_direction_output(mtx1_wdt_device.gpio, 0); +		gpio_set_value(mtx1_wdt_device.gpio, 0);  	}  	ticks = mtx1_wdt_device.default_ticks;  	spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags); @@ -214,6 +209,12 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev)  	int ret;  	mtx1_wdt_device.gpio = pdev->resource[0].start; +	ret = gpio_request_one(mtx1_wdt_device.gpio, +				GPIOF_OUT_INIT_HIGH, "mtx1-wdt"); +	if (ret < 0) { +		dev_err(&pdev->dev, "failed to request gpio"); +		return ret; +	}  	spin_lock_init(&mtx1_wdt_device.lock);  	init_completion(&mtx1_wdt_device.stop); @@ -224,11 +225,11 @@ static int __devinit mtx1_wdt_probe(struct platform_device *pdev)  	ret = misc_register(&mtx1_wdt_misc);  	if (ret < 0) { -		printk(KERN_ERR " mtx-1_wdt : failed to register\n"); +		dev_err(&pdev->dev, "failed to register\n");  		return ret;  	}  	mtx1_wdt_start(); -	printk(KERN_INFO "MTX-1 Watchdog driver\n"); +	dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");  	return 0;  } @@ -239,11 +240,13 @@ static int __devexit mtx1_wdt_remove(struct platform_device *pdev)  		mtx1_wdt_device.queue = 0;  		wait_for_completion(&mtx1_wdt_device.stop);  	} + +	gpio_free(mtx1_wdt_device.gpio);  	misc_deregister(&mtx1_wdt_misc);  	return 0;  } -static struct platform_driver mtx1_wdt = { +static struct platform_driver mtx1_wdt_driver = {  	.probe = mtx1_wdt_probe,  	.remove = __devexit_p(mtx1_wdt_remove),  	.driver.name = "mtx1-wdt", @@ -252,12 +255,12 @@ static struct platform_driver mtx1_wdt = {  static int __init mtx1_wdt_init(void)  { -	return platform_driver_register(&mtx1_wdt); +	return platform_driver_register(&mtx1_wdt_driver);  }  static void __exit mtx1_wdt_exit(void)  { -	platform_driver_unregister(&mtx1_wdt); +	platform_driver_unregister(&mtx1_wdt_driver);  }  module_init(mtx1_wdt_init); diff --git a/drivers/watchdog/nv_tco.c b/drivers/watchdog/nv_tco.c index afa78a54711e..809f41c30c44 100644 --- a/drivers/watchdog/nv_tco.c +++ b/drivers/watchdog/nv_tco.c @@ -458,7 +458,15 @@ static int __devexit nv_tco_remove(struct platform_device *dev)  static void nv_tco_shutdown(struct platform_device *dev)  { +	u32 val; +  	tco_timer_stop(); + +	/* Some BIOSes fail the POST (once) if the NO_REBOOT flag is not +	 * unset during shutdown. */ +	pci_read_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, &val); +	val &= ~MCP51_SMBUS_SETUP_B_TCO_REBOOT; +	pci_write_config_dword(tco_pci, MCP51_SMBUS_SETUP_B, val);  }  static struct platform_driver nv_tco_driver = { diff --git a/drivers/watchdog/of_xilinx_wdt.c b/drivers/watchdog/of_xilinx_wdt.c new file mode 100644 index 000000000000..4ec741ac952c --- /dev/null +++ b/drivers/watchdog/of_xilinx_wdt.c @@ -0,0 +1,433 @@ +/* +*   of_xilinx_wdt.c  1.01  A Watchdog Device Driver for Xilinx xps_timebase_wdt +* +*   (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>) +* +*       ----------------------- +* +*   This program is free software; you can redistribute it and/or +*   modify it under the terms of the GNU General Public License +*   as published by the Free Software Foundation; either version +*   2 of the License, or (at your option) any later version. +* +*       ----------------------- +*	30-May-2011 Alejandro Cabrera <aldaya@gmail.com> +*		- If "xlnx,wdt-enable-once" wasn't found on device tree the +*		  module will use CONFIG_WATCHDOG_NOWAYOUT +*		- If the device tree parameters ("clock-frequency" and +*		  "xlnx,wdt-interval") wasn't found the driver won't +*		  know the wdt reset interval +*/ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/watchdog.h> +#include <linux/io.h> +#include <linux/uaccess.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> + +/* Register offsets for the Wdt device */ +#define XWT_TWCSR0_OFFSET   0x0 /* Control/Status Register0 */ +#define XWT_TWCSR1_OFFSET   0x4 /* Control/Status Register1 */ +#define XWT_TBR_OFFSET      0x8 /* Timebase Register Offset */ + +/* Control/Status Register Masks  */ +#define XWT_CSR0_WRS_MASK   0x00000008 /* Reset status */ +#define XWT_CSR0_WDS_MASK   0x00000004 /* Timer state  */ +#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */ + +/* Control/Status Register 0/1 bits  */ +#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */ + +/* SelfTest constants */ +#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000 +#define XWT_TIMER_FAILED            0xFFFFFFFF + +#define WATCHDOG_NAME     "Xilinx Watchdog" +#define PFX WATCHDOG_NAME ": " + +struct xwdt_device { +	struct resource  res; +	void __iomem *base; +	u32 nowayout; +	u32 wdt_interval; +	u32 boot_status; +}; + +static struct xwdt_device xdev; + +static  u32 timeout; +static  u32 control_status_reg; +static  u8  expect_close; +static  u8  no_timeout; +static unsigned long driver_open; + +static  DEFINE_SPINLOCK(spinlock); + +static void xwdt_start(void) +{ +	spin_lock(&spinlock); + +	/* Clean previous status and enable the watchdog timer */ +	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); +	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); + +	iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK), +				xdev.base + XWT_TWCSR0_OFFSET); + +	iowrite32(XWT_CSRX_EWDT2_MASK, xdev.base + XWT_TWCSR1_OFFSET); + +	spin_unlock(&spinlock); +} + +static void xwdt_stop(void) +{ +	spin_lock(&spinlock); + +	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); + +	iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK), +				xdev.base + XWT_TWCSR0_OFFSET); + +	iowrite32(0, xdev.base + XWT_TWCSR1_OFFSET); + +	spin_unlock(&spinlock); +	printk(KERN_INFO PFX "Stopped!\n"); +} + +static void xwdt_keepalive(void) +{ +	spin_lock(&spinlock); + +	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); +	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK); +	iowrite32(control_status_reg, xdev.base + XWT_TWCSR0_OFFSET); + +	spin_unlock(&spinlock); +} + +static void xwdt_get_status(int *status) +{ +	int new_status; + +	spin_lock(&spinlock); + +	control_status_reg = ioread32(xdev.base + XWT_TWCSR0_OFFSET); +	new_status = ((control_status_reg & +			(XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK)) != 0); +	spin_unlock(&spinlock); + +	*status = 0; +	if (new_status & 1) +		*status |= WDIOF_CARDRESET; +} + +static u32 xwdt_selftest(void) +{ +	int i; +	u32 timer_value1; +	u32 timer_value2; + +	spin_lock(&spinlock); + +	timer_value1 = ioread32(xdev.base + XWT_TBR_OFFSET); +	timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); + +	for (i = 0; +		((i <= XWT_MAX_SELFTEST_LOOP_COUNT) && +			(timer_value2 == timer_value1)); i++) { +		timer_value2 = ioread32(xdev.base + XWT_TBR_OFFSET); +	} + +	spin_unlock(&spinlock); + +	if (timer_value2 != timer_value1) +		return ~XWT_TIMER_FAILED; +	else +		return XWT_TIMER_FAILED; +} + +static int xwdt_open(struct inode *inode, struct file *file) +{ +	/* Only one process can handle the wdt at a time */ +	if (test_and_set_bit(0, &driver_open)) +		return -EBUSY; + +	/* Make sure that the module are always loaded...*/ +	if (xdev.nowayout) +		__module_get(THIS_MODULE); + +	xwdt_start(); +	printk(KERN_INFO PFX "Started...\n"); + +	return nonseekable_open(inode, file); +} + +static int xwdt_release(struct inode *inode, struct file *file) +{ +	if (expect_close == 42) { +		xwdt_stop(); +	} else { +		printk(KERN_CRIT PFX +			"Unexpected close, not stopping watchdog!\n"); +		xwdt_keepalive(); +	} + +	clear_bit(0, &driver_open); +	expect_close = 0; +	return 0; +} + +/* + *      xwdt_write: + *      @file: file handle to the watchdog + *      @buf: buffer to write (unused as data does not matter here + *      @count: count of bytes + *      @ppos: pointer to the position to write. No seeks allowed + * + *      A write to a watchdog device is defined as a keepalive signal. Any + *      write of data will do, as we don't define content meaning. + */ +static ssize_t xwdt_write(struct file *file, const char __user *buf, +						size_t len, loff_t *ppos) +{ +	if (len) { +		if (!xdev.nowayout) { +			size_t i; + +			/* In case it was set long ago */ +			expect_close = 0; + +			for (i = 0; i != len; i++) { +				char c; + +				if (get_user(c, buf + i)) +					return -EFAULT; +				if (c == 'V') +					expect_close = 42; +			} +		} +		xwdt_keepalive(); +	} +	return len; +} + +static const struct watchdog_info ident = { +	.options =  WDIOF_MAGICCLOSE | +		    WDIOF_KEEPALIVEPING, +	.firmware_version =	1, +	.identity =	WATCHDOG_NAME, +}; + +/* + *      xwdt_ioctl: + *      @file: file handle to the device + *      @cmd: watchdog command + *      @arg: argument pointer + * + *      The watchdog API defines a common set of functions for all watchdogs + *      according to their available features. + */ +static long xwdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	int status; + +	union { +		struct watchdog_info __user *ident; +		int __user *i; +	} uarg; + +	uarg.i = (int __user *)arg; + +	switch (cmd) { +	case WDIOC_GETSUPPORT: +		return copy_to_user(uarg.ident, &ident, +					sizeof(ident)) ? -EFAULT : 0; + +	case WDIOC_GETBOOTSTATUS: +		return put_user(xdev.boot_status, uarg.i); + +	case WDIOC_GETSTATUS: +		xwdt_get_status(&status); +		return put_user(status, uarg.i); + +	case WDIOC_KEEPALIVE: +		xwdt_keepalive(); +		return 0; + +	case WDIOC_GETTIMEOUT: +		if (no_timeout) +			return -ENOTTY; +		else +			return put_user(timeout, uarg.i); + +	default: +		return -ENOTTY; +	} +} + +static const struct file_operations xwdt_fops = { +	.owner      = THIS_MODULE, +	.llseek     = no_llseek, +	.write      = xwdt_write, +	.open       = xwdt_open, +	.release    = xwdt_release, +	.unlocked_ioctl = xwdt_ioctl, +}; + +static struct miscdevice xwdt_miscdev = { +	.minor      = WATCHDOG_MINOR, +	.name       = "watchdog", +	.fops       = &xwdt_fops, +}; + +static int __devinit xwdt_probe(struct platform_device *pdev) +{ +	int rc; +	u32 *tmptr; +	u32 *pfreq; + +	no_timeout = 0; + +	pfreq = (u32 *)of_get_property(pdev->dev.of_node->parent, +					"clock-frequency", NULL); + +	if (pfreq == NULL) { +		printk(KERN_WARNING PFX +			"The watchdog clock frequency cannot be obtained!\n"); +		no_timeout = 1; +	} + +	rc = of_address_to_resource(pdev->dev.of_node, 0, &xdev.res); +	if (rc) { +		printk(KERN_WARNING PFX "invalid address!\n"); +		return rc; +	} + +	tmptr = (u32 *)of_get_property(pdev->dev.of_node, +					"xlnx,wdt-interval", NULL); +	if (tmptr == NULL) { +		printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-interval\"" +					" not found in device tree!\n"); +		no_timeout = 1; +	} else { +		xdev.wdt_interval = *tmptr; +	} + +	tmptr = (u32 *)of_get_property(pdev->dev.of_node, +					"xlnx,wdt-enable-once", NULL); +	if (tmptr == NULL) { +		printk(KERN_WARNING PFX "Parameter \"xlnx,wdt-enable-once\"" +					" not found in device tree!\n"); +		xdev.nowayout = WATCHDOG_NOWAYOUT; +	} + +/* + *  Twice of the 2^wdt_interval / freq  because the first wdt overflow is + *  ignored (interrupt), reset is only generated at second wdt overflow + */ +	if (!no_timeout) +		timeout = 2 * ((1<<xdev.wdt_interval) / *pfreq); + +	if (!request_mem_region(xdev.res.start, +			xdev.res.end - xdev.res.start + 1, WATCHDOG_NAME)) { +		rc = -ENXIO; +		printk(KERN_ERR PFX "memory request failure!\n"); +		goto err_out; +	} + +	xdev.base = ioremap(xdev.res.start, xdev.res.end - xdev.res.start + 1); +	if (xdev.base == NULL) { +		rc = -ENOMEM; +		printk(KERN_ERR PFX "ioremap failure!\n"); +		goto release_mem; +	} + +	rc = xwdt_selftest(); +	if (rc == XWT_TIMER_FAILED) { +		printk(KERN_ERR PFX "SelfTest routine error!\n"); +		goto unmap_io; +	} + +	xwdt_get_status(&xdev.boot_status); + +	rc = misc_register(&xwdt_miscdev); +	if (rc) { +		printk(KERN_ERR PFX +			"cannot register miscdev on minor=%d (err=%d)\n", +						xwdt_miscdev.minor, rc); +		goto unmap_io; +	} + +	if (no_timeout) +		printk(KERN_INFO PFX +			"driver loaded (timeout=? sec, nowayout=%d)\n", +						    xdev.nowayout); +	else +		printk(KERN_INFO PFX +			"driver loaded (timeout=%d sec, nowayout=%d)\n", +					timeout, xdev.nowayout); + +	expect_close = 0; +	clear_bit(0, &driver_open); + +	return 0; + +unmap_io: +	iounmap(xdev.base); +release_mem: +	release_mem_region(xdev.res.start, resource_size(&xdev.res)); +err_out: +	return rc; +} + +static int __devexit xwdt_remove(struct platform_device *dev) +{ +	misc_deregister(&xwdt_miscdev); +	iounmap(xdev.base); +	release_mem_region(xdev.res.start, resource_size(&xdev.res)); + +	return 0; +} + +/* Match table for of_platform binding */ +static struct of_device_id __devinitdata xwdt_of_match[] = { +	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, xwdt_of_match); + +static struct platform_driver xwdt_driver = { +	.probe       = xwdt_probe, +	.remove      = __devexit_p(xwdt_remove), +	.driver = { +		.owner = THIS_MODULE, +		.name  = WATCHDOG_NAME, +		.of_match_table = xwdt_of_match, +	}, +}; + +static int __init xwdt_init(void) +{ +	return platform_driver_register(&xwdt_driver); +} + +static void __exit xwdt_exit(void) +{ +	platform_driver_unregister(&xwdt_driver); +} + +module_init(xwdt_init); +module_exit(xwdt_exit); + +MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>"); +MODULE_DESCRIPTION("Xilinx Watchdog driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/pc87413_wdt.c b/drivers/watchdog/pc87413_wdt.c index b7c139051575..e78d89986768 100644 --- a/drivers/watchdog/pc87413_wdt.c +++ b/drivers/watchdog/pc87413_wdt.c @@ -56,6 +56,7 @@  #define IO_DEFAULT	0x2E		/* Address used on Portwell Boards */  static int io = IO_DEFAULT; +static int swc_base_addr = -1;  static int timeout = DEFAULT_TIMEOUT;	/* timeout value */  static unsigned long timer_enabled;	/* is the timer enabled? */ @@ -116,9 +117,8 @@ static inline void pc87413_enable_swc(void)  /* Read SWC I/O base address */ -static inline unsigned int pc87413_get_swc_base(void) +static void pc87413_get_swc_base_addr(void)  { -	unsigned int  swc_base_addr = 0;  	unsigned char addr_l, addr_h = 0;  	/* Step 3: Read SWC I/O Base Address */ @@ -136,12 +136,11 @@ static inline unsigned int pc87413_get_swc_base(void)  		"Read SWC I/O Base Address: low %d, high %d, res %d\n",  						addr_l, addr_h, swc_base_addr);  #endif -	return swc_base_addr;  }  /* Select Bank 3 of SWC */ -static inline void pc87413_swc_bank3(unsigned int swc_base_addr) +static inline void pc87413_swc_bank3(void)  {  	/* Step 4: Select Bank3 of SWC */  	outb_p(inb(swc_base_addr + 0x0f) | 0x03, swc_base_addr + 0x0f); @@ -152,8 +151,7 @@ static inline void pc87413_swc_bank3(unsigned int swc_base_addr)  /* Set watchdog timeout to x minutes */ -static inline void pc87413_programm_wdto(unsigned int swc_base_addr, -					 char pc87413_time) +static inline void pc87413_programm_wdto(char pc87413_time)  {  	/* Step 5: Programm WDTO, Twd. */  	outb_p(pc87413_time, swc_base_addr + WDTO); @@ -164,7 +162,7 @@ static inline void pc87413_programm_wdto(unsigned int swc_base_addr,  /* Enable WDEN */ -static inline void pc87413_enable_wden(unsigned int swc_base_addr) +static inline void pc87413_enable_wden(void)  {  	/* Step 6: Enable WDEN */  	outb_p(inb(swc_base_addr + WDCTL) | 0x01, swc_base_addr + WDCTL); @@ -174,7 +172,7 @@ static inline void pc87413_enable_wden(unsigned int swc_base_addr)  }  /* Enable SW_WD_TREN */ -static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr) +static inline void pc87413_enable_sw_wd_tren(void)  {  	/* Enable SW_WD_TREN */  	outb_p(inb(swc_base_addr + WDCFG) | 0x80, swc_base_addr + WDCFG); @@ -185,7 +183,7 @@ static inline void pc87413_enable_sw_wd_tren(unsigned int swc_base_addr)  /* Disable SW_WD_TREN */ -static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr) +static inline void pc87413_disable_sw_wd_tren(void)  {  	/* Disable SW_WD_TREN */  	outb_p(inb(swc_base_addr + WDCFG) & 0x7f, swc_base_addr + WDCFG); @@ -196,7 +194,7 @@ static inline void pc87413_disable_sw_wd_tren(unsigned int swc_base_addr)  /* Enable SW_WD_TRG */ -static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr) +static inline void pc87413_enable_sw_wd_trg(void)  {  	/* Enable SW_WD_TRG */  	outb_p(inb(swc_base_addr + WDCTL) | 0x80, swc_base_addr + WDCTL); @@ -207,7 +205,7 @@ static inline void pc87413_enable_sw_wd_trg(unsigned int swc_base_addr)  /* Disable SW_WD_TRG */ -static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr) +static inline void pc87413_disable_sw_wd_trg(void)  {  	/* Disable SW_WD_TRG */  	outb_p(inb(swc_base_addr + WDCTL) & 0x7f, swc_base_addr + WDCTL); @@ -222,18 +220,13 @@ static inline void pc87413_disable_sw_wd_trg(unsigned int swc_base_addr)  static void pc87413_enable(void)  { -	unsigned int swc_base_addr; -  	spin_lock(&io_lock); -	pc87413_select_wdt_out(); -	pc87413_enable_swc(); -	swc_base_addr = pc87413_get_swc_base(); -	pc87413_swc_bank3(swc_base_addr); -	pc87413_programm_wdto(swc_base_addr, timeout); -	pc87413_enable_wden(swc_base_addr); -	pc87413_enable_sw_wd_tren(swc_base_addr); -	pc87413_enable_sw_wd_trg(swc_base_addr); +	pc87413_swc_bank3(); +	pc87413_programm_wdto(timeout); +	pc87413_enable_wden(); +	pc87413_enable_sw_wd_tren(); +	pc87413_enable_sw_wd_trg();  	spin_unlock(&io_lock);  } @@ -242,17 +235,12 @@ static void pc87413_enable(void)  static void pc87413_disable(void)  { -	unsigned int swc_base_addr; -  	spin_lock(&io_lock); -	pc87413_select_wdt_out(); -	pc87413_enable_swc(); -	swc_base_addr = pc87413_get_swc_base(); -	pc87413_swc_bank3(swc_base_addr); -	pc87413_disable_sw_wd_tren(swc_base_addr); -	pc87413_disable_sw_wd_trg(swc_base_addr); -	pc87413_programm_wdto(swc_base_addr, 0); +	pc87413_swc_bank3(); +	pc87413_disable_sw_wd_tren(); +	pc87413_disable_sw_wd_trg(); +	pc87413_programm_wdto(0);  	spin_unlock(&io_lock);  } @@ -261,20 +249,15 @@ static void pc87413_disable(void)  static void pc87413_refresh(void)  { -	unsigned int swc_base_addr; -  	spin_lock(&io_lock); -	pc87413_select_wdt_out(); -	pc87413_enable_swc(); -	swc_base_addr = pc87413_get_swc_base(); -	pc87413_swc_bank3(swc_base_addr); -	pc87413_disable_sw_wd_tren(swc_base_addr); -	pc87413_disable_sw_wd_trg(swc_base_addr); -	pc87413_programm_wdto(swc_base_addr, timeout); -	pc87413_enable_wden(swc_base_addr); -	pc87413_enable_sw_wd_tren(swc_base_addr); -	pc87413_enable_sw_wd_trg(swc_base_addr); +	pc87413_swc_bank3(); +	pc87413_disable_sw_wd_tren(); +	pc87413_disable_sw_wd_trg(); +	pc87413_programm_wdto(timeout); +	pc87413_enable_wden(); +	pc87413_enable_sw_wd_tren(); +	pc87413_enable_sw_wd_trg();  	spin_unlock(&io_lock);  } @@ -528,7 +511,8 @@ static int __init pc87413_init(void)  	printk(KERN_INFO PFX "Version " VERSION " at io 0x%X\n",  							WDT_INDEX_IO_PORT); -	/* request_region(io, 2, "pc87413"); */ +	if (!request_muxed_region(io, 2, MODNAME)) +		return -EBUSY;  	ret = register_reboot_notifier(&pc87413_notifier);  	if (ret != 0) { @@ -541,12 +525,32 @@ static int __init pc87413_init(void)  		printk(KERN_ERR PFX  			"cannot register miscdev on minor=%d (err=%d)\n",  			WATCHDOG_MINOR, ret); -		unregister_reboot_notifier(&pc87413_notifier); -		return ret; +		goto reboot_unreg;  	}  	printk(KERN_INFO PFX "initialized. timeout=%d min \n", timeout); + +	pc87413_select_wdt_out(); +	pc87413_enable_swc(); +	pc87413_get_swc_base_addr(); + +	if (!request_region(swc_base_addr, 0x20, MODNAME)) { +		printk(KERN_ERR PFX +			"cannot request SWC region at 0x%x\n", swc_base_addr); +		ret = -EBUSY; +		goto misc_unreg; +	} +  	pc87413_enable(); + +	release_region(io, 2);  	return 0; + +misc_unreg: +	misc_deregister(&pc87413_miscdev); +reboot_unreg: +	unregister_reboot_notifier(&pc87413_notifier); +	release_region(io, 2); +	return ret;  }  /** @@ -569,7 +573,7 @@ static void __exit pc87413_exit(void)  	misc_deregister(&pc87413_miscdev);  	unregister_reboot_notifier(&pc87413_notifier); -	/* release_region(io, 2); */ +	release_region(swc_base_addr, 0x20);  	printk(KERN_INFO MODNAME " watchdog component driver removed.\n");  } diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index f7f5aa00df60..30da88f47cd3 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -589,6 +589,15 @@ static int s3c2410wdt_resume(struct platform_device *dev)  #define s3c2410wdt_resume  NULL  #endif /* CONFIG_PM */ +#ifdef CONFIG_OF +static const struct of_device_id s3c2410_wdt_match[] = { +	{ .compatible = "samsung,s3c2410-wdt" }, +	{}, +}; +MODULE_DEVICE_TABLE(of, s3c2410_wdt_match); +#else +#define s3c2410_wdt_match NULL +#endif  static struct platform_driver s3c2410wdt_driver = {  	.probe		= s3c2410wdt_probe, @@ -599,6 +608,7 @@ static struct platform_driver s3c2410wdt_driver = {  	.driver		= {  		.owner	= THIS_MODULE,  		.name	= "s3c2410-wdt", +		.of_match_table	= s3c2410_wdt_match,  	},  }; diff --git a/drivers/watchdog/sbc7240_wdt.c b/drivers/watchdog/sbc7240_wdt.c index ff11504c376e..93ac58953122 100644 --- a/drivers/watchdog/sbc7240_wdt.c +++ b/drivers/watchdog/sbc7240_wdt.c @@ -29,7 +29,7 @@  #include <linux/watchdog.h>  #include <linux/io.h>  #include <linux/uaccess.h> -#include <asm/atomic.h> +#include <linux/atomic.h>  #include <asm/system.h>  #define SBC7240_PREFIX "sbc7240_wdt: " diff --git a/drivers/watchdog/sch311x_wdt.c b/drivers/watchdog/sch311x_wdt.c index c7cf4b01f58d..029467e34636 100644 --- a/drivers/watchdog/sch311x_wdt.c +++ b/drivers/watchdog/sch311x_wdt.c @@ -472,15 +472,10 @@ static void sch311x_wdt_shutdown(struct platform_device *dev)  	sch311x_wdt_stop();  } -#define sch311x_wdt_suspend NULL -#define sch311x_wdt_resume  NULL -  static struct platform_driver sch311x_wdt_driver = {  	.probe		= sch311x_wdt_probe,  	.remove		= __devexit_p(sch311x_wdt_remove),  	.shutdown	= sch311x_wdt_shutdown, -	.suspend	= sch311x_wdt_suspend, -	.resume		= sch311x_wdt_resume,  	.driver		= {  		.owner = THIS_MODULE,  		.name = DRV_NAME, diff --git a/drivers/watchdog/shwdt.c b/drivers/watchdog/shwdt.c index db84f2322d1a..a267dc078daf 100644 --- a/drivers/watchdog/shwdt.c +++ b/drivers/watchdog/shwdt.c @@ -64,7 +64,7 @@   * misses its deadline, the kernel timer will allow the WDT to overflow.   */  static int clock_division_ratio = WTCSR_CKS_4096; -#define next_ping_period(cks)	msecs_to_jiffies(cks - 4) +#define next_ping_period(cks)	(jiffies + msecs_to_jiffies(cks - 4))  static const struct watchdog_info sh_wdt_info;  static struct platform_device *sh_wdt_dev; diff --git a/drivers/watchdog/sp805_wdt.c b/drivers/watchdog/sp805_wdt.c index 0d80e08b6439..cc2cfbe33b30 100644 --- a/drivers/watchdog/sp805_wdt.c +++ b/drivers/watchdog/sp805_wdt.c @@ -134,6 +134,8 @@ static void wdt_enable(void)  	writel(INT_ENABLE | RESET_ENABLE, wdt->base + WDTCONTROL);  	writel(LOCK, wdt->base + WDTLOCK); +	/* Flush posted writes. */ +	readl(wdt->base + WDTLOCK);  	spin_unlock(&wdt->lock);  } @@ -144,9 +146,10 @@ static void wdt_disable(void)  	writel(UNLOCK, wdt->base + WDTLOCK);  	writel(0, wdt->base + WDTCONTROL); -	writel(0, wdt->base + WDTLOAD);  	writel(LOCK, wdt->base + WDTLOCK); +	/* Flush posted writes. */ +	readl(wdt->base + WDTLOCK);  	spin_unlock(&wdt->lock);  } diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c new file mode 100644 index 000000000000..cfa1a1518aad --- /dev/null +++ b/drivers/watchdog/watchdog_core.c @@ -0,0 +1,111 @@ +/* + *	watchdog_core.c + * + *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, + *						All Rights Reserved. + * + *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. + * + *	This source code is part of the generic code that can be used + *	by all the watchdog timer drivers. + * + *	Based on source code of the following authors: + *	  Matt Domsch <Matt_Domsch@dell.com>, + *	  Rob Radez <rob@osinvestor.com>, + *	  Rusty Lynch <rusty@linux.co.intel.com> + *	  Satyam Sharma <satyam@infradead.org> + *	  Randy Dunlap <randy.dunlap@oracle.com> + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; either version + *	2 of the License, or (at your option) any later version. + * + *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + *	admit liability nor provide warranty for any of this software. + *	This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h>	/* For EXPORT_SYMBOL/module stuff/... */ +#include <linux/types.h>	/* For standard types */ +#include <linux/errno.h>	/* For the -ENODEV/... values */ +#include <linux/kernel.h>	/* For printk/panic/... */ +#include <linux/watchdog.h>	/* For watchdog specific items */ +#include <linux/init.h>		/* For __init/__exit/... */ + +#include "watchdog_dev.h"	/* For watchdog_dev_register/... */ + +/** + * watchdog_register_device() - register a watchdog device + * @wdd: watchdog device + * + * Register a watchdog device with the kernel so that the + * watchdog timer can be accessed from userspace. + * + * A zero is returned on success and a negative errno code for + * failure. + */ +int watchdog_register_device(struct watchdog_device *wdd) +{ +	int ret; + +	if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) +		return -EINVAL; + +	/* Mandatory operations need to be supported */ +	if (wdd->ops->start == NULL || wdd->ops->stop == NULL) +		return -EINVAL; + +	/* +	 * Check that we have valid min and max timeout values, if +	 * not reset them both to 0 (=not used or unknown) +	 */ +	if (wdd->min_timeout > wdd->max_timeout) { +		pr_info("Invalid min and max timeout values, resetting to 0!\n"); +		wdd->min_timeout = 0; +		wdd->max_timeout = 0; +	} + +	/* +	 * Note: now that all watchdog_device data has been verified, we +	 * will not check this anymore in other functions. If data gets +	 * corrupted in a later stage then we expect a kernel panic! +	 */ + +	/* We only support 1 watchdog device via the /dev/watchdog interface */ +	ret = watchdog_dev_register(wdd); +	if (ret) { +		pr_err("error registering /dev/watchdog (err=%d).\n", ret); +		return ret; +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(watchdog_register_device); + +/** + * watchdog_unregister_device() - unregister a watchdog device + * @wdd: watchdog device to unregister + * + * Unregister a watchdog device that was previously successfully + * registered with watchdog_register_device(). + */ +void watchdog_unregister_device(struct watchdog_device *wdd) +{ +	int ret; + +	if (wdd == NULL) +		return; + +	ret = watchdog_dev_unregister(wdd); +	if (ret) +		pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); +} +EXPORT_SYMBOL_GPL(watchdog_unregister_device); + +MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>"); +MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>"); +MODULE_DESCRIPTION("WatchDog Timer Driver Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c new file mode 100644 index 000000000000..d33520d0b4c9 --- /dev/null +++ b/drivers/watchdog/watchdog_dev.c @@ -0,0 +1,395 @@ +/* + *	watchdog_dev.c + * + *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, + *						All Rights Reserved. + * + *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. + * + * + *	This source code is part of the generic code that can be used + *	by all the watchdog timer drivers. + * + *	This part of the generic code takes care of the following + *	misc device: /dev/watchdog. + * + *	Based on source code of the following authors: + *	  Matt Domsch <Matt_Domsch@dell.com>, + *	  Rob Radez <rob@osinvestor.com>, + *	  Rusty Lynch <rusty@linux.co.intel.com> + *	  Satyam Sharma <satyam@infradead.org> + *	  Randy Dunlap <randy.dunlap@oracle.com> + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; either version + *	2 of the License, or (at your option) any later version. + * + *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + *	admit liability nor provide warranty for any of this software. + *	This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/module.h>	/* For module stuff/... */ +#include <linux/types.h>	/* For standard types (like size_t) */ +#include <linux/errno.h>	/* For the -ENODEV/... values */ +#include <linux/kernel.h>	/* For printk/panic/... */ +#include <linux/fs.h>		/* For file operations */ +#include <linux/watchdog.h>	/* For watchdog specific items */ +#include <linux/miscdevice.h>	/* For handling misc devices */ +#include <linux/init.h>		/* For __init/__exit/... */ +#include <linux/uaccess.h>	/* For copy_to_user/put_user/... */ + +/* make sure we only register one /dev/watchdog device */ +static unsigned long watchdog_dev_busy; +/* the watchdog device behind /dev/watchdog */ +static struct watchdog_device *wdd; + +/* + *	watchdog_ping: ping the watchdog. + *	@wddev: the watchdog device to ping + * + *	If the watchdog has no own ping operation then it needs to be + *	restarted via the start operation. This wrapper function does + *	exactly that. + *	We only ping when the watchdog device is running. + */ + +static int watchdog_ping(struct watchdog_device *wddev) +{ +	if (test_bit(WDOG_ACTIVE, &wdd->status)) { +		if (wddev->ops->ping) +			return wddev->ops->ping(wddev);  /* ping the watchdog */ +		else +			return wddev->ops->start(wddev); /* restart watchdog */ +	} +	return 0; +} + +/* + *	watchdog_start: wrapper to start the watchdog. + *	@wddev: the watchdog device to start + * + *	Start the watchdog if it is not active and mark it active. + *	This function returns zero on success or a negative errno code for + *	failure. + */ + +static int watchdog_start(struct watchdog_device *wddev) +{ +	int err; + +	if (!test_bit(WDOG_ACTIVE, &wdd->status)) { +		err = wddev->ops->start(wddev); +		if (err < 0) +			return err; + +		set_bit(WDOG_ACTIVE, &wdd->status); +	} +	return 0; +} + +/* + *	watchdog_stop: wrapper to stop the watchdog. + *	@wddev: the watchdog device to stop + * + *	Stop the watchdog if it is still active and unmark it active. + *	This function returns zero on success or a negative errno code for + *	failure. + *	If the 'nowayout' feature was set, the watchdog cannot be stopped. + */ + +static int watchdog_stop(struct watchdog_device *wddev) +{ +	int err = -EBUSY; + +	if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { +		pr_info("%s: nowayout prevents watchdog to be stopped!\n", +							wdd->info->identity); +		return err; +	} + +	if (test_bit(WDOG_ACTIVE, &wdd->status)) { +		err = wddev->ops->stop(wddev); +		if (err < 0) +			return err; + +		clear_bit(WDOG_ACTIVE, &wdd->status); +	} +	return 0; +} + +/* + *	watchdog_write: writes to the watchdog. + *	@file: file from VFS + *	@data: user address of data + *	@len: length of data + *	@ppos: pointer to the file offset + * + *	A write to a watchdog device is defined as a keepalive ping. + *	Writing the magic 'V' sequence allows the next close to turn + *	off the watchdog (if 'nowayout' is not set). + */ + +static ssize_t watchdog_write(struct file *file, const char __user *data, +						size_t len, loff_t *ppos) +{ +	size_t i; +	char c; + +	if (len == 0) +		return 0; + +	/* +	 * Note: just in case someone wrote the magic character +	 * five months ago... +	 */ +	clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); + +	/* scan to see whether or not we got the magic character */ +	for (i = 0; i != len; i++) { +		if (get_user(c, data + i)) +			return -EFAULT; +		if (c == 'V') +			set_bit(WDOG_ALLOW_RELEASE, &wdd->status); +	} + +	/* someone wrote to us, so we send the watchdog a keepalive ping */ +	watchdog_ping(wdd); + +	return len; +} + +/* + *	watchdog_ioctl: handle the different ioctl's for the watchdog device. + *	@file: file handle to the device + *	@cmd: watchdog command + *	@arg: argument pointer + * + *	The watchdog API defines a common set of functions for all watchdogs + *	according to their available features. + */ + +static long watchdog_ioctl(struct file *file, unsigned int cmd, +							unsigned long arg) +{ +	void __user *argp = (void __user *)arg; +	int __user *p = argp; +	unsigned int val; +	int err; + +	if (wdd->ops->ioctl) { +		err = wdd->ops->ioctl(wdd, cmd, arg); +		if (err != -ENOIOCTLCMD) +			return err; +	} + +	switch (cmd) { +	case WDIOC_GETSUPPORT: +		return copy_to_user(argp, wdd->info, +			sizeof(struct watchdog_info)) ? -EFAULT : 0; +	case WDIOC_GETSTATUS: +		val = wdd->ops->status ? wdd->ops->status(wdd) : 0; +		return put_user(val, p); +	case WDIOC_GETBOOTSTATUS: +		return put_user(wdd->bootstatus, p); +	case WDIOC_SETOPTIONS: +		if (get_user(val, p)) +			return -EFAULT; +		if (val & WDIOS_DISABLECARD) { +			err = watchdog_stop(wdd); +			if (err < 0) +				return err; +		} +		if (val & WDIOS_ENABLECARD) { +			err = watchdog_start(wdd); +			if (err < 0) +				return err; +		} +		return 0; +	case WDIOC_KEEPALIVE: +		if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) +			return -EOPNOTSUPP; +		watchdog_ping(wdd); +		return 0; +	case WDIOC_SETTIMEOUT: +		if ((wdd->ops->set_timeout == NULL) || +		    !(wdd->info->options & WDIOF_SETTIMEOUT)) +			return -EOPNOTSUPP; +		if (get_user(val, p)) +			return -EFAULT; +		if ((wdd->max_timeout != 0) && +		    (val < wdd->min_timeout || val > wdd->max_timeout)) +				return -EINVAL; +		err = wdd->ops->set_timeout(wdd, val); +		if (err < 0) +			return err; +		wdd->timeout = val; +		/* If the watchdog is active then we send a keepalive ping +		 * to make sure that the watchdog keep's running (and if +		 * possible that it takes the new timeout) */ +		watchdog_ping(wdd); +		/* Fall */ +	case WDIOC_GETTIMEOUT: +		/* timeout == 0 means that we don't know the timeout */ +		if (wdd->timeout == 0) +			return -EOPNOTSUPP; +		return put_user(wdd->timeout, p); +	default: +		return -ENOTTY; +	} +} + +/* + *	watchdog_open: open the /dev/watchdog device. + *	@inode: inode of device + *	@file: file handle to device + * + *	When the /dev/watchdog device gets opened, we start the watchdog. + *	Watch out: the /dev/watchdog device is single open, so we make sure + *	it can only be opened once. + */ + +static int watchdog_open(struct inode *inode, struct file *file) +{ +	int err = -EBUSY; + +	/* the watchdog is single open! */ +	if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) +		return -EBUSY; + +	/* +	 * If the /dev/watchdog device is open, we don't want the module +	 * to be unloaded. +	 */ +	if (!try_module_get(wdd->ops->owner)) +		goto out; + +	err = watchdog_start(wdd); +	if (err < 0) +		goto out_mod; + +	/* dev/watchdog is a virtual (and thus non-seekable) filesystem */ +	return nonseekable_open(inode, file); + +out_mod: +	module_put(wdd->ops->owner); +out: +	clear_bit(WDOG_DEV_OPEN, &wdd->status); +	return err; +} + +/* + *      watchdog_release: release the /dev/watchdog device. + *      @inode: inode of device + *      @file: file handle to device + * + *	This is the code for when /dev/watchdog gets closed. We will only + *	stop the watchdog when we have received the magic char (and nowayout + *	was not set), else the watchdog will keep running. + */ + +static int watchdog_release(struct inode *inode, struct file *file) +{ +	int err = -EBUSY; + +	/* +	 * We only stop the watchdog if we received the magic character +	 * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then +	 * watchdog_stop will fail. +	 */ +	if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || +	    !(wdd->info->options & WDIOF_MAGICCLOSE)) +		err = watchdog_stop(wdd); + +	/* If the watchdog was not stopped, send a keepalive ping */ +	if (err < 0) { +		pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); +		watchdog_ping(wdd); +	} + +	/* Allow the owner module to be unloaded again */ +	module_put(wdd->ops->owner); + +	/* make sure that /dev/watchdog can be re-opened */ +	clear_bit(WDOG_DEV_OPEN, &wdd->status); + +	return 0; +} + +static const struct file_operations watchdog_fops = { +	.owner		= THIS_MODULE, +	.write		= watchdog_write, +	.unlocked_ioctl	= watchdog_ioctl, +	.open		= watchdog_open, +	.release	= watchdog_release, +}; + +static struct miscdevice watchdog_miscdev = { +	.minor		= WATCHDOG_MINOR, +	.name		= "watchdog", +	.fops		= &watchdog_fops, +}; + +/* + *	watchdog_dev_register: + *	@watchdog: watchdog device + * + *	Register a watchdog device as /dev/watchdog. /dev/watchdog + *	is actually a miscdevice and thus we set it up like that. + */ + +int watchdog_dev_register(struct watchdog_device *watchdog) +{ +	int err; + +	/* Only one device can register for /dev/watchdog */ +	if (test_and_set_bit(0, &watchdog_dev_busy)) { +		pr_err("only one watchdog can use /dev/watchdog.\n"); +		return -EBUSY; +	} + +	wdd = watchdog; + +	err = misc_register(&watchdog_miscdev); +	if (err != 0) { +		pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", +			watchdog->info->identity, WATCHDOG_MINOR, err); +		goto out; +	} + +	return 0; + +out: +	wdd = NULL; +	clear_bit(0, &watchdog_dev_busy); +	return err; +} + +/* + *	watchdog_dev_unregister: + *	@watchdog: watchdog device + * + *	Deregister the /dev/watchdog device. + */ + +int watchdog_dev_unregister(struct watchdog_device *watchdog) +{ +	/* Check that a watchdog device was registered in the past */ +	if (!test_bit(0, &watchdog_dev_busy) || !wdd) +		return -ENODEV; + +	/* We can only unregister the watchdog device that was registered */ +	if (watchdog != wdd) { +		pr_err("%s: watchdog was not registered as /dev/watchdog.\n", +			watchdog->info->identity); +		return -ENODEV; +	} + +	misc_deregister(&watchdog_miscdev); +	wdd = NULL; +	clear_bit(0, &watchdog_dev_busy); +	return 0; +} diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h new file mode 100644 index 000000000000..bc7612be25ce --- /dev/null +++ b/drivers/watchdog/watchdog_dev.h @@ -0,0 +1,33 @@ +/* + *	watchdog_core.h + * + *	(c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>, + *						All Rights Reserved. + * + *	(c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>. + * + *	This source code is part of the generic code that can be used + *	by all the watchdog timer drivers. + * + *	Based on source code of the following authors: + *	  Matt Domsch <Matt_Domsch@dell.com>, + *	  Rob Radez <rob@osinvestor.com>, + *	  Rusty Lynch <rusty@linux.co.intel.com> + *	  Satyam Sharma <satyam@infradead.org> + *	  Randy Dunlap <randy.dunlap@oracle.com> + * + *	This program is free software; you can redistribute it and/or + *	modify it under the terms of the GNU General Public License + *	as published by the Free Software Foundation; either version + *	2 of the License, or (at your option) any later version. + * + *	Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + *	admit liability nor provide warranty for any of this software. + *	This material is provided "AS-IS" and at no charge. + */ + +/* + *	Functions/procedures to be called by the core + */ +int watchdog_dev_register(struct watchdog_device *); +int watchdog_dev_unregister(struct watchdog_device *); diff --git a/drivers/watchdog/wm831x_wdt.c b/drivers/watchdog/wm831x_wdt.c index 8c4b2d5bb7da..871caea4e1c6 100644 --- a/drivers/watchdog/wm831x_wdt.c +++ b/drivers/watchdog/wm831x_wdt.c @@ -320,6 +320,11 @@ static int __devinit wm831x_wdt_probe(struct platform_device *pdev)  	struct wm831x_watchdog_pdata *pdata;  	int reg, ret; +	if (wm831x) { +		dev_err(&pdev->dev, "wm831x watchdog already registered\n"); +		return -EBUSY; +	} +  	wm831x = dev_get_drvdata(pdev->dev.parent);  	ret = wm831x_reg_read(wm831x, WM831X_WATCHDOG); | 
