All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kevin Strasser <kevin.strasser@linux.intel.com>
To: linux-kernel@vger.kernel.org
Cc: Michael Brunner <michael.brunner@kontron.com>,
	Samuel Ortiz <sameo@linux.intel.com>,
	Wolfram Sang <wsa@the-dreams.de>, Ben Dooks <ben-linux@fluff.org>,
	linux-i2c@vger.kernel.org,
	Grant Likely <grant.likely@secretlab.ca>,
	Linus Walleij <linus.walleij@linaro.org>,
	Wim Van Sebroeck <wim@iguana.be>,
	linux-watchdog@vger.kernel.org,
	Darren Hart <dvhart@linux.intel.com>,
	Michael Brunner <mibru@gmx.de>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Kevin Strasser <kevin.strasser@linux.intel.com>
Subject: [PATCH 4/4] watchdog: Kontron PLD watchdog timer
Date: Mon,  8 Apr 2013 10:15:21 -0700	[thread overview]
Message-ID: <1365441321-21952-4-git-send-email-kevin.strasser@linux.intel.com> (raw)
In-Reply-To: <1365441321-21952-1-git-send-email-kevin.strasser@linux.intel.com>

From: Michael Brunner <michael.brunner@kontron.com>

Add watchdog timer support for the on-board PLD found on some Kontron
embedded modules.

Signed-off-by: Michael Brunner <michael.brunner@kontron.com>
Signed-off-by: Kevin Strasser <kevin.strasser@linux.intel.com>
---
 drivers/watchdog/Kconfig           |   20 +
 drivers/watchdog/Makefile          |    2 +
 drivers/watchdog/kempld_now1_wdt.c |  602 +++++++++++++++++++++++++++
 drivers/watchdog/kempld_wdt.c      |  796 ++++++++++++++++++++++++++++++++++++
 drivers/watchdog/kempld_wdt.h      |   75 ++++
 5 files changed, 1495 insertions(+)
 create mode 100644 drivers/watchdog/kempld_now1_wdt.c
 create mode 100644 drivers/watchdog/kempld_wdt.c
 create mode 100644 drivers/watchdog/kempld_wdt.h

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9fcc70c..9ac71ca 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -687,6 +687,26 @@ config HP_WATCHDOG
 	  To compile this driver as a module, choose M here: the module will be
 	  called hpwdt.
 
+config KEMPLD_WDT
+	tristate "Kontron COM watchdog"
+	depends on MFD_KEMPLD
+	help
+	  Support for the PLD watchdog on some Kontron ETX and COMexpress
+	  (ETXexpress) modules
+
+	  This driver can also be built as a module. If so, the module will be
+	  called kempld_wdt.
+
+config KEMPLD_NOW1_WDT
+	tristate "Kontron COMe-mSP1 (nanoETXexpress-SP) watchdog"
+	depends on MFD_KEMPLD
+	help
+	  Support for the PLD watchdog on the Kontron COMe-mSP1
+	  (nanoETXexpress-SP) module.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called kempld_now1_wdt.
+
 config HPWDT_NMI_DECODING
 	bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
 	depends on HP_WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a300b94..a029930 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -90,6 +90,8 @@ endif
 obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
 obj-$(CONFIG_IT87_WDT) += it87_wdt.o
 obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
+obj-$(CONFIG_KEMPLD_WDT) += kempld_wdt.o
+obj-$(COnFIG_KEMPLD_NOW1_WDT) += kempld_now1_wdt.o
 obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
 obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
 obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
diff --git a/drivers/watchdog/kempld_now1_wdt.c b/drivers/watchdog/kempld_now1_wdt.c
new file mode 100644
index 0000000..19b7272
--- /dev/null
+++ b/drivers/watchdog/kempld_now1_wdt.c
@@ -0,0 +1,602 @@
+/*
+ *  kempld_now1_wdt.c - Kontron PLD watchdog driver for COMe-mSP1
+ *
+ *  Copyright (c) 2011-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ *  Note: The watchdog of the COMe-mSP1 (nanoETXexpress-SP) has one stage and
+ *        only supports predefined watchdog timeout values.
+ *
+ *        The supported timeouts are:
+ *              1 s, 5 s, 10 s, 30 s, 1 min, 5 min, 10 min, 15 min
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/kempld.h>
+
+#include "kempld_wdt.h"
+
+#define WATCHDOG_TIMEOUT 30
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+		 "Watchdog timeout in seconds. (1, 5, 10, 30, 60, 300, 600, "
+		 "900, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+		 "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* nanoETXexpress-SP watchdog register definitions */
+#define KEMPLD_WDT_NOW1					0xA2
+#define		KEMPLD_WDT_NOW1_KICK_MASK		0x80
+#define		KEMPLD_WDT_NOW1_ENABLE_MASK		0x40
+#define		KEMPLD_WDT_NOW1_TIMEOUT_MASK		0x1f
+#define			KEMPLD_WDT_NOW1_TIMEOUT_1SEC	0x00
+#define			KEMPLD_WDT_NOW1_TIMEOUT_5SEC	0x01
+#define			KEMPLD_WDT_NOW1_TIMEOUT_10SEC	0x02
+#define			KEMPLD_WDT_NOW1_TIMEOUT_30SEC	0x03
+#define			KEMPLD_WDT_NOW1_TIMEOUT_1MIN	0x10
+#define			KEMPLD_WDT_NOW1_TIMEOUT_5MIN	0x11
+#define			KEMPLD_WDT_NOW1_TIMEOUT_10MIN	0x12
+#define			KEMPLD_WDT_NOW1_TIMEOUT_15MIN	0x13
+
+/* delay in us necessary due to clock domain sync */
+#define		KEMPLD_WDT_NOW1_SYNC_DELAY		31
+
+static struct kempld_watchdog_data *kempld_now1_wdt;
+
+static int kempld_now1_wdt_read_supported;
+static int kempld_now1_wdt_reg_cache;
+
+static int kempld_now1_wdt_start(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	if (kempld_now1_wdt_read_supported)
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	wdt_reg |= KEMPLD_WDT_NOW1_ENABLE_MASK;
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	if (kempld_now1_wdt_read_supported) {
+		/* read out the register again to check if everything worked */
+		kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_release_mutex(pld);
+
+		/* check if the watchdog was disabled */
+		if (!(wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK))
+			return -EACCES;
+	}
+
+	return 0;
+}
+
+static int kempld_now1_wdt_stop(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	wdt_reg &= ~KEMPLD_WDT_NOW1_ENABLE_MASK;
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	if (kempld_now1_wdt_read_supported) {
+		/* read out the register again to check if everything worked */
+		kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_release_mutex(pld);
+
+		/* check if the watchdog was disabled */
+		if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK)
+			return -EACCES;
+	}
+
+	return 0;
+}
+
+static int kempld_now1_wdt_keepalive(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+	if (kempld_now1_wdt_read_supported) {
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = kempld_now1_wdt_reg_cache;
+		/* write the state again to be sure the trigger register has
+		 * the right level */
+		kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	}
+
+	if (wdt_reg & KEMPLD_WDT_NOW1_KICK_MASK)
+		wdt_reg &= ~KEMPLD_WDT_NOW1_KICK_MASK;
+	else
+		wdt_reg |= KEMPLD_WDT_NOW1_KICK_MASK;
+
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+
+	kempld_now1_wdt_reg_cache = wdt_reg;
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+
+static int kempld_now1_wdt_settimeout(struct kempld_watchdog_data *wdt,
+					int check_only)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+	int ret = 0;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+
+	wdt_reg &= ~KEMPLD_WDT_NOW1_TIMEOUT_MASK;
+
+	switch (wdt->timeout) {
+	case 1:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_1SEC;
+		break;
+	case 5:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_5SEC;
+		break;
+	case 10:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_10SEC;
+		break;
+	case 30:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_30SEC;
+		break;
+	case 60:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_1MIN;
+		break;
+	case 300:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_5MIN;
+		break;
+	case 600:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_10MIN;
+		break;
+	case 900:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_15MIN;
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err(wdt->pld->dev,
+			"Invalid timeout value given!\n");
+	}
+
+	if (!check_only) {
+		if (ret == 0) {
+			udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+			kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+		}
+	}
+
+	kempld_now1_wdt_reg_cache = wdt_reg;
+
+	kempld_release_mutex(pld);
+
+	return ret;
+}
+
+static int kempld_now1_wdt_gettimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+	int timeout;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_now1_wdt_reg_cache = wdt_reg;
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+
+	kempld_release_mutex(pld);
+
+	switch (wdt_reg&KEMPLD_WDT_NOW1_TIMEOUT_MASK) {
+	case KEMPLD_WDT_NOW1_TIMEOUT_1SEC:
+		timeout = 1;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_5SEC:
+		timeout = 5;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_10SEC:
+		timeout = 10;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_30SEC:
+		timeout = 30;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_1MIN:
+		timeout = 60;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_5MIN:
+		timeout = 300;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_10MIN:
+		timeout = 600;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_15MIN:
+		timeout = 900;
+		break;
+	default:
+		timeout = -ERANGE;
+	}
+
+	return timeout;
+}
+
+static ssize_t kempld_now1_wdt_write(struct file *file, const char __user
+					*data, size_t count, loff_t *ppos)
+{
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (count) {
+		kempld_now1_wdt_keepalive(wdt);
+
+		if (!nowayout) {
+			size_t i;
+
+			wdt->expect_close = 0;
+
+			for (i = 0; i < count; i++) {
+				char c;
+				if (get_user(c, data+i))
+					return -EFAULT;
+				if (c == 'V')
+					wdt->expect_close = 42;
+			}
+		}
+	}
+
+	return count;
+}
+
+static long kempld_now1_wdt_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+	int options;
+	int value;
+	int ret = 0;
+
+	BUG_ON(wdt == NULL);
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user(argp, &wdt->ident, sizeof(wdt->ident)))
+			ret = -EFAULT;
+		break;
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		ret = put_user(0, p);
+		break;
+	case WDIOC_SETOPTIONS:
+		if (get_user(options, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		if (options & WDIOS_DISABLECARD)
+			ret = kempld_now1_wdt_stop(wdt);
+		if (options & WDIOS_ENABLECARD) {
+			ret = kempld_now1_wdt_start(wdt);
+			kempld_now1_wdt_keepalive(wdt);
+		}
+		break;
+	case WDIOC_KEEPALIVE:
+		kempld_now1_wdt_keepalive(wdt);
+		break;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		wdt->timeout = value;
+		ret = kempld_now1_wdt_settimeout(wdt, 0);
+		kempld_now1_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETTIMEOUT:
+		value = kempld_now1_wdt_gettimeout(wdt, wdt->timeout_stage);
+		if (timeout < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(timeout, p);
+		break;
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static int kempld_now1_wdt_release(struct inode *inode, struct file *file)
+{
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (wdt->expect_close)
+		kempld_now1_wdt_stop(wdt);
+	else {
+		dev_warn(wdt->pld->dev,
+			 "Unexpected close, not stopping watchdog!\n");
+		kempld_now1_wdt_keepalive(wdt);
+	}
+
+	kempld_now1_wdt->expect_close = 0;
+
+	clear_bit(0, &wdt->is_open);
+
+	return 0;
+}
+
+static int kempld_now1_wdt_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+	struct kempld_device_data *pld = wdt->pld;
+	u8 wdt_reg;
+
+	BUG_ON(wdt == NULL);
+
+	if (test_and_set_bit(0, &wdt->is_open))
+		return -EBUSY;
+
+	if (nowayout)
+		__module_get(THIS_MODULE);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	}
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	/* kick the watchdog if it is already enabled, otherwise start it */
+	if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK) {
+		kempld_now1_wdt_keepalive(wdt);
+	} else {
+		ret = kempld_now1_wdt_settimeout(wdt, 0);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_now1_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+	}
+
+	return nonseekable_open(inode, file);
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev, "Failed to enable the watchdog timer!\n");
+	wdt->expect_close = 1;
+	kempld_now1_wdt_release(inode, file);
+
+	return ret;
+}
+
+static const struct file_operations kempld_now1_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= kempld_now1_wdt_write,
+	.unlocked_ioctl	= kempld_now1_wdt_ioctl,
+	.open		= kempld_now1_wdt_open,
+	.release	= kempld_now1_wdt_release,
+};
+
+static struct miscdevice kempld_now1_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &kempld_now1_wdt_fops,
+};
+
+static int kempld_now1_wdt_probe(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt;
+	struct kempld_device_data *pld;
+	u8 wdt_reg;
+	int ret;
+
+	if (kempld_now1_wdt != NULL) {
+		dev_err(&pdev->dev,
+			"unable to support more than one watchdog devices\n");
+		return -EMFILE;
+	}
+
+	wdt = kzalloc(sizeof(struct kempld_watchdog_data), GFP_KERNEL);
+	if (wdt == NULL) {
+		dev_err(&pdev->dev, "unable to get memory for device data\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	wdt->timeout_stage = kzalloc(sizeof(struct kempld_watchdog_stage),
+					GFP_KERNEL);
+	if (wdt->timeout_stage == NULL) {
+		dev_err(&pdev->dev,
+			"unable to get memory for watchdog stage\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	wdt->stages = 1;
+	wdt->stage[0] = wdt->timeout_stage;
+
+	pld = dev_get_drvdata(pdev->dev.parent);
+	wdt->pld = pld;
+
+	platform_set_drvdata(pdev, wdt);
+
+	strncpy(wdt->ident.identity, "nanoETXexpress-SP Watchdog",
+		sizeof(wdt->ident.identity));
+
+	/* set default values for the case we start the watchdog or change
+	 * the configuration */
+	wdt->timeout = timeout;
+
+	/* use settimeout to check if the timeout parameter is valid */
+	ret = kempld_now1_wdt_settimeout(wdt, 1);
+	if (ret)
+		goto err_check_timeout;
+	wdt->ident.firmware_version = (pld->info.major<<8) + pld->info.minor;
+	if (pld->info.major > 67)
+		kempld_now1_wdt_read_supported = 1;
+	else
+		dev_info(wdt->pld->dev,
+			 "Watchdog revision does not support read - "
+			 "unable to get watchdog state!\n");
+
+	/* get initial watchdog status */
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = 0x0;
+	}
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(wdt->pld);
+
+	/* check if watchdog is enabled */
+	if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK)
+		dev_info(wdt->pld->dev, "Watchdog is already enabled!\n");
+
+	wdt->ident.options = WDIOF_KEEPALIVEPING;
+	wdt->ident.options |= WDIOF_SETTIMEOUT;
+	if (!nowayout)
+		wdt->ident.options |= WDIOF_MAGICCLOSE;
+
+	kempld_now1_wdt = wdt;
+
+	ret = misc_register(&kempld_now1_wdt_miscdev);
+	if (ret)
+		goto err_misc_register;
+
+	dev_info(wdt->pld->dev, "watchdog initialized\n");
+
+	return 0;
+
+err_misc_register:
+	kfree(kempld_now1_wdt);
+	kempld_now1_wdt = NULL;
+err_check_timeout:
+err_alloc_dev_data:
+	return ret;
+}
+
+static int kempld_now1_wdt_remove(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_now1_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	if (wdt != NULL) {
+		if (!nowayout)
+			kempld_now1_wdt_stop(wdt);
+		else
+			kempld_now1_wdt_keepalive(wdt);
+	}
+
+	misc_deregister(&kempld_now1_wdt_miscdev);
+
+	kfree(wdt);
+	kempld_now1_wdt = NULL;
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver kempld_now1_wdt_driver = {
+	.driver = {
+		.name = "kempld_now1-wdt",
+		.owner = THIS_MODULE,
+	},
+	.probe = kempld_now1_wdt_probe,
+	.remove = kempld_now1_wdt_remove,
+};
+
+static int __init kempld_now1_wdt_init(void)
+{
+	return platform_driver_register(&kempld_now1_wdt_driver);
+}
+
+static void __exit kempld_now1_wdt_exit(void)
+{
+	platform_driver_unregister(&kempld_now1_wdt_driver);
+}
+
+module_init(kempld_now1_wdt_init);
+module_exit(kempld_now1_wdt_exit);
+
+MODULE_DESCRIPTION("KEM PLD nanoETXexpress-SP Watchdog Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c
new file mode 100644
index 0000000..bc150e5
--- /dev/null
+++ b/drivers/watchdog/kempld_wdt.c
@@ -0,0 +1,796 @@
+/*
+ *  kempld_wdt.c - Kontron PLD watchdog driver
+ *
+ *  Copyright (c) 2010-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ *  Note: From the PLD watchdog point of view timeout and pretimeout are
+ *        defined differently than in the kernel.
+ *        First the pretimeout stage runs out before the timeout stage gets
+ *        active. This has to be kept in mind.
+ *
+ *  Kernel/API:                     P-----| pretimeout
+ *                |-----------------------T timeout
+ *  Watchdog:     |-----------------P       pretimeout_stage
+ *                                  |-----T timeout_stage
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/kempld.h>
+
+#include "kempld_wdt.h"
+
+#define WATCHDOG_DEFAULT_TIMEOUT 20
+#define WATCHDOG_DEFAULT_PRETIMEOUT 0
+static int timeout = -1;
+static int pretimeout = -1;
+/* The maximum timeout values have to be probed */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+		 "Watchdog timeout in seconds. (>0, default="
+		__MODULE_STRING(WATCHDOG_DEFAULT_TIMEOUT) ")");
+module_param(pretimeout, int, 0);
+MODULE_PARM_DESC(pretimeout,
+		 "Watchdog pretimeout in seconds. (>=0, default="
+		__MODULE_STRING(WATCHDOG_DEFAULT_PRETIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+		 "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct kempld_watchdog_data *kempld_wdt;
+
+static int kempld_wdt_start(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	status |= KEMPLD_WDT_CFG_ENABLE;
+	kempld_write8(pld, KEMPLD_WDT_CFG, status);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+
+	kempld_release_mutex(pld);
+
+	/* check if the watchdog was enabled */
+	if (!(status & KEMPLD_WDT_CFG_ENABLE))
+		return -EACCES;
+
+	return 0;
+}
+
+static int kempld_wdt_stop(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	status &= ~KEMPLD_WDT_CFG_ENABLE;
+	kempld_write8(pld, KEMPLD_WDT_CFG, status);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+
+	kempld_release_mutex(pld);
+
+	/* check if the watchdog was disabled */
+	if (status & KEMPLD_WDT_CFG_ENABLE)
+		return -EACCES;
+
+	return 0;
+}
+
+static int kempld_wdt_keepalive(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static int kempld_wdt_gettimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+	int bits;
+	u64 timeout;
+	u32 remainder;
+
+	if (stage == NULL)
+		return 0;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num));
+
+	kempld_release_mutex(pld);
+
+	bits = KEMPLD_WDT_STAGE_CFG_GET_PRESCALER(stage_cfg);
+	timeout = (timeout & stage->timeout_mask) * KEMPLD_PRESCALER(bits);
+	remainder = do_div(timeout, pld->pld_clock);
+
+	/* Round up the return value if necessary */
+	if ((timeout > 0) && (remainder >= (pld->pld_clock/2)))
+		timeout++;
+
+	return timeout;
+}
+
+static int kempld_wdt_setstageaction(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage,
+				 int action)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+
+	if (stage == NULL)
+		return -EINVAL;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ACTION_MASK;
+	stage_cfg |= (action & KEMPLD_WDT_STAGE_CFG_ACTION_MASK);
+	if (action == KEMPLD_WDT_ACTION_RESET)
+		stage_cfg |= KEMPLD_WDT_STAGE_CFG_ASSERT;
+	else
+		stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ASSERT;
+
+	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage,
+				 int timeout)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+	u8 prescaler;
+	u64 stage_timeout64;
+	u32 stage_timeout;
+	u32 remainder;
+
+	if (stage == NULL)
+		return -EINVAL;
+
+	prescaler = KEMPLD_WDT_PRESCALER_21BIT;
+
+	stage_timeout64 = ((u64)timeout*pld->pld_clock);
+	remainder = do_div(stage_timeout64, KEMPLD_PRESCALER(prescaler));
+	if (remainder)
+		stage_timeout64++;
+	stage_timeout = stage_timeout64 & stage->timeout_mask;
+
+	if (stage_timeout64 != (u64)stage_timeout)
+		return -EINVAL;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK;
+	stage_cfg |= KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(prescaler);
+	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
+	kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num),
+			stage_timeout);
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static void kempld_wdt_update_timeouts(struct kempld_watchdog_data *wdt)
+{
+	int pretimeout_stage;
+	int timeout_stage;
+
+	pretimeout_stage = kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+	timeout_stage = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+
+	if (pretimeout_stage)
+		wdt->pretimeout = timeout_stage;
+	else
+		wdt->pretimeout = 0;
+
+	wdt->timeout = pretimeout_stage + timeout_stage;
+
+	if (wdt->pretimeout < 0) {
+		wdt->pretimeout = WATCHDOG_DEFAULT_PRETIMEOUT;
+		dev_err(wdt->pld->dev, "failed to get valid pretimeout value\n"
+			" -> using driver default\n");
+	}
+	if (wdt->timeout < 0) {
+		wdt->timeout = WATCHDOG_DEFAULT_TIMEOUT;
+		dev_err(wdt->pld->dev, "failed to get valid timeout value\n"
+			" -> using driver default\n");
+	}
+}
+
+static int kempld_wdt_settimeout(struct kempld_watchdog_data *wdt)
+{
+	int stage_timeout;
+	int stage_pretimeout;
+	int ret;
+
+	if ((wdt->timeout <= 0) ||
+		(wdt->pretimeout < 0) ||
+		(wdt->pretimeout > wdt->timeout)) {
+		ret = -EINVAL;
+		goto err_check_values;
+	}
+
+	if ((wdt->pretimeout == 0) || (wdt->pretimeout_stage == NULL)) {
+		if (wdt->pretimeout != 0)
+			dev_warn(wdt->pld->dev,
+				 "no pretimeout stage available\n"
+				 " -> only enabling reset\n");
+		stage_pretimeout = 0;
+		stage_timeout = wdt->timeout;
+	} else {
+		stage_pretimeout = wdt->timeout - wdt->pretimeout;
+		stage_timeout = wdt->pretimeout;
+	}
+
+	if (stage_pretimeout != 0) {
+		ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
+						KEMPLD_WDT_ACTION_NMI);
+	} else if ((stage_pretimeout == 0)
+			&& (wdt->pretimeout_stage != NULL)) {
+		ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
+						KEMPLD_WDT_ACTION_NONE);
+	} else
+		ret = 0;
+	if (ret)
+		goto err_setstage;
+
+	if (stage_pretimeout != 0) {
+		ret = kempld_wdt_setstagetimeout(wdt, wdt->pretimeout_stage,
+						 stage_pretimeout);
+		if (ret)
+			goto err_setstage;
+	}
+
+	ret = kempld_wdt_setstageaction(wdt, wdt->timeout_stage,
+					KEMPLD_WDT_ACTION_RESET);
+	if (ret)
+		goto err_setstage;
+
+	ret = kempld_wdt_setstagetimeout(wdt, wdt->timeout_stage,
+					 stage_timeout);
+	if (ret)
+		goto err_setstage;
+
+	return 0;
+err_setstage:
+err_check_values:
+	return ret;
+}
+
+static ssize_t kempld_wdt_write(struct file *file, const char __user *data,
+				size_t count, loff_t *ppos)
+{
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (count) {
+		kempld_wdt_keepalive(wdt);
+
+		if (!nowayout) {
+			size_t i;
+
+			wdt->expect_close = 0;
+
+			for (i = 0; i < count; i++) {
+				char c;
+				if (get_user(c, data+i))
+					return -EFAULT;
+				if (c == 'V')
+					wdt->expect_close = 42;
+			}
+		}
+	}
+
+	return count;
+}
+
+static long kempld_wdt_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+	int options;
+	int value;
+	int ret = 0;
+
+	BUG_ON(wdt == NULL);
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user(argp, &wdt->ident, sizeof(wdt->ident)))
+			ret = -EFAULT;
+		break;
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		ret = put_user(0, p);
+		break;
+	case WDIOC_SETOPTIONS:
+		if (get_user(options, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		if (options & WDIOS_DISABLECARD)
+			ret = kempld_wdt_stop(wdt);
+		if (options & WDIOS_ENABLECARD) {
+			ret = kempld_wdt_start(wdt);
+			kempld_wdt_keepalive(wdt);
+		}
+		break;
+	case WDIOC_KEEPALIVE:
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		kempld_wdt_update_timeouts(wdt);
+		wdt->timeout = value;
+		ret = kempld_wdt_settimeout(wdt);
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETTIMEOUT:
+		value = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+		value += kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+		if (value < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(value, p);
+		break;
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		kempld_wdt_update_timeouts(wdt);
+		wdt->pretimeout = value;
+		ret = kempld_wdt_settimeout(wdt);
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETPRETIMEOUT:
+		value = kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+		if (value)
+			value = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+		if (value < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(value, p);
+		break;
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static int kempld_wdt_release(struct inode *inode, struct file *file)
+{
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (wdt->expect_close)
+		kempld_wdt_stop(wdt);
+	else {
+		dev_warn(wdt->pld->dev,
+			 "Unexpected close, not stopping watchdog!\n");
+		kempld_wdt_keepalive(wdt);
+	}
+
+	kempld_wdt->expect_close = 0;
+
+	clear_bit(0, &wdt->is_open);
+
+	return 0;
+}
+
+static int kempld_wdt_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	BUG_ON(wdt == NULL);
+
+	if (test_and_set_bit(0, &wdt->is_open))
+		return -EBUSY;
+
+	if (nowayout)
+		__module_get(THIS_MODULE);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(pld);
+
+	/* kick the watchdog if it is already enabled, otherwise start it */
+	if (status & KEMPLD_WDT_CFG_ENABLE) {
+		kempld_wdt_keepalive(wdt);
+	} else {
+		ret = kempld_wdt_settimeout(wdt);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+	}
+
+	return nonseekable_open(inode, file);
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev, "Failed to enable the watchdog timer!\n");
+	wdt->expect_close = 1;
+	kempld_wdt_release(inode, file);
+
+	return ret;
+}
+
+static void kempld_wdt_release_stages(struct kempld_watchdog_data *wdt)
+{
+	int stage;
+
+	wdt->timeout_stage = NULL;
+	wdt->pretimeout_stage = NULL;
+
+	for (stage = 0; stage < KEMPLD_WDT_MAX_STAGES; stage++) {
+		kfree(wdt->stage[stage]);
+		wdt->stage[stage] = NULL;
+	}
+}
+
+static int kempld_wdt_probe_stages(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int i, ret;
+	u32 timeout_mask;
+	struct kempld_watchdog_stage *stage;
+
+	wdt->stages = 0;
+	wdt->timeout_stage = NULL;
+	wdt->pretimeout_stage = NULL;
+
+	for (i = 0; i < KEMPLD_WDT_MAX_STAGES; i++) {
+		int j;
+		u8 index, data, data_orig;
+
+		index = KEMPLD_WDT_STAGE_TIMEOUT(i);
+		timeout_mask = ~0;
+
+		kempld_get_mutex_set_index(pld, index);
+
+		/* Probe each byte individually according to new spec revision.
+		 * Register content is restored afterwards. */
+		for (j = 0; j < 4; j++) {
+			data_orig = kempld_read8(pld, index);
+			kempld_write8(pld, index, 0x00);
+			data = kempld_read8(pld, index);
+			kempld_write8(pld, index, data_orig);
+			*(((u8 *)&timeout_mask)+j) &= data;
+			if (data != 0x0)
+				break;
+			index++;
+		}
+
+		kempld_release_mutex(pld);
+
+		if ((timeout_mask & 0xff) != 0xff) {
+			stage = kzalloc(sizeof(struct kempld_watchdog_stage),
+					GFP_KERNEL);
+			if (stage == NULL) {
+				ret = -ENOMEM;
+				goto err_alloc_stages;
+			}
+			stage->num = i;
+			stage->timeout_mask = ~timeout_mask;
+			wdt->stage[i] = stage;
+			wdt->stages++;
+
+			/* assign available stages to timeout and pretimeout */
+			if (wdt->timeout_stage == NULL) {
+				wdt->timeout_stage = stage;
+			} else if ((wdt->pretimeout_stage == NULL) &&
+				(pld->feature_mask & KEMPLD_FEATURE_BIT_NMI)) {
+				wdt->pretimeout_stage = wdt->timeout_stage;
+				wdt->timeout_stage = stage;
+			}
+		} else
+			wdt->stage[i] = NULL;
+	}
+
+	return 0;
+
+err_alloc_stages:
+	kempld_wdt_release_stages(wdt);
+
+	return ret;
+}
+
+static const struct file_operations kempld_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= kempld_wdt_write,
+	.unlocked_ioctl	= kempld_wdt_ioctl,
+	.open		= kempld_wdt_open,
+	.release	= kempld_wdt_release,
+};
+
+static struct miscdevice kempld_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &kempld_wdt_fops,
+};
+
+static int kempld_wdt_probe(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt;
+	struct kempld_device_data *pld;
+	u8 status;
+	int ret;
+
+	if (kempld_wdt != NULL) {
+		dev_err(&pdev->dev,
+			"unable to support more than one watchdog devices\n");
+		return -EMFILE;
+	}
+
+	wdt = kzalloc(sizeof(struct kempld_watchdog_data), GFP_KERNEL);
+	if (wdt == NULL) {
+		dev_err(&pdev->dev, "unable to get memory for device data\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	pld = dev_get_drvdata(pdev->dev.parent);
+	wdt->pld = pld;
+
+	platform_set_drvdata(pdev, wdt);
+
+	strncpy(wdt->ident.identity, "KEMPLD Watchdog",
+		sizeof(wdt->ident.identity));
+
+	/* watchdog firmware version is identical to the CPLD version */
+	wdt->ident.firmware_version = (pld->info.major<<24)
+		| (pld->info.minor<<16) | pld->info.buildnr;
+
+	/* probe how many usable stages we have */
+	ret = kempld_wdt_probe_stages(wdt);
+	if (ret)
+		goto err_probe_stages;
+
+	/* get initial watchdog status */
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(wdt->pld);
+
+	/* check if the watchdog is already locked and enable the nowayout
+	 * option in that case */
+	if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
+			KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
+		if (!nowayout)
+			dev_warn(wdt->pld->dev,
+				 "Forcing nowayout - watchdog lock enabled!\n");
+		nowayout = 1;
+	}
+
+	/* set default values for the case we start the watchdog or change
+	 * the configuration */
+	wdt->timeout = WATCHDOG_DEFAULT_TIMEOUT;
+	wdt->pretimeout = WATCHDOG_DEFAULT_PRETIMEOUT;
+
+	/* check if watchdog is enabled */
+	if (status & KEMPLD_WDT_CFG_ENABLE) {
+		/* Get current watchdog settings */
+		kempld_wdt_update_timeouts(wdt);
+
+		dev_info(wdt->pld->dev, "Watchdog is already enabled:\n"
+			 "%d s timeout and %d s pretimeout!\n",
+			 wdt->timeout, wdt->pretimeout);
+	}
+
+	/* update the timeout settings if requested by module parameters */
+	if (timeout > 0)
+		wdt->timeout = timeout;
+	if (pretimeout >= 0)
+		wdt->pretimeout = pretimeout;
+
+	dev_info(wdt->pld->dev, "watchdog will be set on (re)start\n"
+		 "new settings: %d s timeout and %d s pretimeout\n",
+		 wdt->timeout, wdt->pretimeout);
+
+	wdt->ident.options = WDIOF_KEEPALIVEPING;
+	if ((wdt->timeout_stage) && !(status & KEMPLD_WDT_CFG_GLOBAL_LOCK))
+		wdt->ident.options |= WDIOF_SETTIMEOUT;
+	if (wdt->pretimeout_stage)
+		wdt->ident.options |= WDIOF_PRETIMEOUT;
+	if (!nowayout)
+		wdt->ident.options |= WDIOF_MAGICCLOSE;
+
+	kempld_wdt = wdt;
+
+	ret = misc_register(&kempld_wdt_miscdev);
+	if (ret)
+		goto err_misc_register;
+
+	dev_info(wdt->pld->dev,
+		 "%d stage watchdog initialized, pretimeout %ssupported\n",
+		 wdt->stages, wdt->pretimeout_stage ? "" : "not ");
+
+	return 0;
+
+err_probe_stages:
+err_misc_register:
+	kfree(kempld_wdt);
+	kempld_wdt = NULL;
+err_alloc_dev_data:
+	return ret;
+}
+
+static void kempld_wdt_shutdown(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	if (wdt != NULL) {
+		if (!nowayout)
+			kempld_wdt_stop(wdt);
+		else
+			kempld_wdt_keepalive(wdt);
+	}
+}
+
+static int kempld_wdt_remove(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	kempld_wdt_shutdown(pdev);
+
+	misc_deregister(&kempld_wdt_miscdev);
+
+	kempld_wdt_release_stages(wdt);
+
+	kfree(wdt);
+	kempld_wdt = NULL;
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wdt_pm_status_store;
+
+/* Disable watchdog if it is active during suspend */
+static int kempld_wdt_suspend(struct platform_device *pdev,
+				pm_message_t message)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+	struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	wdt_pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(pld);
+
+	kempld_wdt_update_timeouts(wdt);
+
+	if (wdt_pm_status_store & KEMPLD_WDT_CFG_ENABLE)
+		kempld_wdt_shutdown(pdev);
+
+	return 0;
+}
+
+/* Enable watchdog and configure it if necessary */
+static int kempld_wdt_resume(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+	int ret;
+
+	/* if watchdog was stopped before suspend be sure it gets disabled
+	 * again, for the case BIOS has enabled it during resume */
+	if (wdt_pm_status_store & KEMPLD_WDT_CFG_ENABLE) {
+		ret = kempld_wdt_settimeout(wdt);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+
+		dev_info(wdt->pld->dev, "Resuming watchdog operation:\n"
+			 "%d s timeout and %d s pretimeout\n", wdt->timeout,
+			 wdt->pretimeout);
+	} else
+		kempld_wdt_shutdown(pdev);
+
+	return 0;
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev,
+		"Failed to reenable the watchdog timer after resume!\n");
+
+	return ret;
+}
+#else
+#define kempld_wdt_suspend	NULL
+#define kempld_wdt_resume	NULL
+#endif
+
+static struct platform_driver kempld_wdt_driver = {
+	.driver = {
+		.name = "kempld-wdt",
+		.owner = THIS_MODULE,
+	},
+	.probe = kempld_wdt_probe,
+	.remove = kempld_wdt_remove,
+	.shutdown = kempld_wdt_shutdown,
+	.suspend = kempld_wdt_suspend,
+	.resume = kempld_wdt_resume,
+};
+
+static int __init kempld_wdt_init(void)
+{
+	return platform_driver_register(&kempld_wdt_driver);
+}
+
+static void __exit kempld_wdt_exit(void)
+{
+	platform_driver_unregister(&kempld_wdt_driver);
+}
+
+module_init(kempld_wdt_init);
+module_exit(kempld_wdt_exit);
+
+MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/kempld_wdt.h b/drivers/watchdog/kempld_wdt.h
new file mode 100644
index 0000000..80f68f6
--- /dev/null
+++ b/drivers/watchdog/kempld_wdt.h
@@ -0,0 +1,75 @@
+/*
+ *  kempld_wdt.h - Kontron PLD watchdog driver definitions
+ *
+ *  Copyright (c) 2010-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner@kontron.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _KEMPLD_WDT_H_
+#define _KEMPLD_WDT_H_
+
+/* watchdog register definitions */
+#define KEMPLD_WDT_KICK			0x16
+#define KEMPLD_WDT_CFG			0x17
+#define		KEMPLD_WDT_CFG_STAGE_TIMEOUT_OCCURED(x)	(1<<x)
+#define		KEMPLD_WDT_CFG_ENABLE_LOCK		0x8
+#define		KEMPLD_WDT_CFG_ENABLE			0x10
+#define		KEMPLD_WDT_CFG_AUTO_RELOAD		0x40
+#define		KEMPLD_WDT_CFG_GLOBAL_LOCK		0x80
+#define KEMPLD_WDT_STAGE_CFG(x)		(0x18+x)
+#define		KEMPLD_WDT_STAGE_CFG_ACTION_MASK	0x7
+#define		KEMPLD_WDT_STAGE_CFG_GET_ACTION(x)	(x & 0x7)
+#define		KEMPLD_WDT_STAGE_CFG_ASSERT		(1<<3)
+#define		KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK	0x30
+#define		KEMPLD_WDT_STAGE_CFG_GET_PRESCALER(x)	((x & 0x30)>>4)
+#define		KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(x)	((x & 0x30)<<4)
+#define KEMPLD_WDT_STAGE_TIMEOUT(x)	(0x1b+x*4)
+#define KEMPLD_WDT_MAX_STAGES		3
+
+#define	KEMPLD_WDT_ACTION_NONE		0x0
+#define	KEMPLD_WDT_ACTION_RESET		0x1
+#define	KEMPLD_WDT_ACTION_NMI		0x2
+#define	KEMPLD_WDT_ACTION_SMI		0x3
+#define	KEMPLD_WDT_ACTION_SCI		0x4
+#define	KEMPLD_WDT_ACTION_DELAY		0x5
+
+#define	KEMPLD_WDT_PRESCALER_21BIT	0x0
+#define	KEMPLD_WDT_PRESCALER_17BIT	0x1
+#define	KEMPLD_WDT_PRESCALER_12BIT	0x2
+
+const int kempld_prescaler_bits[] = { 21, 17, 12 };
+#define KEMPLD_PRESCALER(x)	(0xffffffff>>(32-kempld_prescaler_bits[x]))
+
+
+struct kempld_watchdog_stage {
+	int	num;
+	u32	timeout_mask;
+};
+
+struct kempld_watchdog_data {
+	int				timeout;
+	int				pretimeout;
+	unsigned long			is_open;
+	unsigned long			expect_close;
+	int				stages;
+	struct kempld_watchdog_stage	*timeout_stage;
+	struct kempld_watchdog_stage	*pretimeout_stage;
+	struct kempld_device_data	*pld;
+	struct watchdog_info		ident;
+	struct kempld_watchdog_stage	*stage[KEMPLD_WDT_MAX_STAGES];
+};
+
+#endif /* _KEMPLD_WDT_H_ */
-- 
1.7.9.5


WARNING: multiple messages have this Message-ID (diff)
From: Kevin Strasser <kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
To: linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Cc: Michael Brunner
	<michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>,
	Samuel Ortiz <sameo-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>,
	Wolfram Sang <wsa-z923LK4zBo2bacvFa/9K2g@public.gmane.org>,
	Ben Dooks <ben-linux-elnMNo+KYs3YtjvyW6yDsg@public.gmane.org>,
	linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Grant Likely
	<grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>,
	Linus Walleij
	<linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>,
	Wim Van Sebroeck <wim-IQzOog9fTRqzQB+pC5nmwQ@public.gmane.org>,
	linux-watchdog-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	Darren Hart <dvhart-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>,
	Michael Brunner <mibru-Mmb7MZpHnFY@public.gmane.org>,
	Greg Kroah-Hartman
	<gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r@public.gmane.org>,
	Kevin Strasser
	<kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
Subject: [PATCH 4/4] watchdog: Kontron PLD watchdog timer
Date: Mon,  8 Apr 2013 10:15:21 -0700	[thread overview]
Message-ID: <1365441321-21952-4-git-send-email-kevin.strasser@linux.intel.com> (raw)
In-Reply-To: <1365441321-21952-1-git-send-email-kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>

From: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>

Add watchdog timer support for the on-board PLD found on some Kontron
embedded modules.

Signed-off-by: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Kevin Strasser <kevin.strasser-VuQAYsv1563Yd54FQh9/CA@public.gmane.org>
---
 drivers/watchdog/Kconfig           |   20 +
 drivers/watchdog/Makefile          |    2 +
 drivers/watchdog/kempld_now1_wdt.c |  602 +++++++++++++++++++++++++++
 drivers/watchdog/kempld_wdt.c      |  796 ++++++++++++++++++++++++++++++++++++
 drivers/watchdog/kempld_wdt.h      |   75 ++++
 5 files changed, 1495 insertions(+)
 create mode 100644 drivers/watchdog/kempld_now1_wdt.c
 create mode 100644 drivers/watchdog/kempld_wdt.c
 create mode 100644 drivers/watchdog/kempld_wdt.h

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 9fcc70c..9ac71ca 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -687,6 +687,26 @@ config HP_WATCHDOG
 	  To compile this driver as a module, choose M here: the module will be
 	  called hpwdt.
 
+config KEMPLD_WDT
+	tristate "Kontron COM watchdog"
+	depends on MFD_KEMPLD
+	help
+	  Support for the PLD watchdog on some Kontron ETX and COMexpress
+	  (ETXexpress) modules
+
+	  This driver can also be built as a module. If so, the module will be
+	  called kempld_wdt.
+
+config KEMPLD_NOW1_WDT
+	tristate "Kontron COMe-mSP1 (nanoETXexpress-SP) watchdog"
+	depends on MFD_KEMPLD
+	help
+	  Support for the PLD watchdog on the Kontron COMe-mSP1
+	  (nanoETXexpress-SP) module.
+
+	  This driver can also be built as a module. If so, the module will
+	  be called kempld_now1_wdt.
+
 config HPWDT_NMI_DECODING
 	bool "NMI decoding support for the HP ProLiant iLO2+ Hardware Watchdog Timer"
 	depends on HP_WATCHDOG
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index a300b94..a029930 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -90,6 +90,8 @@ endif
 obj-$(CONFIG_IT8712F_WDT) += it8712f_wdt.o
 obj-$(CONFIG_IT87_WDT) += it87_wdt.o
 obj-$(CONFIG_HP_WATCHDOG) += hpwdt.o
+obj-$(CONFIG_KEMPLD_WDT) += kempld_wdt.o
+obj-$(COnFIG_KEMPLD_NOW1_WDT) += kempld_now1_wdt.o
 obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
 obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
 obj-$(CONFIG_PC87413_WDT) += pc87413_wdt.o
diff --git a/drivers/watchdog/kempld_now1_wdt.c b/drivers/watchdog/kempld_now1_wdt.c
new file mode 100644
index 0000000..19b7272
--- /dev/null
+++ b/drivers/watchdog/kempld_now1_wdt.c
@@ -0,0 +1,602 @@
+/*
+ *  kempld_now1_wdt.c - Kontron PLD watchdog driver for COMe-mSP1
+ *
+ *  Copyright (c) 2011-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>
+ *
+ *  Note: The watchdog of the COMe-mSP1 (nanoETXexpress-SP) has one stage and
+ *        only supports predefined watchdog timeout values.
+ *
+ *        The supported timeouts are:
+ *              1 s, 5 s, 10 s, 30 s, 1 min, 5 min, 10 min, 15 min
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/kempld.h>
+
+#include "kempld_wdt.h"
+
+#define WATCHDOG_TIMEOUT 30
+static int timeout = WATCHDOG_TIMEOUT;
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+		 "Watchdog timeout in seconds. (1, 5, 10, 30, 60, 300, 600, "
+		 "900, default=" __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+		 "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+/* nanoETXexpress-SP watchdog register definitions */
+#define KEMPLD_WDT_NOW1					0xA2
+#define		KEMPLD_WDT_NOW1_KICK_MASK		0x80
+#define		KEMPLD_WDT_NOW1_ENABLE_MASK		0x40
+#define		KEMPLD_WDT_NOW1_TIMEOUT_MASK		0x1f
+#define			KEMPLD_WDT_NOW1_TIMEOUT_1SEC	0x00
+#define			KEMPLD_WDT_NOW1_TIMEOUT_5SEC	0x01
+#define			KEMPLD_WDT_NOW1_TIMEOUT_10SEC	0x02
+#define			KEMPLD_WDT_NOW1_TIMEOUT_30SEC	0x03
+#define			KEMPLD_WDT_NOW1_TIMEOUT_1MIN	0x10
+#define			KEMPLD_WDT_NOW1_TIMEOUT_5MIN	0x11
+#define			KEMPLD_WDT_NOW1_TIMEOUT_10MIN	0x12
+#define			KEMPLD_WDT_NOW1_TIMEOUT_15MIN	0x13
+
+/* delay in us necessary due to clock domain sync */
+#define		KEMPLD_WDT_NOW1_SYNC_DELAY		31
+
+static struct kempld_watchdog_data *kempld_now1_wdt;
+
+static int kempld_now1_wdt_read_supported;
+static int kempld_now1_wdt_reg_cache;
+
+static int kempld_now1_wdt_start(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	if (kempld_now1_wdt_read_supported)
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	wdt_reg |= KEMPLD_WDT_NOW1_ENABLE_MASK;
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	if (kempld_now1_wdt_read_supported) {
+		/* read out the register again to check if everything worked */
+		kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_release_mutex(pld);
+
+		/* check if the watchdog was disabled */
+		if (!(wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK))
+			return -EACCES;
+	}
+
+	return 0;
+}
+
+static int kempld_now1_wdt_stop(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	wdt_reg &= ~KEMPLD_WDT_NOW1_ENABLE_MASK;
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	if (kempld_now1_wdt_read_supported) {
+		/* read out the register again to check if everything worked */
+		kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_release_mutex(pld);
+
+		/* check if the watchdog was disabled */
+		if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK)
+			return -EACCES;
+	}
+
+	return 0;
+}
+
+static int kempld_now1_wdt_keepalive(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+	if (kempld_now1_wdt_read_supported) {
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = kempld_now1_wdt_reg_cache;
+		/* write the state again to be sure the trigger register has
+		 * the right level */
+		kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+	}
+
+	if (wdt_reg & KEMPLD_WDT_NOW1_KICK_MASK)
+		wdt_reg &= ~KEMPLD_WDT_NOW1_KICK_MASK;
+	else
+		wdt_reg |= KEMPLD_WDT_NOW1_KICK_MASK;
+
+	udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+	kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+
+	kempld_now1_wdt_reg_cache = wdt_reg;
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+
+static int kempld_now1_wdt_settimeout(struct kempld_watchdog_data *wdt,
+					int check_only)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+	int ret = 0;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+
+	wdt_reg &= ~KEMPLD_WDT_NOW1_TIMEOUT_MASK;
+
+	switch (wdt->timeout) {
+	case 1:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_1SEC;
+		break;
+	case 5:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_5SEC;
+		break;
+	case 10:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_10SEC;
+		break;
+	case 30:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_30SEC;
+		break;
+	case 60:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_1MIN;
+		break;
+	case 300:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_5MIN;
+		break;
+	case 600:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_10MIN;
+		break;
+	case 900:
+		wdt_reg |= KEMPLD_WDT_NOW1_TIMEOUT_15MIN;
+		break;
+	default:
+		ret = -EINVAL;
+		dev_err(wdt->pld->dev,
+			"Invalid timeout value given!\n");
+	}
+
+	if (!check_only) {
+		if (ret == 0) {
+			udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+
+			kempld_write8(pld, KEMPLD_WDT_NOW1, wdt_reg);
+		}
+	}
+
+	kempld_now1_wdt_reg_cache = wdt_reg;
+
+	kempld_release_mutex(pld);
+
+	return ret;
+}
+
+static int kempld_now1_wdt_gettimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int wdt_reg;
+	int timeout;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+		kempld_now1_wdt_reg_cache = wdt_reg;
+	} else
+		wdt_reg = kempld_now1_wdt_reg_cache;
+
+	kempld_release_mutex(pld);
+
+	switch (wdt_reg&KEMPLD_WDT_NOW1_TIMEOUT_MASK) {
+	case KEMPLD_WDT_NOW1_TIMEOUT_1SEC:
+		timeout = 1;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_5SEC:
+		timeout = 5;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_10SEC:
+		timeout = 10;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_30SEC:
+		timeout = 30;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_1MIN:
+		timeout = 60;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_5MIN:
+		timeout = 300;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_10MIN:
+		timeout = 600;
+		break;
+	case KEMPLD_WDT_NOW1_TIMEOUT_15MIN:
+		timeout = 900;
+		break;
+	default:
+		timeout = -ERANGE;
+	}
+
+	return timeout;
+}
+
+static ssize_t kempld_now1_wdt_write(struct file *file, const char __user
+					*data, size_t count, loff_t *ppos)
+{
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (count) {
+		kempld_now1_wdt_keepalive(wdt);
+
+		if (!nowayout) {
+			size_t i;
+
+			wdt->expect_close = 0;
+
+			for (i = 0; i < count; i++) {
+				char c;
+				if (get_user(c, data+i))
+					return -EFAULT;
+				if (c == 'V')
+					wdt->expect_close = 42;
+			}
+		}
+	}
+
+	return count;
+}
+
+static long kempld_now1_wdt_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+	int options;
+	int value;
+	int ret = 0;
+
+	BUG_ON(wdt == NULL);
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user(argp, &wdt->ident, sizeof(wdt->ident)))
+			ret = -EFAULT;
+		break;
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		ret = put_user(0, p);
+		break;
+	case WDIOC_SETOPTIONS:
+		if (get_user(options, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		if (options & WDIOS_DISABLECARD)
+			ret = kempld_now1_wdt_stop(wdt);
+		if (options & WDIOS_ENABLECARD) {
+			ret = kempld_now1_wdt_start(wdt);
+			kempld_now1_wdt_keepalive(wdt);
+		}
+		break;
+	case WDIOC_KEEPALIVE:
+		kempld_now1_wdt_keepalive(wdt);
+		break;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		wdt->timeout = value;
+		ret = kempld_now1_wdt_settimeout(wdt, 0);
+		kempld_now1_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETTIMEOUT:
+		value = kempld_now1_wdt_gettimeout(wdt, wdt->timeout_stage);
+		if (timeout < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(timeout, p);
+		break;
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static int kempld_now1_wdt_release(struct inode *inode, struct file *file)
+{
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (wdt->expect_close)
+		kempld_now1_wdt_stop(wdt);
+	else {
+		dev_warn(wdt->pld->dev,
+			 "Unexpected close, not stopping watchdog!\n");
+		kempld_now1_wdt_keepalive(wdt);
+	}
+
+	kempld_now1_wdt->expect_close = 0;
+
+	clear_bit(0, &wdt->is_open);
+
+	return 0;
+}
+
+static int kempld_now1_wdt_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	struct kempld_watchdog_data *wdt = kempld_now1_wdt;
+	struct kempld_device_data *pld = wdt->pld;
+	u8 wdt_reg;
+
+	BUG_ON(wdt == NULL);
+
+	if (test_and_set_bit(0, &wdt->is_open))
+		return -EBUSY;
+
+	if (nowayout)
+		__module_get(THIS_MODULE);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = kempld_now1_wdt_reg_cache;
+	}
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(pld);
+
+	/* kick the watchdog if it is already enabled, otherwise start it */
+	if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK) {
+		kempld_now1_wdt_keepalive(wdt);
+	} else {
+		ret = kempld_now1_wdt_settimeout(wdt, 0);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_now1_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+	}
+
+	return nonseekable_open(inode, file);
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev, "Failed to enable the watchdog timer!\n");
+	wdt->expect_close = 1;
+	kempld_now1_wdt_release(inode, file);
+
+	return ret;
+}
+
+static const struct file_operations kempld_now1_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= kempld_now1_wdt_write,
+	.unlocked_ioctl	= kempld_now1_wdt_ioctl,
+	.open		= kempld_now1_wdt_open,
+	.release	= kempld_now1_wdt_release,
+};
+
+static struct miscdevice kempld_now1_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &kempld_now1_wdt_fops,
+};
+
+static int kempld_now1_wdt_probe(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt;
+	struct kempld_device_data *pld;
+	u8 wdt_reg;
+	int ret;
+
+	if (kempld_now1_wdt != NULL) {
+		dev_err(&pdev->dev,
+			"unable to support more than one watchdog devices\n");
+		return -EMFILE;
+	}
+
+	wdt = kzalloc(sizeof(struct kempld_watchdog_data), GFP_KERNEL);
+	if (wdt == NULL) {
+		dev_err(&pdev->dev, "unable to get memory for device data\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	wdt->timeout_stage = kzalloc(sizeof(struct kempld_watchdog_stage),
+					GFP_KERNEL);
+	if (wdt->timeout_stage == NULL) {
+		dev_err(&pdev->dev,
+			"unable to get memory for watchdog stage\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	wdt->stages = 1;
+	wdt->stage[0] = wdt->timeout_stage;
+
+	pld = dev_get_drvdata(pdev->dev.parent);
+	wdt->pld = pld;
+
+	platform_set_drvdata(pdev, wdt);
+
+	strncpy(wdt->ident.identity, "nanoETXexpress-SP Watchdog",
+		sizeof(wdt->ident.identity));
+
+	/* set default values for the case we start the watchdog or change
+	 * the configuration */
+	wdt->timeout = timeout;
+
+	/* use settimeout to check if the timeout parameter is valid */
+	ret = kempld_now1_wdt_settimeout(wdt, 1);
+	if (ret)
+		goto err_check_timeout;
+	wdt->ident.firmware_version = (pld->info.major<<8) + pld->info.minor;
+	if (pld->info.major > 67)
+		kempld_now1_wdt_read_supported = 1;
+	else
+		dev_info(wdt->pld->dev,
+			 "Watchdog revision does not support read - "
+			 "unable to get watchdog state!\n");
+
+	/* get initial watchdog status */
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_NOW1);
+	if (kempld_now1_wdt_read_supported) {
+		udelay(KEMPLD_WDT_NOW1_SYNC_DELAY);
+		wdt_reg = kempld_read8(pld, KEMPLD_WDT_NOW1);
+	} else {
+		wdt_reg = 0x0;
+	}
+	kempld_now1_wdt_reg_cache = wdt_reg;
+	kempld_release_mutex(wdt->pld);
+
+	/* check if watchdog is enabled */
+	if (wdt_reg & KEMPLD_WDT_NOW1_ENABLE_MASK)
+		dev_info(wdt->pld->dev, "Watchdog is already enabled!\n");
+
+	wdt->ident.options = WDIOF_KEEPALIVEPING;
+	wdt->ident.options |= WDIOF_SETTIMEOUT;
+	if (!nowayout)
+		wdt->ident.options |= WDIOF_MAGICCLOSE;
+
+	kempld_now1_wdt = wdt;
+
+	ret = misc_register(&kempld_now1_wdt_miscdev);
+	if (ret)
+		goto err_misc_register;
+
+	dev_info(wdt->pld->dev, "watchdog initialized\n");
+
+	return 0;
+
+err_misc_register:
+	kfree(kempld_now1_wdt);
+	kempld_now1_wdt = NULL;
+err_check_timeout:
+err_alloc_dev_data:
+	return ret;
+}
+
+static int kempld_now1_wdt_remove(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_now1_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	if (wdt != NULL) {
+		if (!nowayout)
+			kempld_now1_wdt_stop(wdt);
+		else
+			kempld_now1_wdt_keepalive(wdt);
+	}
+
+	misc_deregister(&kempld_now1_wdt_miscdev);
+
+	kfree(wdt);
+	kempld_now1_wdt = NULL;
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver kempld_now1_wdt_driver = {
+	.driver = {
+		.name = "kempld_now1-wdt",
+		.owner = THIS_MODULE,
+	},
+	.probe = kempld_now1_wdt_probe,
+	.remove = kempld_now1_wdt_remove,
+};
+
+static int __init kempld_now1_wdt_init(void)
+{
+	return platform_driver_register(&kempld_now1_wdt_driver);
+}
+
+static void __exit kempld_now1_wdt_exit(void)
+{
+	platform_driver_unregister(&kempld_now1_wdt_driver);
+}
+
+module_init(kempld_now1_wdt_init);
+module_exit(kempld_now1_wdt_exit);
+
+MODULE_DESCRIPTION("KEM PLD nanoETXexpress-SP Watchdog Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/kempld_wdt.c b/drivers/watchdog/kempld_wdt.c
new file mode 100644
index 0000000..bc150e5
--- /dev/null
+++ b/drivers/watchdog/kempld_wdt.c
@@ -0,0 +1,796 @@
+/*
+ *  kempld_wdt.c - Kontron PLD watchdog driver
+ *
+ *  Copyright (c) 2010-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>
+ *
+ *  Note: From the PLD watchdog point of view timeout and pretimeout are
+ *        defined differently than in the kernel.
+ *        First the pretimeout stage runs out before the timeout stage gets
+ *        active. This has to be kept in mind.
+ *
+ *  Kernel/API:                     P-----| pretimeout
+ *                |-----------------------T timeout
+ *  Watchdog:     |-----------------P       pretimeout_stage
+ *                                  |-----T timeout_stage
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/kempld.h>
+
+#include "kempld_wdt.h"
+
+#define WATCHDOG_DEFAULT_TIMEOUT 20
+#define WATCHDOG_DEFAULT_PRETIMEOUT 0
+static int timeout = -1;
+static int pretimeout = -1;
+/* The maximum timeout values have to be probed */
+module_param(timeout, int, 0);
+MODULE_PARM_DESC(timeout,
+		 "Watchdog timeout in seconds. (>0, default="
+		__MODULE_STRING(WATCHDOG_DEFAULT_TIMEOUT) ")");
+module_param(pretimeout, int, 0);
+MODULE_PARM_DESC(pretimeout,
+		 "Watchdog pretimeout in seconds. (>=0, default="
+		__MODULE_STRING(WATCHDOG_DEFAULT_PRETIMEOUT) ")");
+
+static bool nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, bool, 0);
+MODULE_PARM_DESC(nowayout,
+		 "Watchdog cannot be stopped once started (default="
+		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+static struct kempld_watchdog_data *kempld_wdt;
+
+static int kempld_wdt_start(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	status |= KEMPLD_WDT_CFG_ENABLE;
+	kempld_write8(pld, KEMPLD_WDT_CFG, status);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+
+	kempld_release_mutex(pld);
+
+	/* check if the watchdog was enabled */
+	if (!(status & KEMPLD_WDT_CFG_ENABLE))
+		return -EACCES;
+
+	return 0;
+}
+
+static int kempld_wdt_stop(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	status &= ~KEMPLD_WDT_CFG_ENABLE;
+	kempld_write8(pld, KEMPLD_WDT_CFG, status);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+
+	kempld_release_mutex(pld);
+
+	/* check if the watchdog was disabled */
+	if (status & KEMPLD_WDT_CFG_ENABLE)
+		return -EACCES;
+
+	return 0;
+}
+
+static int kempld_wdt_keepalive(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+
+	kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static int kempld_wdt_gettimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+	int bits;
+	u64 timeout;
+	u32 remainder;
+
+	if (stage == NULL)
+		return 0;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num));
+
+	kempld_release_mutex(pld);
+
+	bits = KEMPLD_WDT_STAGE_CFG_GET_PRESCALER(stage_cfg);
+	timeout = (timeout & stage->timeout_mask) * KEMPLD_PRESCALER(bits);
+	remainder = do_div(timeout, pld->pld_clock);
+
+	/* Round up the return value if necessary */
+	if ((timeout > 0) && (remainder >= (pld->pld_clock/2)))
+		timeout++;
+
+	return timeout;
+}
+
+static int kempld_wdt_setstageaction(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage,
+				 int action)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+
+	if (stage == NULL)
+		return -EINVAL;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ACTION_MASK;
+	stage_cfg |= (action & KEMPLD_WDT_STAGE_CFG_ACTION_MASK);
+	if (action == KEMPLD_WDT_ACTION_RESET)
+		stage_cfg |= KEMPLD_WDT_STAGE_CFG_ASSERT;
+	else
+		stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_ASSERT;
+
+	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static int kempld_wdt_setstagetimeout(struct kempld_watchdog_data *wdt,
+				 struct kempld_watchdog_stage *stage,
+				 int timeout)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	u8 stage_cfg;
+	u8 prescaler;
+	u64 stage_timeout64;
+	u32 stage_timeout;
+	u32 remainder;
+
+	if (stage == NULL)
+		return -EINVAL;
+
+	prescaler = KEMPLD_WDT_PRESCALER_21BIT;
+
+	stage_timeout64 = ((u64)timeout*pld->pld_clock);
+	remainder = do_div(stage_timeout64, KEMPLD_PRESCALER(prescaler));
+	if (remainder)
+		stage_timeout64++;
+	stage_timeout = stage_timeout64 & stage->timeout_mask;
+
+	if (stage_timeout64 != (u64)stage_timeout)
+		return -EINVAL;
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+
+	stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->num));
+	stage_cfg &= ~KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK;
+	stage_cfg |= KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(prescaler);
+	kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->num), stage_cfg);
+	kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->num),
+			stage_timeout);
+
+	kempld_release_mutex(pld);
+
+	return 0;
+}
+
+static void kempld_wdt_update_timeouts(struct kempld_watchdog_data *wdt)
+{
+	int pretimeout_stage;
+	int timeout_stage;
+
+	pretimeout_stage = kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+	timeout_stage = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+
+	if (pretimeout_stage)
+		wdt->pretimeout = timeout_stage;
+	else
+		wdt->pretimeout = 0;
+
+	wdt->timeout = pretimeout_stage + timeout_stage;
+
+	if (wdt->pretimeout < 0) {
+		wdt->pretimeout = WATCHDOG_DEFAULT_PRETIMEOUT;
+		dev_err(wdt->pld->dev, "failed to get valid pretimeout value\n"
+			" -> using driver default\n");
+	}
+	if (wdt->timeout < 0) {
+		wdt->timeout = WATCHDOG_DEFAULT_TIMEOUT;
+		dev_err(wdt->pld->dev, "failed to get valid timeout value\n"
+			" -> using driver default\n");
+	}
+}
+
+static int kempld_wdt_settimeout(struct kempld_watchdog_data *wdt)
+{
+	int stage_timeout;
+	int stage_pretimeout;
+	int ret;
+
+	if ((wdt->timeout <= 0) ||
+		(wdt->pretimeout < 0) ||
+		(wdt->pretimeout > wdt->timeout)) {
+		ret = -EINVAL;
+		goto err_check_values;
+	}
+
+	if ((wdt->pretimeout == 0) || (wdt->pretimeout_stage == NULL)) {
+		if (wdt->pretimeout != 0)
+			dev_warn(wdt->pld->dev,
+				 "no pretimeout stage available\n"
+				 " -> only enabling reset\n");
+		stage_pretimeout = 0;
+		stage_timeout = wdt->timeout;
+	} else {
+		stage_pretimeout = wdt->timeout - wdt->pretimeout;
+		stage_timeout = wdt->pretimeout;
+	}
+
+	if (stage_pretimeout != 0) {
+		ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
+						KEMPLD_WDT_ACTION_NMI);
+	} else if ((stage_pretimeout == 0)
+			&& (wdt->pretimeout_stage != NULL)) {
+		ret = kempld_wdt_setstageaction(wdt, wdt->pretimeout_stage,
+						KEMPLD_WDT_ACTION_NONE);
+	} else
+		ret = 0;
+	if (ret)
+		goto err_setstage;
+
+	if (stage_pretimeout != 0) {
+		ret = kempld_wdt_setstagetimeout(wdt, wdt->pretimeout_stage,
+						 stage_pretimeout);
+		if (ret)
+			goto err_setstage;
+	}
+
+	ret = kempld_wdt_setstageaction(wdt, wdt->timeout_stage,
+					KEMPLD_WDT_ACTION_RESET);
+	if (ret)
+		goto err_setstage;
+
+	ret = kempld_wdt_setstagetimeout(wdt, wdt->timeout_stage,
+					 stage_timeout);
+	if (ret)
+		goto err_setstage;
+
+	return 0;
+err_setstage:
+err_check_values:
+	return ret;
+}
+
+static ssize_t kempld_wdt_write(struct file *file, const char __user *data,
+				size_t count, loff_t *ppos)
+{
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (count) {
+		kempld_wdt_keepalive(wdt);
+
+		if (!nowayout) {
+			size_t i;
+
+			wdt->expect_close = 0;
+
+			for (i = 0; i < count; i++) {
+				char c;
+				if (get_user(c, data+i))
+					return -EFAULT;
+				if (c == 'V')
+					wdt->expect_close = 42;
+			}
+		}
+	}
+
+	return count;
+}
+
+static long kempld_wdt_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	void __user *argp = (void __user *)arg;
+	int __user *p = argp;
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+	int options;
+	int value;
+	int ret = 0;
+
+	BUG_ON(wdt == NULL);
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user(argp, &wdt->ident, sizeof(wdt->ident)))
+			ret = -EFAULT;
+		break;
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		ret = put_user(0, p);
+		break;
+	case WDIOC_SETOPTIONS:
+		if (get_user(options, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		if (options & WDIOS_DISABLECARD)
+			ret = kempld_wdt_stop(wdt);
+		if (options & WDIOS_ENABLECARD) {
+			ret = kempld_wdt_start(wdt);
+			kempld_wdt_keepalive(wdt);
+		}
+		break;
+	case WDIOC_KEEPALIVE:
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		kempld_wdt_update_timeouts(wdt);
+		wdt->timeout = value;
+		ret = kempld_wdt_settimeout(wdt);
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETTIMEOUT:
+		value = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+		value += kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+		if (value < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(value, p);
+		break;
+	case WDIOC_SETPRETIMEOUT:
+		if (get_user(value, p)) {
+			ret = -EFAULT;
+			break;
+		}
+		kempld_wdt_update_timeouts(wdt);
+		wdt->pretimeout = value;
+		ret = kempld_wdt_settimeout(wdt);
+		kempld_wdt_keepalive(wdt);
+		break;
+	case WDIOC_GETPRETIMEOUT:
+		value = kempld_wdt_gettimeout(wdt, wdt->pretimeout_stage);
+		if (value)
+			value = kempld_wdt_gettimeout(wdt, wdt->timeout_stage);
+		if (value < 0)
+			ret = ERANGE;
+		else
+			ret = put_user(value, p);
+		break;
+	default:
+		ret = -ENOTTY;
+	}
+
+	return ret;
+}
+
+static int kempld_wdt_release(struct inode *inode, struct file *file)
+{
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+
+	BUG_ON(wdt == NULL);
+
+	if (wdt->expect_close)
+		kempld_wdt_stop(wdt);
+	else {
+		dev_warn(wdt->pld->dev,
+			 "Unexpected close, not stopping watchdog!\n");
+		kempld_wdt_keepalive(wdt);
+	}
+
+	kempld_wdt->expect_close = 0;
+
+	clear_bit(0, &wdt->is_open);
+
+	return 0;
+}
+
+static int kempld_wdt_open(struct inode *inode, struct file *file)
+{
+	int ret;
+	struct kempld_watchdog_data *wdt = kempld_wdt;
+	struct kempld_device_data *pld = wdt->pld;
+	u8 status;
+
+	BUG_ON(wdt == NULL);
+
+	if (test_and_set_bit(0, &wdt->is_open))
+		return -EBUSY;
+
+	if (nowayout)
+		__module_get(THIS_MODULE);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(pld);
+
+	/* kick the watchdog if it is already enabled, otherwise start it */
+	if (status & KEMPLD_WDT_CFG_ENABLE) {
+		kempld_wdt_keepalive(wdt);
+	} else {
+		ret = kempld_wdt_settimeout(wdt);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+	}
+
+	return nonseekable_open(inode, file);
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev, "Failed to enable the watchdog timer!\n");
+	wdt->expect_close = 1;
+	kempld_wdt_release(inode, file);
+
+	return ret;
+}
+
+static void kempld_wdt_release_stages(struct kempld_watchdog_data *wdt)
+{
+	int stage;
+
+	wdt->timeout_stage = NULL;
+	wdt->pretimeout_stage = NULL;
+
+	for (stage = 0; stage < KEMPLD_WDT_MAX_STAGES; stage++) {
+		kfree(wdt->stage[stage]);
+		wdt->stage[stage] = NULL;
+	}
+}
+
+static int kempld_wdt_probe_stages(struct kempld_watchdog_data *wdt)
+{
+	struct kempld_device_data *pld = wdt->pld;
+	int i, ret;
+	u32 timeout_mask;
+	struct kempld_watchdog_stage *stage;
+
+	wdt->stages = 0;
+	wdt->timeout_stage = NULL;
+	wdt->pretimeout_stage = NULL;
+
+	for (i = 0; i < KEMPLD_WDT_MAX_STAGES; i++) {
+		int j;
+		u8 index, data, data_orig;
+
+		index = KEMPLD_WDT_STAGE_TIMEOUT(i);
+		timeout_mask = ~0;
+
+		kempld_get_mutex_set_index(pld, index);
+
+		/* Probe each byte individually according to new spec revision.
+		 * Register content is restored afterwards. */
+		for (j = 0; j < 4; j++) {
+			data_orig = kempld_read8(pld, index);
+			kempld_write8(pld, index, 0x00);
+			data = kempld_read8(pld, index);
+			kempld_write8(pld, index, data_orig);
+			*(((u8 *)&timeout_mask)+j) &= data;
+			if (data != 0x0)
+				break;
+			index++;
+		}
+
+		kempld_release_mutex(pld);
+
+		if ((timeout_mask & 0xff) != 0xff) {
+			stage = kzalloc(sizeof(struct kempld_watchdog_stage),
+					GFP_KERNEL);
+			if (stage == NULL) {
+				ret = -ENOMEM;
+				goto err_alloc_stages;
+			}
+			stage->num = i;
+			stage->timeout_mask = ~timeout_mask;
+			wdt->stage[i] = stage;
+			wdt->stages++;
+
+			/* assign available stages to timeout and pretimeout */
+			if (wdt->timeout_stage == NULL) {
+				wdt->timeout_stage = stage;
+			} else if ((wdt->pretimeout_stage == NULL) &&
+				(pld->feature_mask & KEMPLD_FEATURE_BIT_NMI)) {
+				wdt->pretimeout_stage = wdt->timeout_stage;
+				wdt->timeout_stage = stage;
+			}
+		} else
+			wdt->stage[i] = NULL;
+	}
+
+	return 0;
+
+err_alloc_stages:
+	kempld_wdt_release_stages(wdt);
+
+	return ret;
+}
+
+static const struct file_operations kempld_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= kempld_wdt_write,
+	.unlocked_ioctl	= kempld_wdt_ioctl,
+	.open		= kempld_wdt_open,
+	.release	= kempld_wdt_release,
+};
+
+static struct miscdevice kempld_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &kempld_wdt_fops,
+};
+
+static int kempld_wdt_probe(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt;
+	struct kempld_device_data *pld;
+	u8 status;
+	int ret;
+
+	if (kempld_wdt != NULL) {
+		dev_err(&pdev->dev,
+			"unable to support more than one watchdog devices\n");
+		return -EMFILE;
+	}
+
+	wdt = kzalloc(sizeof(struct kempld_watchdog_data), GFP_KERNEL);
+	if (wdt == NULL) {
+		dev_err(&pdev->dev, "unable to get memory for device data\n");
+		ret = -ENOMEM;
+		goto err_alloc_dev_data;
+	}
+
+	pld = dev_get_drvdata(pdev->dev.parent);
+	wdt->pld = pld;
+
+	platform_set_drvdata(pdev, wdt);
+
+	strncpy(wdt->ident.identity, "KEMPLD Watchdog",
+		sizeof(wdt->ident.identity));
+
+	/* watchdog firmware version is identical to the CPLD version */
+	wdt->ident.firmware_version = (pld->info.major<<24)
+		| (pld->info.minor<<16) | pld->info.buildnr;
+
+	/* probe how many usable stages we have */
+	ret = kempld_wdt_probe_stages(wdt);
+	if (ret)
+		goto err_probe_stages;
+
+	/* get initial watchdog status */
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	status = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(wdt->pld);
+
+	/* check if the watchdog is already locked and enable the nowayout
+	 * option in that case */
+	if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
+			KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
+		if (!nowayout)
+			dev_warn(wdt->pld->dev,
+				 "Forcing nowayout - watchdog lock enabled!\n");
+		nowayout = 1;
+	}
+
+	/* set default values for the case we start the watchdog or change
+	 * the configuration */
+	wdt->timeout = WATCHDOG_DEFAULT_TIMEOUT;
+	wdt->pretimeout = WATCHDOG_DEFAULT_PRETIMEOUT;
+
+	/* check if watchdog is enabled */
+	if (status & KEMPLD_WDT_CFG_ENABLE) {
+		/* Get current watchdog settings */
+		kempld_wdt_update_timeouts(wdt);
+
+		dev_info(wdt->pld->dev, "Watchdog is already enabled:\n"
+			 "%d s timeout and %d s pretimeout!\n",
+			 wdt->timeout, wdt->pretimeout);
+	}
+
+	/* update the timeout settings if requested by module parameters */
+	if (timeout > 0)
+		wdt->timeout = timeout;
+	if (pretimeout >= 0)
+		wdt->pretimeout = pretimeout;
+
+	dev_info(wdt->pld->dev, "watchdog will be set on (re)start\n"
+		 "new settings: %d s timeout and %d s pretimeout\n",
+		 wdt->timeout, wdt->pretimeout);
+
+	wdt->ident.options = WDIOF_KEEPALIVEPING;
+	if ((wdt->timeout_stage) && !(status & KEMPLD_WDT_CFG_GLOBAL_LOCK))
+		wdt->ident.options |= WDIOF_SETTIMEOUT;
+	if (wdt->pretimeout_stage)
+		wdt->ident.options |= WDIOF_PRETIMEOUT;
+	if (!nowayout)
+		wdt->ident.options |= WDIOF_MAGICCLOSE;
+
+	kempld_wdt = wdt;
+
+	ret = misc_register(&kempld_wdt_miscdev);
+	if (ret)
+		goto err_misc_register;
+
+	dev_info(wdt->pld->dev,
+		 "%d stage watchdog initialized, pretimeout %ssupported\n",
+		 wdt->stages, wdt->pretimeout_stage ? "" : "not ");
+
+	return 0;
+
+err_probe_stages:
+err_misc_register:
+	kfree(kempld_wdt);
+	kempld_wdt = NULL;
+err_alloc_dev_data:
+	return ret;
+}
+
+static void kempld_wdt_shutdown(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	if (wdt != NULL) {
+		if (!nowayout)
+			kempld_wdt_stop(wdt);
+		else
+			kempld_wdt_keepalive(wdt);
+	}
+}
+
+static int kempld_wdt_remove(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+
+	BUG_ON(wdt != kempld_wdt);
+
+	/* stop or at least keepalive the watchdog before we leave */
+	kempld_wdt_shutdown(pdev);
+
+	misc_deregister(&kempld_wdt_miscdev);
+
+	kempld_wdt_release_stages(wdt);
+
+	kfree(wdt);
+	kempld_wdt = NULL;
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int wdt_pm_status_store;
+
+/* Disable watchdog if it is active during suspend */
+static int kempld_wdt_suspend(struct platform_device *pdev,
+				pm_message_t message)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+	struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
+
+	kempld_get_mutex_set_index(pld, KEMPLD_WDT_CFG);
+	wdt_pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
+	kempld_release_mutex(pld);
+
+	kempld_wdt_update_timeouts(wdt);
+
+	if (wdt_pm_status_store & KEMPLD_WDT_CFG_ENABLE)
+		kempld_wdt_shutdown(pdev);
+
+	return 0;
+}
+
+/* Enable watchdog and configure it if necessary */
+static int kempld_wdt_resume(struct platform_device *pdev)
+{
+	struct kempld_watchdog_data *wdt = platform_get_drvdata(pdev);
+	int ret;
+
+	/* if watchdog was stopped before suspend be sure it gets disabled
+	 * again, for the case BIOS has enabled it during resume */
+	if (wdt_pm_status_store & KEMPLD_WDT_CFG_ENABLE) {
+		ret = kempld_wdt_settimeout(wdt);
+		if (ret)
+			goto err_enable_wdt;
+		ret = kempld_wdt_start(wdt);
+		if (ret)
+			goto err_enable_wdt;
+
+		dev_info(wdt->pld->dev, "Resuming watchdog operation:\n"
+			 "%d s timeout and %d s pretimeout\n", wdt->timeout,
+			 wdt->pretimeout);
+	} else
+		kempld_wdt_shutdown(pdev);
+
+	return 0;
+
+err_enable_wdt:
+	dev_err(wdt->pld->dev,
+		"Failed to reenable the watchdog timer after resume!\n");
+
+	return ret;
+}
+#else
+#define kempld_wdt_suspend	NULL
+#define kempld_wdt_resume	NULL
+#endif
+
+static struct platform_driver kempld_wdt_driver = {
+	.driver = {
+		.name = "kempld-wdt",
+		.owner = THIS_MODULE,
+	},
+	.probe = kempld_wdt_probe,
+	.remove = kempld_wdt_remove,
+	.shutdown = kempld_wdt_shutdown,
+	.suspend = kempld_wdt_suspend,
+	.resume = kempld_wdt_resume,
+};
+
+static int __init kempld_wdt_init(void)
+{
+	return platform_driver_register(&kempld_wdt_driver);
+}
+
+static void __exit kempld_wdt_exit(void)
+{
+	platform_driver_unregister(&kempld_wdt_driver);
+}
+
+module_init(kempld_wdt_init);
+module_exit(kempld_wdt_exit);
+
+MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
+MODULE_AUTHOR("Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/watchdog/kempld_wdt.h b/drivers/watchdog/kempld_wdt.h
new file mode 100644
index 0000000..80f68f6
--- /dev/null
+++ b/drivers/watchdog/kempld_wdt.h
@@ -0,0 +1,75 @@
+/*
+ *  kempld_wdt.h - Kontron PLD watchdog driver definitions
+ *
+ *  Copyright (c) 2010-2012 Kontron Europe GmbH
+ *  Author: Michael Brunner <michael.brunner-2UyDCMiLNfhBDgjK7y7TUQ@public.gmane.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License 2 as published
+ *  by the Free Software Foundation.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _KEMPLD_WDT_H_
+#define _KEMPLD_WDT_H_
+
+/* watchdog register definitions */
+#define KEMPLD_WDT_KICK			0x16
+#define KEMPLD_WDT_CFG			0x17
+#define		KEMPLD_WDT_CFG_STAGE_TIMEOUT_OCCURED(x)	(1<<x)
+#define		KEMPLD_WDT_CFG_ENABLE_LOCK		0x8
+#define		KEMPLD_WDT_CFG_ENABLE			0x10
+#define		KEMPLD_WDT_CFG_AUTO_RELOAD		0x40
+#define		KEMPLD_WDT_CFG_GLOBAL_LOCK		0x80
+#define KEMPLD_WDT_STAGE_CFG(x)		(0x18+x)
+#define		KEMPLD_WDT_STAGE_CFG_ACTION_MASK	0x7
+#define		KEMPLD_WDT_STAGE_CFG_GET_ACTION(x)	(x & 0x7)
+#define		KEMPLD_WDT_STAGE_CFG_ASSERT		(1<<3)
+#define		KEMPLD_WDT_STAGE_CFG_PRESCALER_MASK	0x30
+#define		KEMPLD_WDT_STAGE_CFG_GET_PRESCALER(x)	((x & 0x30)>>4)
+#define		KEMPLD_WDT_STAGE_CFG_SET_PRESCALER(x)	((x & 0x30)<<4)
+#define KEMPLD_WDT_STAGE_TIMEOUT(x)	(0x1b+x*4)
+#define KEMPLD_WDT_MAX_STAGES		3
+
+#define	KEMPLD_WDT_ACTION_NONE		0x0
+#define	KEMPLD_WDT_ACTION_RESET		0x1
+#define	KEMPLD_WDT_ACTION_NMI		0x2
+#define	KEMPLD_WDT_ACTION_SMI		0x3
+#define	KEMPLD_WDT_ACTION_SCI		0x4
+#define	KEMPLD_WDT_ACTION_DELAY		0x5
+
+#define	KEMPLD_WDT_PRESCALER_21BIT	0x0
+#define	KEMPLD_WDT_PRESCALER_17BIT	0x1
+#define	KEMPLD_WDT_PRESCALER_12BIT	0x2
+
+const int kempld_prescaler_bits[] = { 21, 17, 12 };
+#define KEMPLD_PRESCALER(x)	(0xffffffff>>(32-kempld_prescaler_bits[x]))
+
+
+struct kempld_watchdog_stage {
+	int	num;
+	u32	timeout_mask;
+};
+
+struct kempld_watchdog_data {
+	int				timeout;
+	int				pretimeout;
+	unsigned long			is_open;
+	unsigned long			expect_close;
+	int				stages;
+	struct kempld_watchdog_stage	*timeout_stage;
+	struct kempld_watchdog_stage	*pretimeout_stage;
+	struct kempld_device_data	*pld;
+	struct watchdog_info		ident;
+	struct kempld_watchdog_stage	*stage[KEMPLD_WDT_MAX_STAGES];
+};
+
+#endif /* _KEMPLD_WDT_H_ */
-- 
1.7.9.5

  parent reply	other threads:[~2013-04-08 17:18 UTC|newest]

Thread overview: 67+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-04-08 17:15 [PATCH 1/4] mfd: Kontron PLD mfd driver Kevin Strasser
2013-04-08 17:15 ` [PATCH 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser
2013-04-08 17:15   ` Kevin Strasser
2013-04-10 17:02   ` Guenter Roeck
2013-04-10 17:02     ` Guenter Roeck
2013-04-16  9:53     ` Wolfram Sang
2013-04-16  9:53       ` Wolfram Sang
2013-04-08 17:15 ` [PATCH 3/4] gpio: Kontron PLD gpio driver Kevin Strasser
2013-04-08 17:15   ` Kevin Strasser
2013-04-09  8:46   ` Linus Walleij
2013-04-09 16:41     ` Guenter Roeck
2013-04-09 16:41       ` Guenter Roeck
2013-04-10 20:06       ` Linus Walleij
2013-04-10 20:45   ` Linus Walleij
2013-04-12 11:09     ` Michael Brunner
2013-04-12 11:09       ` Michael Brunner
2013-04-12 22:05       ` Linus Walleij
2013-04-08 17:15 ` Kevin Strasser [this message]
2013-04-08 17:15   ` [PATCH 4/4] watchdog: Kontron PLD watchdog timer Kevin Strasser
2013-04-10 16:47   ` Guenter Roeck
2013-04-10 16:57     ` Kevin Strasser
2013-04-10 16:57       ` Kevin Strasser
2013-05-26 14:38       ` Wim Van Sebroeck
2013-04-13 20:38 ` [PATCH 1/4] mfd: Kontron PLD mfd driver Thomas Gleixner
2013-04-18  4:19   ` Guenter Roeck
2013-04-18  4:40     ` Joe Perches
2013-04-18  4:40       ` Joe Perches
2013-04-18 13:35       ` Guenter Roeck
2013-04-18 13:35         ` Guenter Roeck
2013-04-18 16:42         ` Joe Perches
2013-04-18 18:40           ` Guenter Roeck
2013-06-18 21:04 ` [PATCH v2 0/4] Kontron PLD drivers Kevin Strasser
2013-06-18 21:04   ` Kevin Strasser
2013-06-18 21:04   ` [PATCH v2 1/4] mfd: Kontron PLD mfd driver Kevin Strasser
2013-06-19  8:40     ` Linus Walleij
2013-06-19  9:11       ` Samuel Ortiz
2013-06-19  9:48         ` Mark Brown
2013-06-19  9:12     ` Thomas Gleixner
2013-06-19 18:03       ` Kevin Strasser
2013-06-19 20:35         ` Guenter Roeck
2013-06-18 21:04   ` [PATCH v2 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser
2013-06-18 21:04   ` [PATCH v2 3/4] gpio: Kontron PLD gpio driver Kevin Strasser
2013-06-19  8:36     ` Linus Walleij
2013-06-27 22:14       ` Kevin Strasser
2013-06-18 21:04   ` [PATCH v2 4/4] watchdog: Kontron PLD watchdog timer driver Kevin Strasser
2013-06-24  4:00 ` [PATCH v3 0/4] Kontron PLD drivers Kevin Strasser
2013-06-24  4:00   ` Kevin Strasser
2013-06-24  4:00   ` [PATCH v3 1/4] mfd: Kontron PLD mfd driver Kevin Strasser
2013-06-24  4:00   ` [PATCH v3 2/4] i2c: Kontron PLD i2c bus driver Kevin Strasser
2013-07-01  6:40     ` Wolfram Sang
2013-07-01  6:40       ` Wolfram Sang
2013-06-24  4:00   ` [PATCH v3 3/4] gpio: Kontron PLD gpio driver Kevin Strasser
2013-07-21 14:31     ` Linus Walleij
2013-06-24  4:00   ` [PATCH v3 4/4] watchdog: Kontron PLD watchdog timer driver Kevin Strasser
2013-06-27 18:23     ` Kevin Strasser
2013-06-27 21:47       ` Samuel Ortiz
2013-06-27 21:47         ` Samuel Ortiz
2013-06-27 22:05         ` Kevin Strasser
2013-06-27 22:05           ` Kevin Strasser
2013-06-24 12:06   ` [PATCH v3 0/4] Kontron PLD drivers Samuel Ortiz
2013-06-24 12:06     ` Samuel Ortiz
2013-06-24 16:09     ` Wolfram Sang
2013-06-24 16:09       ` Wolfram Sang
2013-06-27 20:34     ` Wim Van Sebroeck
2013-06-27 20:34       ` Wim Van Sebroeck
2013-06-27 21:48       ` Samuel Ortiz
2013-06-27 21:48         ` Samuel Ortiz

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1365441321-21952-4-git-send-email-kevin.strasser@linux.intel.com \
    --to=kevin.strasser@linux.intel.com \
    --cc=ben-linux@fluff.org \
    --cc=dvhart@linux.intel.com \
    --cc=grant.likely@secretlab.ca \
    --cc=gregkh@linuxfoundation.org \
    --cc=linus.walleij@linaro.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-watchdog@vger.kernel.org \
    --cc=mibru@gmx.de \
    --cc=michael.brunner@kontron.com \
    --cc=sameo@linux.intel.com \
    --cc=wim@iguana.be \
    --cc=wsa@the-dreams.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.