[PATCH] watchdog: Add driver for Altera Watchdog Timer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This driver adds support for the Altera Timer in the Watchdog Timer
configuration. This component is usually found on SOPC (System on
Programmable Chip) for Altera FPGAs containing a Nios2 softcore
processor.

Signed-off-by: Tobias Klauser <tklauser@xxxxxxxxxx>
---
 drivers/watchdog/Kconfig      |   11 ++
 drivers/watchdog/Makefile     |    3 +
 drivers/watchdog/altera_wdt.c |  229 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 243 insertions(+), 0 deletions(-)
 create mode 100644 drivers/watchdog/altera_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 62e743d..be4cb39 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -972,6 +972,17 @@ config BCM63XX_WDT
 	  To compile this driver as a loadable module, choose M here.
 	  The module will be called bcm63xx_wdt.
 
+# NIOS2 Architecture
+
+config ALTERA_WDT
+	tristate "Altera Watchdog Timer"
+	select WATCHDOG_NOWAYOUT
+	help
+	  Watchdog driver for the Altera Watchdog Timer found on Altera
+	  Nios2 SOPC systems.
+
+	  Once the watchdog timer is started it cannot be stopped anymore.
+
 # PARISC Architecture
 
 # POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index dd77665..0e7cf8f 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -122,6 +122,9 @@ obj-$(CONFIG_TXX9_WDT) += txx9wdt.o
 obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
 octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
 
+# NIOS2 Architecture
+obj-$(CONFIG_ALTERA_WDT) += altera_wdt.o
+
 # PARISC Architecture
 
 # POWERPC Architecture
diff --git a/drivers/watchdog/altera_wdt.c b/drivers/watchdog/altera_wdt.c
new file mode 100644
index 0000000..a7bf7ce
--- /dev/null
+++ b/drivers/watchdog/altera_wdt.c
@@ -0,0 +1,229 @@
+/*
+ * Driver for the Altera Watchdog Timer
+ *
+ * Copyright (C) 2011 Tobias Klauser <tklauser@xxxxxxxxxx>
+ * Copyright (C) 2005 Walter Goossens
+ * Copyright (C) 1996 Alan Cox <alan@xxxxxxxxxx>
+ *
+ * Originally based on wdt.c
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/platform_device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+#define WATCHDOG_NAME	"altera_wdt"
+
+/* Register offsets */
+#define ALTERA_WDT_SIZE		0x18
+#define ALTERA_WDT_STATUS	0x00
+#define ALTERA_WDT_CONTROL	0x01
+#define ALTERA_WDT_PERIODL	0x02
+#define ALTERA_WDT_PERIODH	0x03
+
+#define ALTERA_WDT_RUN_BIT	0x04
+
+static struct platform_device *altera_wdt_pdev;
+
+struct altera_wdt_dev {
+	void __iomem *base;
+	unsigned long wdt_users;
+};
+
+static int altera_wdt_start(struct altera_wdt_dev *wdev)
+{
+	u32 control = readl(wdev->base + ALTERA_WDT_CONTROL);
+
+	pr_debug("%s: Starting watchdog timer\n", WATCHDOG_NAME);
+	writel(control | ALTERA_WDT_RUN_BIT, wdev->base + ALTERA_WDT_CONTROL);
+	return 0;
+}
+
+static void altera_wdt_ping(struct altera_wdt_dev *wdev)
+{
+	/* It doesn't matter what value we write */
+	writel(1, wdev->base + ALTERA_WDT_PERIODL);
+}
+
+static ssize_t altera_wdt_write(struct file *file, const char __user *buf,
+		size_t len, loff_t *ppos)
+{
+	struct altera_wdt_dev *wdev = file->private_data;
+
+	if (len)
+		altera_wdt_ping(wdev);
+	return len;
+}
+
+static const struct watchdog_info altera_wdt_info = {
+	.identity		= "Altera Watchdog",
+	.options		= WDIOF_KEEPALIVEPING,
+	.firmware_version	= 1,
+};
+
+static long altera_wdt_ioctl(struct file *file, unsigned int cmd,
+		unsigned long arg)
+{
+	struct altera_wdt_dev *wdev = file->private_data;
+	void __user *argp = (void __user *) arg;
+
+	switch (cmd) {
+	case WDIOC_GETSUPPORT:
+		return copy_to_user(argp, &altera_wdt_info, sizeof(altera_wdt_info));
+	case WDIOC_KEEPALIVE:
+		altera_wdt_ping(wdev);
+		return 0;
+	default:
+		return -ENOTTY;
+	}
+}
+
+static int altera_wdt_open(struct inode *inode, struct file *file)
+{
+	struct altera_wdt_dev *wdev = platform_get_drvdata(altera_wdt_pdev);
+
+	if (test_and_set_bit(1, &wdev->wdt_users))
+		return -EBUSY;
+
+	file->private_data = wdev;
+
+	altera_wdt_start(wdev);
+	altera_wdt_ping(wdev);
+
+	return nonseekable_open(inode, file);
+}
+
+static int altera_wdt_release(struct inode *inode, struct file *file)
+{
+	struct altera_wdt_dev *wdev = file->private_data;
+
+	/* It is not possible to stop the watchdog timer, once started */
+	pr_crit("%s: Unexpected close, not stopping!\n", WATCHDOG_NAME);
+	wdev->wdt_users = 0;
+
+	return 0;
+}
+
+static const struct file_operations altera_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= altera_wdt_write,
+	.unlocked_ioctl	= altera_wdt_ioctl,
+	.open		= altera_wdt_open,
+	.release	= altera_wdt_release,
+};
+
+static struct miscdevice altera_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &altera_wdt_fops,
+};
+
+static int __devinit altera_wdt_probe(struct platform_device *pdev)
+{
+	struct altera_wdt_dev *wdev;
+	struct resource *res, *mem;
+	int ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -ENOENT;
+
+	if (altera_wdt_pdev)
+		return -EBUSY;
+
+	mem = request_mem_region(res->start, resource_size(res), pdev->name);
+	if (!mem)
+		return -EBUSY;
+
+	wdev = kzalloc(sizeof(struct altera_wdt_dev), GFP_KERNEL);
+	if (!wdev) {
+		ret = -ENOMEM;
+		goto err_kzalloc;
+	}
+
+	wdev->base = ioremap(mem->start, resource_size(mem));
+	if (!wdev->base) {
+		ret = -ENOMEM;
+		goto err_ioremap;
+	}
+
+	platform_set_drvdata(pdev, wdev);
+
+	ret = misc_register(&altera_wdt_miscdev);
+	if (ret)
+		goto err_misc;
+
+	altera_wdt_pdev = pdev;
+
+	return 0;
+
+err_misc:
+	iounmap(wdev->base);
+err_ioremap:
+	kfree(wdev);
+err_kzalloc:
+	release_mem_region(res->start, resource_size(res));
+
+	return ret;
+}
+
+static int __devexit altera_wdt_remove(struct platform_device *pdev)
+{
+	struct altera_wdt_dev *wdev = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	misc_deregister(&altera_wdt_miscdev);
+	platform_set_drvdata(pdev, NULL);
+	iounmap(wdev->base);
+	release_mem_region(res->start, resource_size(res));
+	kfree(wdev);
+	altera_wdt_pdev = NULL;
+
+	return 0;
+}
+
+static void altera_wdt_shutdown(struct platform_device *pdev)
+{
+	/* Do nothing. It is not possible to stop the watchdog timer, once
+	 * started */
+}
+
+static struct platform_driver altera_wdt_driver = {
+	.probe		= altera_wdt_probe,
+	.remove		= __devexit_p(altera_wdt_remove),
+	.shutdown	= altera_wdt_shutdown,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= WATCHDOG_NAME,
+	},
+};
+
+static int __init altera_wdt_init(void)
+{
+	return platform_driver_register(&altera_wdt_driver);
+}
+
+static void __exit altera_wdt_exit(void)
+{
+	platform_driver_unregister(&altera_wdt_driver);
+}
+
+module_init(altera_wdt_init);
+module_exit(altera_wdt_exit);
+
+MODULE_AUTHOR("Walter Goossens, Tobias Klauser <tklauser@xxxxxxxxxx>");
+MODULE_DESCRIPTION("Driver for Altera Watchdog Timer");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [eCos]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux