From: Marc Pignat <marc.pignat@hevs.ch>
To: wim@iguana.be
Cc: linux-kernel@vger.kernel.org,
"Alan Cox" <alan@lxorguk.ukuu.org.uk>,
"Mike Frysinger" <vapier.adi@gmail.com>,
Florian Fainelli <florian.fainelli@telecomint.eu>
Subject: [RFC, PATCH, take 2] watchdog on generic gpio
Date: Mon, 14 Jan 2008 11:03:23 +0100 [thread overview]
Message-ID: <200801141103.23744.marc.pignat@hevs.ch> (raw)
watchdog driver for embedded systems with a supervisor watchdog (MAX823 or so)
connected to a gpio. This is the platform_driver and needs platform_data for
defining the gpio pin and the watchdog timeout.
Signed-off-by: Marc Pignat <marc.pignat@hevs.ch>
---
Hi!
Changes form take1:
* corrected includes
* fixed default ioctl to respond -ENOTTY
* fixed CONFIG_PM
* other small changes
* simplified banner display (simpler)
This problem will probably go away when the watchdog-core will be merged
and the memory gain is very small.
* changed from __init to __devinit
More robust and flexible
Thanks for all comments!
If you have already read take1, you can safely go to the patch.
If you've got a max823, 824 or any cpu supervisor like this connected to your
cpu by a gpio pin, you can use this watchdog driver.
Simply add this to your board specific setup file:
#include <linux/gpio_wdt.h>
static struct gpio_wdt_pdata my_wdt = {
/* WDI input connected to this gpio */
.pin = AT91_PIN_PD27,
/* The *min* timeout */
.timeout_ms = 1140
};
static struct platform_device my_watchdog_device =
{
.name = "gpio_wdt",
.id = 0,
.dev = {
.platform_data = &my_wdt,
}
};
then
platform_device_register(&my_watchdog_device);
This patch is against 2.6.24-rc7-git7
Comments are welcome
Regards
Marc
diff -urN a/drivers/watchdog/gpio_wdt.c b/drivers/watchdog/gpio_wdt.c
--- a/drivers/watchdog/gpio_wdt.c 1970-01-01 01:00:00.000000000 +0100
+++ b/drivers/watchdog/gpio_wdt.c 2008-01-14 10:40:46.000000000 +0100
@@ -0,0 +1,301 @@
+/*
+ * Generic GPIO based watchdog driver
+ *
+ * Copyright (C) 2007 Marc Pignat for www.hevs.ch
+ *
+ * This is a driver for the MAXIM MAX823 (and similar watchdog chips) connected
+ * on a generic gpio line.
+ *
+ * Principle of operation:
+ *
+ * When the WDI input is not driven (or in high impedance state), the
+ * watchdog is disabled. This mode of operations supposes that the gpio line
+ * can be put in high-impedance state (input) and is not driving too much
+ * current from the WDI pin.
+ *
+ * When the WDI input is driven, it must be toggled before the timeout,
+ * otherwise a reset is generated.
+ *
+ * Other supported chips:
+ * MAXIM: MAX824, ...
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/gpio_watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/uaccess.h>
+#include <asm/gpio.h>
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+
+struct gpio_wdt {
+ struct gpio_wdt_pdata *pdata;
+ unsigned long driver_open;
+ char expect_close;
+};
+
+static struct gpio_wdt *watchdog;
+
+#define GPIO_WDT_DRIVER_NAME "gpio_wdt"
+#define PFX GPIO_WDT_DRIVER_NAME":"
+
+static int gpio_wdt_keepalive(struct gpio_wdt *wdt)
+{
+ gpio_set_value(wdt->pdata->pin, 0);
+ gpio_set_value(wdt->pdata->pin, 1);
+ return 0;
+}
+
+static int gpio_wdt_stop(struct gpio_wdt *wdt)
+{
+ gpio_wdt_keepalive(wdt);
+ gpio_direction_input(wdt->pdata->pin);
+ return 0;
+}
+
+static int gpio_wdt_start(struct gpio_wdt *wdt)
+{
+ gpio_direction_output(wdt->pdata->pin, 0);
+ gpio_wdt_keepalive(wdt);
+ return 0;
+}
+
+static int gpio_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(0, &watchdog->driver_open))
+ return -EBUSY;
+
+ gpio_wdt_start(watchdog);
+ gpio_wdt_keepalive(watchdog);
+
+ return nonseekable_open(inode, file);
+}
+
+static int gpio_wdt_release(struct inode *inode, struct file *file)
+{
+ if (watchdog->expect_close == 42) {
+ gpio_wdt_stop(watchdog);
+ module_put(THIS_MODULE);
+ } else {
+ printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+ gpio_wdt_keepalive(watchdog);
+ }
+ clear_bit(0, &watchdog->driver_open);
+ watchdog->expect_close = 0;
+ return 0;
+}
+
+static ssize_t gpio_wdt_write(struct file *file, const char __user *data, size_t len, loff_t *ppos)
+{
+ /*
+ * Refresh the timer.
+ */
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ /* In case it was set long ago */
+ watchdog->expect_close = 0;
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ watchdog->expect_close = 42;
+ }
+ }
+ gpio_wdt_keepalive(watchdog);
+ }
+ return len;
+
+ gpio_wdt_keepalive(watchdog);
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
+ .firmware_version = 0,
+ .identity = GPIO_WDT_DRIVER_NAME,
+};
+
+static int gpio_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ int __user *p = argp;
+ u32 timeout = watchdog->pdata->timeout_ms;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
+ case WDIOC_GETSTATUS:
+ case WDIOC_GETBOOTSTATUS:
+ return put_user(0, p);
+ case WDIOC_KEEPALIVE:
+ gpio_wdt_keepalive(watchdog);
+ return 0;
+ case WDIOC_GETTIMEOUT:
+ return put_user(timeout, p);
+ case WDIOC_SETTIMEOUT:
+ return -EINVAL;
+ default:
+ return -ENOTTY;
+ }
+}
+
+static int gpio_wdt_notify_sys(struct notifier_block *this,
+ unsigned long code, void *unused)
+{
+ if (code == SYS_DOWN || code == SYS_HALT)
+ gpio_wdt_stop(watchdog);
+
+ return NOTIFY_DONE;
+}
+
+static struct file_operations gpio_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = gpio_wdt_write,
+ .ioctl = gpio_wdt_ioctl,
+ .open = gpio_wdt_open,
+ .release = gpio_wdt_release,
+};
+
+static struct miscdevice gpio_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &gpio_wdt_fops,
+};
+
+static struct notifier_block gpio_wdt_notifier = {
+ .notifier_call = gpio_wdt_notify_sys,
+};
+
+static int __devinit gpio_wdt_probe(struct platform_device *pdev)
+{
+ struct gpio_wdt_pdata *pdata = (struct gpio_wdt_pdata *)pdev->dev.platform_data;
+ int ret;
+
+ if (watchdog) {
+ printk(KERN_ERR PFX "only one device supported\n");
+ return -ENODEV;
+ }
+
+ if (!pdata) {
+ printk(KERN_ERR PFX "no platform data\n");
+ return -ENODEV;
+ }
+
+ watchdog = kzalloc(sizeof(*watchdog), GFP_KERNEL);
+
+ if (!watchdog)
+ return -ENOMEM;
+
+ watchdog->pdata = pdata;
+
+ if (watchdog->pdata->pin == 0 || watchdog->pdata->timeout_ms == 0) {
+ kfree(watchdog);
+ printk(KERN_ERR PFX "invalid platform data\n");
+ return -ENODEV;
+ }
+
+ ret = register_reboot_notifier(&gpio_wdt_notifier);
+ if (ret) {
+ kfree(watchdog);
+ printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", ret);
+ return ret;
+ }
+
+ ret = misc_register(&gpio_wdt_miscdev);
+ if (ret) {
+ unregister_reboot_notifier(&gpio_wdt_notifier);
+ kfree(watchdog);
+ printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
+ WATCHDOG_MINOR, ret);
+ return ret;
+ }
+
+ printk(KERN_INFO PFX "fixed %d.%03d seconds timeout (nowayout= %d)\n",
+ watchdog->pdata->timeout_ms/1000,
+ watchdog->pdata->timeout_ms%1000,
+ nowayout);
+
+ return 0;
+}
+
+static int __devexit gpio_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&gpio_wdt_miscdev);
+ unregister_reboot_notifier(&gpio_wdt_notifier);
+ kfree(watchdog);
+ watchdog = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+int gpio_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ gpio_wdt_stop(watchdog);
+ return 0;
+}
+
+int gpio_wdt_resume(struct platform_device *pdev)
+{
+ if (test_bit(0, &watchdog->driver_open))
+ gpio_wdt_start(watchdog);
+ return 0;
+}
+
+#else
+#define gpio_wdt_suspend NULL
+#define gpio_wdt_resume NULL
+#endif
+
+/* the device driver */
+static struct platform_driver gpio_wdt_driver = {
+ .probe = gpio_wdt_probe,
+ .remove = __devexit_p(gpio_wdt_remove),
+ .suspend = gpio_wdt_suspend,
+ .resume = gpio_wdt_resume,
+ .driver = {
+ .name = GPIO_WDT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+/* module init or kerel entry point */
+static int __init gpio_wdt_driver_init(void)
+{
+ return platform_driver_register(&gpio_wdt_driver);
+}
+
+/* module exit */
+static void __exit gpio_wdt_driver_exit(void)
+{
+ platform_driver_unregister(&gpio_wdt_driver);
+}
+
+module_init(gpio_wdt_driver_init);
+module_exit(gpio_wdt_driver_exit);
+
+MODULE_AUTHOR("Marc Pignat");
+MODULE_DESCRIPTION("Watchdog driver on a generic gpio line");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff -urN a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
--- a/drivers/watchdog/Kconfig 2008-01-08 17:30:30.000000000 +0100
+++ b/drivers/watchdog/Kconfig 2008-01-14 10:14:32.000000000 +0100
@@ -55,6 +55,16 @@
To compile this driver as a module, choose M here: the
module will be called softdog.
+config GPIO_WATCHDOG
+ tristate "Support for GPIO connected watchdog"
+ depends on GENERIC_GPIO
+ help
+ This option enables support for a watchdog connected via a GPIO line
+ (such as the MAX823).
+
+ To compile this driver as a module, choose M here: the module will be
+ called gpio_wdt.
+
# ALPHA Architecture
# ARM Architecture
diff -urN a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
--- a/drivers/watchdog/Makefile 2008-01-08 17:30:30.000000000 +0100
+++ b/drivers/watchdog/Makefile 2008-01-10 14:01:32.000000000 +0100
@@ -121,3 +121,4 @@
# Architecture Independant
obj-$(CONFIG_SOFT_WATCHDOG) += softdog.o
+obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
next reply other threads:[~2008-01-14 10:03 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-01-14 10:03 Marc Pignat [this message]
2008-01-14 11:06 ` [RFC, PATCH, take 2] watchdog on generic gpio Florian Fainelli
2008-01-14 13:01 ` Marc Pignat
2008-01-14 14:03 ` Florian Fainelli
2008-01-14 14:59 ` Marc Pignat
2008-01-14 15:57 ` Haavard Skinnemoen
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=200801141103.23744.marc.pignat@hevs.ch \
--to=marc.pignat@hevs.ch \
--cc=alan@lxorguk.ukuu.org.uk \
--cc=florian.fainelli@telecomint.eu \
--cc=linux-kernel@vger.kernel.org \
--cc=vapier.adi@gmail.com \
--cc=wim@iguana.be \
/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.