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