Make retu watchdog behave like a standard Linux watchdog.
Let the kernel do the kicking until the watchdog device is opened.
Note: We should remove the old non-standard interface, please
change to use standard /dev/watchdog instead.
Signed-off-by: Tony Lindgren <[EMAIL PROTECTED]>
---
drivers/cbus/Kconfig | 2 +-
drivers/cbus/retu-wdt.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 183 insertions(+), 3 deletions(-)
diff --git a/drivers/cbus/Kconfig b/drivers/cbus/Kconfig
index 25f8039..c344a99 100644
--- a/drivers/cbus/Kconfig
+++ b/drivers/cbus/Kconfig
@@ -72,7 +72,7 @@ config CBUS_RETU_RTC
RTC in Retu. This will expose a sysfs interface for it.
config CBUS_RETU_WDT
- depends on CBUS_RETU && SYSFS
+ depends on CBUS_RETU && SYSFS && WATCHDOG
tristate "Support for Retu watchdog timer"
---help---
Say Y here if you want support for the watchdog in Retu. This will
diff --git a/drivers/cbus/retu-wdt.c b/drivers/cbus/retu-wdt.c
index b7b20b7..8ca5c72 100644
--- a/drivers/cbus/retu-wdt.c
+++ b/drivers/cbus/retu-wdt.c
@@ -25,11 +25,19 @@
#include <linux/module.h>
#include <linux/device.h>
#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
#include <linux/completion.h>
#include <linux/errno.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+
+#include <asm/uaccess.h>
+
+#include <asm/arch/prcm.h>
#include "cbus.h"
#include "retu.h"
@@ -46,6 +54,17 @@ static DEFINE_MUTEX(retu_wdt_mutex);
static unsigned int period_val = RETU_WDT_DEFAULT_TIMER;
static int counter_param = RETU_WDT_MAX_TIMER;
+struct retu_wdt_dev {
+ struct device *dev;
+ int users;
+ struct miscdevice retu_wdt_miscdev;
+ struct timer_list ping_timer;
+};
+
+static struct retu_wdt_dev *retu_wdt;
+
+static void retu_wdt_set_ping_timer(unsigned long enable);
+
static int retu_modify_counter(unsigned int new)
{
int ret = 0;
@@ -69,6 +88,10 @@ static ssize_t retu_wdt_period_show(struct device *dev,
return sprintf(buf, "%u\n", (u16)period_val);
}
+/*
+ * Note: This inteface is non-standard and likely to disappear!
+ * Use /dev/watchdog instead, that's the standard.
+ */
static ssize_t retu_wdt_period_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -76,6 +99,10 @@ static ssize_t retu_wdt_period_store(struct device *dev,
unsigned int new_period;
int ret;
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+ retu_wdt_set_ping_timer(0);
+#endif
+
if (sscanf(buf, "%u", &new_period) != 1) {
printk(KERN_ALERT "retu_wdt_period_store: Invalid input\n");
return -EINVAL;
@@ -104,31 +131,184 @@ static DEVICE_ATTR(period, S_IRUGO | S_IWUSR,
retu_wdt_period_show, \
retu_wdt_period_store);
static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL);
+/*----------------------------------------------------------------------------*/
+
+/*
+ * Since retu watchdog cannot be disabled in hardware, we must kick it
+ * with a timer until userspace watchdog software takes over. Do this
+ * unless /dev/watchdog is open or CONFIG_WATCHDOG_NOWAYOUT is set.
+ */
+static void retu_wdt_set_ping_timer(unsigned long enable)
+{
+ retu_modify_counter(RETU_WDT_MAX_TIMER);
+ if (enable)
+ mod_timer(&retu_wdt->ping_timer,
+ jiffies + RETU_WDT_DEFAULT_TIMER * HZ);
+ else
+ del_timer_sync(&retu_wdt->ping_timer);
+}
+
+static int retu_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, (unsigned long *)&(retu_wdt->users)))
+ return -EBUSY;
+
+ file->private_data = (void *)retu_wdt;
+ retu_wdt_set_ping_timer(0);
+
+ return nonseekable_open(inode, file);
+}
+
+static int retu_wdt_release(struct inode *inode, struct file *file)
+{
+ struct retu_wdt_dev *wdev = file->private_data;
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ retu_wdt_set_ping_timer(1);
+#endif
+ wdev->users = 0;
+
+ return 0;
+}
+
+static ssize_t retu_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ if (len)
+ retu_modify_counter(RETU_WDT_MAX_TIMER);
+
+ return len;
+}
+
+static int retu_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int new_margin;
+
+ static struct watchdog_info ident = {
+ .identity = "Retu Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOTTY;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident));
+ case WDIOC_GETSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_GETBOOTSTATUS:
+ if (cpu_is_omap16xx())
+ return put_user(omap_readw(ARM_SYSST),
+ (int __user *)arg);
+ if (cpu_is_omap24xx())
+ return put_user(omap_prcm_get_reset_sources(),
+ (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ retu_modify_counter(RETU_WDT_MAX_TIMER);
+ break;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+ retu_modify_counter(new_margin);
+ /* Fall through */
+ case WDIOC_GETTIMEOUT:
+ return put_user(period_val, (int __user *)arg);
+ }
+
+ return 0;
+}
+
+/* Start kicking retu watchdog until user space starts doing the kicking */
+static int __init retu_wdt_ping(void)
+{
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+ retu_modify_counter(RETU_WDT_MAX_TIMER);
+#else
+ retu_wdt_set_ping_timer(1);
+#endif
+
+ return 0;
+}
+late_initcall(retu_wdt_ping);
+
+static const struct file_operations retu_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = retu_wdt_write,
+ .ioctl = retu_wdt_ioctl,
+ .open = retu_wdt_open,
+ .release = retu_wdt_release,
+};
+
+/*----------------------------------------------------------------------------*/
+
static int __devinit retu_wdt_probe(struct device *dev)
{
+ struct retu_wdt_dev *wdev;
int ret;
+ wdev = kzalloc(sizeof(struct retu_wdt_dev), GFP_KERNEL);
+ if (!wdev)
+ return -ENOMEM;
+
+ wdev->users = 0;
+
ret = device_create_file(dev, &dev_attr_period);
if (ret) {
printk(KERN_ERR "retu_wdt_probe: Error creating "
"sys device file: period\n");
- return ret;
+ goto free1;
}
ret = device_create_file(dev, &dev_attr_counter);
if (ret) {
- device_remove_file(dev, &dev_attr_period);
printk(KERN_ERR "retu_wdt_probe: Error creating "
"sys device file: counter\n");
+ goto free2;
}
+ dev_set_drvdata(dev, wdev);
+ retu_wdt = wdev;
+ wdev->retu_wdt_miscdev.parent = dev;
+ wdev->retu_wdt_miscdev.minor = WATCHDOG_MINOR;
+ wdev->retu_wdt_miscdev.name = "watchdog";
+ wdev->retu_wdt_miscdev.fops = &retu_wdt_fops;
+
+ ret = misc_register(&(wdev->retu_wdt_miscdev));
+ if (ret)
+ goto free3;
+
+ setup_timer(&wdev->ping_timer, retu_wdt_set_ping_timer, 1);
+
+ /* Kick the watchdog for kernel booting to finish */
+ retu_modify_counter(RETU_WDT_MAX_TIMER);
+
+ return 0;
+
+free3:
+ device_remove_file(dev, &dev_attr_counter);
+
+free2:
+ device_remove_file(dev, &dev_attr_period);
+free1:
+ kfree(wdev);
+
return ret;
}
static int __devexit retu_wdt_remove(struct device *dev)
{
+ struct retu_wdt_dev *wdev;
+
+ wdev = dev_get_drvdata(dev);
+ misc_deregister(&(wdev->retu_wdt_miscdev));
device_remove_file(dev, &dev_attr_period);
device_remove_file(dev, &dev_attr_counter);
+ kfree(wdev);
+
return 0;
}
--
1.5.3.6
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html