public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH][2.5] Sysfs enabling watchdog infrastructure
@ 2003-02-21 23:21 Rusty Lynch
  2003-02-21 23:31 ` Rusty Lynch
  2003-02-21 23:36 ` Rusty Lynch
  0 siblings, 2 replies; 3+ messages in thread
From: Rusty Lynch @ 2003-02-21 23:21 UTC (permalink / raw)
  To: Alan Cox; +Cc: lkml

Inlined at the end of this email is a patch to implement the latest version 
of the sysfs based watchdog infrastructure refereed to in my previous email
thread.  I have been performing more testing (and makeing bug fixes found in
the testing) and also ported the softdog driver to use this infrastructure, 
and ported a driver donated by Scott Murray for the ZT55XX line of CompactPCI 
cpu blades.

By using this infrastructure, a watchdog driver will pop up in the sysfs
topography, with sysfs control files for performing all the actions that
were traditionally performed via ioctl calls.  In addition the infrastructure
will create a miscdevice that implements all the old ioctl functionality
on behalf of the watchdog driver, increasing code reuse and also making it
easier to remove the watchdog ioctl interface for all watchdog drivers once
the community is ready to make that leap.

I will send patches for the ported drivers on this mail thread.

Here is the system view from the command line when using this patch with
the ported softdog driver:

[root@penguin2 root]# tree /sys/class/watchdog/
/sys/class/watchdog/
|-- devices
`-- drivers

2 directories, 0 files
[root@penguin2 root]# /usr/local/sbin/modprobe softdog
[root@penguin2 root]# tree /sys/class/watchdog/
/sys/class/watchdog/
|-- devices
`-- drivers
    `-- system:softdog -> ../../../bus/system/drivers/softdog

3 directories, 0 files
[root@penguin2 root]# tree /sys/class/watchdog/drivers/system:softdog/
/sys/class/watchdog/drivers/system:softdog/
|-- bootstatus
|-- firmware_version
|-- keepalive
|-- nowayout
|-- options
|-- soft_noboot
|-- start
|-- status
|-- stop
|-- temperature
|-- temppanic
`-- timeout

0 directories, 12 files
[root@penguin2 root]# cd /sys/class/watchdog/drivers/system:softdog/
[root@penguin2 system:softdog]# cat timeout 
60
[root@penguin2 system:softdog]# echo 100 > timeout 
[root@penguin2 system:softdog]# cat timeout 
100

    --rustyl

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1032  -> 1.1040 
#	drivers/char/watchdog/Makefile	1.5     -> 1.8    
#	include/linux/watchdog.h	1.4     -> 1.9    
#	               (new)	        -> 1.5     drivers/char/watchdog/base.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/02/12	rusty@penguin.co.intel.com	1.1033
# Adding a common infrastructure for watchdog timers to implement
# a sysfs interface
# --------------------------------------------
# 03/02/13	rusty@penguin.co.intel.com	1.1034
# Moving wdt_device from a platform_device to a sys_device,
# making stop/start/keepalive be write-only, and removing the get_enable
# and set_enable callbacks.
# --------------------------------------------
# 03/02/13	rusty@penguin.co.intel.com	1.1035
# Changes that should have been adde to the last changeset
# --------------------------------------------
# 03/02/13	rusty@penguin.co.intel.com	1.1036
# Reworked the watchdog infrastructure implementation to use the sysfs
# class concept.
# --------------------------------------------
# 03/02/19	rusty@penguin.co.intel.com	1.1037
# Added support for /dev/watchdog by registering the first watchdog
# as a miscdevice using the watchdog minor.  Also added a new callback
# to enable the firmware version of the watchdog to be queried as 
# specified in watchdog-api.txt.
# --------------------------------------------
# 03/02/19	rusty@penguin.co.intel.com	1.1038
# Removed the dummy1 and dummy2 testing files
# --------------------------------------------
# 03/02/21	rusty@penguin.co.intel.com	1.1039
# * Changed stop callback to not take a string for the watchdog driver
#   to figure out if it contains the magic char, and left all the magic
#   char stuff to the common miscdevice code
# * Fixed code so that module loading and unloading really work.
# --------------------------------------------
# 03/02/21	rusty@penguin.co.intel.com	1.1040
# * changed stop callback to not take a string
# * wrapped kernel specific definitions to only be visible to the kernel
# --------------------------------------------
#
diff -Nru a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
--- a/drivers/char/watchdog/Makefile	Fri Feb 21 14:41:04 2003
+++ b/drivers/char/watchdog/Makefile	Fri Feb 21 14:41:04 2003
@@ -7,6 +7,8 @@
 # watchdog dies or is 'borrowed' for some reason the software watchdog
 # still gives you some cover.
 
+obj-y += base.o
+
 obj-$(CONFIG_PCWATCHDOG) += pcwd.o
 obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
 obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
diff -Nru a/drivers/char/watchdog/base.c b/drivers/char/watchdog/base.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/watchdog/base.c	Fri Feb 21 14:41:04 2003
@@ -0,0 +1,603 @@
+/*
+ * base.c
+ *
+ * Base watchdog timer infrastructure
+ *
+ * Copyright (C) 2003 Rusty Lynch <rusty@linux.co.intel.com>
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Send feedback to <rusty@linux.co.intel.com>
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/watchdog.h>
+
+#include <linux/miscdevice.h>
+
+#ifdef DEBUG
+#define trace(format, args...) \
+        printk(KERN_INFO "%s(" format ")\n", __FUNCTION__ , ## args)
+#define dbg(format, arg...)				\
+		 printk (KERN_DEBUG "%s: " format "\n",	\
+			 __FUNCTION__, ## arg);
+#else
+#define trace(format, arg...) do { } while (0)
+#define dbg(format, arg...) do { } while (0)
+#endif
+
+#define crit(format, arg...) \
+                printk(KERN_CRIT "%s: " format "\n", \
+		       __FUNCTION__ , ## arg)
+
+static struct watchdog_driver *miscdev = 0;
+static int expect_close = 0;
+
+static DECLARE_MUTEX(watchdog_sem);
+
+static int miscdev_open(struct inode *inode, struct file *file)
+{
+	trace("%p, %p", inode, file);
+	if (!miscdev || !miscdev->ops->start)
+		return -ENODEV;
+
+	if (miscdev->ops->start(miscdev))
+		return -EBUSY;
+
+	return 0;
+}
+
+static int miscdev_release(struct inode *inode, struct file *file)
+{
+	trace("%p, %p", inode, file);
+	if (!miscdev || !miscdev->ops->stop)
+		return -ENODEV;
+	
+	if (!expect_close) {
+		crit("watchdog device closed unexpectedly, allowing timeout!");
+		return 0;
+	}
+
+	if (miscdev->ops->stop(miscdev))
+		return -EBUSY;
+
+	return 0;
+}
+
+static ssize_t miscdev_write(struct file *file, const char *data, 
+			     size_t len, loff_t *ppos)
+{
+	trace("%p, %p, %i, %p", file, data, len, ppos);
+
+	/*  Can't seek (pwrite) on this device  */
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	if (!miscdev || !miscdev->ops->keepalive)
+		return -ENODEV;
+
+	/*
+	 *	Refresh the timer.
+	 */
+	if(len) {
+		int nowayout = 0;
+		if (miscdev->ops->get_nowayout)
+			miscdev->ops->get_nowayout(miscdev, &nowayout);
+		if (!nowayout) {
+			size_t i;
+
+			/* In case it was set long ago */
+			expect_close = 0;
+
+			for (i = 0; i != len; i++) {
+				char c;
+
+				if (get_user(c, data + i))
+					return -EFAULT;
+				if (c == 'V')
+					expect_close = 1;
+			}
+		}
+		miscdev->ops->keepalive(miscdev);
+		return 1;
+	}
+	return 0;
+}
+
+static int miscdev_ioctl(struct inode *inode, struct file *file,
+			 unsigned int cmd, unsigned long arg)
+{
+	int tmp;
+	static struct watchdog_info ident = {
+		.identity = "Watchdog Device",
+	};
+
+	trace("%p, %p, %i, %li", inode, file, cmd, arg);
+	if (!miscdev)
+		return -ENODEV;	
+
+	switch (cmd) {
+	default:
+		return -ENOTTY;
+	case WDIOC_GETSUPPORT:
+		if (miscdev->ops->get_firmware_version && 
+		    !miscdev->ops->get_firmware_version(miscdev, &tmp))
+			ident.firmware_version = tmp;
+			
+		if (miscdev->ops->get_options && 
+		    !miscdev->ops->get_options(miscdev, &tmp))
+			ident.options = tmp;		
+		
+		if(copy_to_user((struct watchdog_info *)arg, &ident, 
+				sizeof(ident)))
+			return -EFAULT;
+		return 0;		
+	case WDIOC_GETSTATUS:
+		if (!miscdev->ops->get_status || 
+		    miscdev->ops->get_status(miscdev, &tmp))
+			return -EFAULT;
+
+		tmp |= WDIOF_MAGICCLOSE;
+		if(copy_to_user((struct watchdog_info *)arg,&tmp,sizeof(tmp)))
+			return -EFAULT;		
+		return 0;
+	case WDIOC_GETBOOTSTATUS:
+		if (!miscdev->ops->get_bootstatus || 
+		    miscdev->ops->get_bootstatus(miscdev, &tmp))
+			return -EFAULT;
+
+		if(copy_to_user((struct watchdog_info *)arg,&tmp,sizeof(tmp)))
+			return -EFAULT;
+		
+		return 0;
+	case WDIOC_GETTEMP:
+		if (!miscdev->ops->get_temperature || 
+		    miscdev->ops->get_temperature(miscdev, &tmp))
+			return -EFAULT;
+
+		if(copy_to_user((struct watchdog_info *)arg,&tmp,sizeof(tmp)))
+			return -EFAULT;
+		
+		return 0;
+	case WDIOC_SETOPTIONS:
+		if (get_user(tmp, (int *)arg))
+			return -EFAULT;
+
+		if (tmp & WDIOS_DISABLECARD) {
+			if (!miscdev->ops->stop || 
+			    miscdev->ops->stop(miscdev))
+				return -EFAULT;			
+		}
+
+		if (tmp & WDIOS_ENABLECARD) {
+			if (!miscdev->ops->start || 
+			    miscdev->ops->start(miscdev))
+				return -EFAULT;			
+		}
+
+		if (tmp & WDIOS_TEMPPANIC) {
+			if (!miscdev->ops->set_temppanic || 
+			    miscdev->ops->set_temppanic(miscdev, 1))
+				return -EFAULT;			
+		}
+
+		return 0;
+	case WDIOC_KEEPALIVE:
+		if (!miscdev->ops->keepalive || 
+		    miscdev->ops->keepalive(miscdev))
+			return -EFAULT;
+
+		return 0;
+	case WDIOC_SETTIMEOUT:
+		if (!miscdev->ops->set_timeout)
+			return -EFAULT;
+		if (get_user(tmp, (int *)arg))
+			return -EFAULT;
+		if (tmp < 1)
+			return -EINVAL;
+
+		if (miscdev->ops->set_timeout(miscdev, tmp))
+			return -EFAULT;
+
+		if (!miscdev->ops->keepalive || 
+		    miscdev->ops->keepalive(miscdev))
+			return -EFAULT;
+
+		return 0;
+	case WDIOC_GETTIMEOUT:
+		if (!miscdev->ops->get_timeout || 
+		    miscdev->ops->get_timeout(miscdev, &tmp))
+			return -EFAULT;
+
+		if(copy_to_user((struct watchdog_info *)arg,&tmp,sizeof(tmp)))
+			return -EFAULT;
+		
+		return 0;
+	}
+}
+
+static struct file_operations watchdog_fops = {
+	.owner		= THIS_MODULE,
+	.write		= miscdev_write,
+	.ioctl		= miscdev_ioctl,
+	.open		= miscdev_open,
+	.release	= miscdev_release,
+};
+
+static struct miscdevice watchdog_miscdev = {
+	.minor		= WATCHDOG_MINOR,
+	.name		= "Watchdog Timer",
+	.fops		= &watchdog_fops,
+};
+
+ssize_t start_store(struct device_driver *d, const char *buf, size_t count)
+{
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->start))
+		return -ENODEV;
+
+	if ((w->ops->start)(w))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(start,S_IWUSR,NULL,start_store);
+
+ssize_t stop_store(struct device_driver *d, const char *buf, size_t count)
+{
+	int nowayout = 0;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->stop))
+		return -ENODEV;
+
+	if ((w->ops->stop)(w))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(stop,S_IWUSR,NULL,stop_store);
+
+ssize_t timeout_show(struct device_driver *d, char *buf)
+{
+	int timeout;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_timeout))
+		return -ENODEV;
+
+	if((w->ops->get_timeout)(w, &timeout))
+		return -EFAULT;
+
+	return sprintf(buf, "%i\n", timeout);
+}
+ssize_t timeout_store(struct device_driver *d, const char *buf, size_t count)
+{
+	int timeout;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->set_timeout))
+		return -ENODEV;
+
+	if (!sscanf(buf,"%i",&timeout))
+		return -EINVAL;
+
+	if ((w->ops->set_timeout)(w, timeout))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(timeout,S_IRUGO|S_IWUSR,timeout_show,timeout_store);
+
+ssize_t keepalive_store(struct device_driver *d, const char *buf, size_t count)
+{
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->keepalive))
+		return -ENODEV;
+
+	if ((w->ops->keepalive)(w))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(keepalive,S_IWUSR,NULL,keepalive_store);
+
+ssize_t nowayout_show(struct device_driver *d, char *buf)
+{
+	int nowayout;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_nowayout))
+		return -ENODEV;
+
+	if ((w->ops->get_nowayout)(w, &nowayout))
+		return -EFAULT;
+
+	return sprintf(buf, "%i\n", nowayout);
+}
+ssize_t nowayout_store(struct device_driver *d, const char *buf, size_t count)
+{
+	int nowayout;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->set_nowayout))
+		return -ENODEV;
+
+	if (!sscanf(buf,"%i",&nowayout))
+		return -EINVAL;
+
+	if ((w->ops->set_nowayout)(w, nowayout))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(nowayout,S_IRUGO|S_IWUSR,nowayout_show,nowayout_store);
+
+ssize_t status_show(struct device_driver *d, char *buf)
+{
+	int status;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_status))
+		return -ENODEV;
+
+	if ((w->ops->get_status)(w, &status))
+		return -EFAULT;
+
+	return sprintf(buf, "0x%08x\n", status);
+}
+DRIVER_ATTR(status,S_IRUGO,status_show,NULL);
+
+ssize_t bootstatus_show(struct device_driver *d, char *buf)
+{
+	int bootstatus;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_bootstatus))
+		return -ENODEV;
+
+	if ((w->ops->get_bootstatus)(w, &bootstatus))
+		return -EFAULT;
+
+	return sprintf(buf, "0x%08x\n", bootstatus);
+}
+DRIVER_ATTR(bootstatus,S_IRUGO,bootstatus_show,NULL);
+
+ssize_t options_show(struct device_driver *d, char *buf)
+{
+	int options;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_options))
+		return -ENODEV;
+
+	if ((w->ops->get_options)(w, &options))
+		return -EFAULT;
+
+	return sprintf(buf, "0x%08x\n", options);
+}
+DRIVER_ATTR(options,S_IRUGO,options_show,NULL);
+
+ssize_t temperature_show(struct device_driver *d, char *buf)
+{
+	int temperature;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_temperature))
+		return -ENODEV;
+
+	if ((w->ops->get_temperature)(w, &temperature))
+		return -EFAULT;
+
+	return sprintf(buf, "%i\n", temperature);
+}
+DRIVER_ATTR(temperature,S_IRUGO,temperature_show,NULL);
+
+ssize_t temppanic_show(struct device_driver *d, char *buf)
+{
+	int temppanic;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_temppanic))
+		return -ENODEV;
+
+	if ((w->ops->get_temppanic)(w, &temppanic))
+		return -EFAULT;
+
+	return sprintf(buf, "%i\n", temppanic);
+}
+ssize_t temppanic_store(struct device_driver *d, const char *buf, size_t count)
+{
+	int temppanic;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p, %i", d, buf, count);
+	if (!(w->ops->set_temppanic))
+		return -ENODEV;
+
+	if (!sscanf(buf,"%i",&temppanic))
+		return -EINVAL;
+
+	if ((w->ops->set_temppanic)(w, temppanic))
+		return -EFAULT;
+
+	return count;
+}
+DRIVER_ATTR(temppanic,S_IRUGO|S_IWUSR,temppanic_show,temppanic_store);
+
+ssize_t firmware_version_show(struct device_driver *d, char *buf)
+{
+	int version;
+	struct watchdog_driver *w = to_watchdog_driver(d);
+
+	trace("%p, %p", d, buf);
+	if (!(w->ops->get_firmware_version))
+		return -ENODEV;
+
+	if ((w->ops->get_firmware_version)(w, &version))
+		return -EFAULT;
+
+	return sprintf(buf, "0x%08x\n", version);
+}
+DRIVER_ATTR(firmware_version,S_IRUGO,firmware_version_show,NULL);
+
+static int watchdog_create_default_files(struct watchdog_driver *d)
+{
+	driver_create_file(&(d->driver), &driver_attr_start);
+	driver_create_file(&(d->driver), &driver_attr_stop);
+	driver_create_file(&(d->driver), &driver_attr_timeout);
+	driver_create_file(&(d->driver), &driver_attr_keepalive);
+	driver_create_file(&(d->driver), &driver_attr_nowayout);
+	driver_create_file(&(d->driver), &driver_attr_status);
+	driver_create_file(&(d->driver), &driver_attr_bootstatus);
+	driver_create_file(&(d->driver), &driver_attr_options);
+	driver_create_file(&(d->driver), &driver_attr_temperature);
+	driver_create_file(&(d->driver), &driver_attr_temppanic);
+	driver_create_file(&(d->driver), &driver_attr_firmware_version);
+
+	return 0;
+}
+
+static void watchdog_remove_default_files(struct watchdog_driver *d)
+{
+	driver_remove_file(&(d->driver), &driver_attr_start);
+	driver_remove_file(&(d->driver), &driver_attr_stop);
+	driver_remove_file(&(d->driver), &driver_attr_timeout);
+	driver_remove_file(&(d->driver), &driver_attr_keepalive);
+	driver_remove_file(&(d->driver), &driver_attr_nowayout);
+	driver_remove_file(&(d->driver), &driver_attr_status);
+	driver_remove_file(&(d->driver), &driver_attr_bootstatus);
+	driver_remove_file(&(d->driver), &driver_attr_options);
+	driver_remove_file(&(d->driver), &driver_attr_temperature);
+	driver_remove_file(&(d->driver), &driver_attr_temppanic);
+	driver_remove_file(&(d->driver), &driver_attr_firmware_version);
+}
+
+int watchdog_driver_register(struct watchdog_driver *d) 
+{
+	int rv;
+
+	down(&watchdog_sem);
+
+	if (!get_driver(&(d->driver))) {
+		crit("unable to gain reference for %s driver", d->driver.name);
+		rv = -EFAULT;
+		goto error1;
+	}
+
+	rv = driver_register(&(d->driver));
+	if (rv) {
+		crit("unable to register %s driver", d->driver.name);
+		goto error2;
+	}
+
+	rv = watchdog_create_default_files(d);
+	if (rv) {
+		crit("unable to create %s driver sysfs files", d->driver.name);
+		goto error3;
+	}
+
+	dbg("miscdev  pointing to %s",miscdev? miscdev->driver.name:"nobody");
+	if (!miscdev) { 
+		if (misc_register(&watchdog_miscdev) == 0) {
+			dbg("registered %s as the miscdev", d->driver.name);
+			miscdev = d;
+		} else {
+			crit("%s failed misc_register()", d->driver.name);
+		}
+	}
+	up(&watchdog_sem);
+
+	return rv;
+
+ error3:
+	driver_unregister(&(d->driver));
+ error2:
+	put_driver(&(d->driver));
+ error1:
+	up(&watchdog_sem);
+	return rv;
+
+}
+
+void watchdog_driver_unregister(struct watchdog_driver *d)
+{
+	down(&watchdog_sem);
+	if (d && miscdev == d) {
+		dbg("deregistering %s as the misc device", d->driver.name);
+		miscdev = 0;
+		misc_deregister(&watchdog_miscdev);
+	}
+	
+	watchdog_remove_default_files(d);
+	driver_unregister(&(d->driver));
+	put_driver(&(d->driver));
+	up(&watchdog_sem);
+}
+
+struct device_class watchdog_devclass = {
+        .name		= "watchdog",
+};
+
+static int __init watchdog_init(void)
+{
+	int ret = 0;
+
+	trace();
+	ret = devclass_register(&watchdog_devclass);
+	if (ret)
+		return -ENODEV;
+
+	return 0;
+}
+
+static void __exit watchdog_exit(void)
+{
+	trace();
+	
+	if (miscdev)
+		misc_deregister(&watchdog_miscdev);
+
+	devclass_unregister(&watchdog_devclass);
+}
+
+module_init(watchdog_init);
+module_exit(watchdog_exit);
+EXPORT_SYMBOL_GPL(watchdog_driver_register);
+EXPORT_SYMBOL_GPL(watchdog_driver_unregister);
+EXPORT_SYMBOL_GPL(watchdog_devclass);
diff -Nru a/include/linux/watchdog.h b/include/linux/watchdog.h
--- a/include/linux/watchdog.h	Fri Feb 21 14:41:04 2003
+++ b/include/linux/watchdog.h	Fri Feb 21 14:41:04 2003
@@ -10,9 +10,17 @@
 #define _LINUX_WATCHDOG_H
 
 #include <linux/ioctl.h>
+#include <linux/device.h>
 
 #define	WATCHDOG_IOCTL_BASE	'W'
 
+#define to_watchdog_driver(n) container_of(n, struct watchdog_driver, driver)
+
+struct watchdog_driver;
+struct watchdog_ops;
+
+extern struct device_class watchdog_devclass;
+
 struct watchdog_info {
 	__u32 options;		/* Options the card/driver supports */
 	__u32 firmware_version;	/* Firmware version of the card */
@@ -46,4 +54,32 @@
 #define	WDIOS_ENABLECARD	0x0002	/* Turn on the watchdog timer */
 #define	WDIOS_TEMPPANIC		0x0004	/* Kernel panic on temperature trip */
 
+#ifdef __KERNEL__
+
+struct watchdog_driver {
+	struct watchdog_ops *ops;
+	struct device_driver driver;
+};
+
+struct watchdog_ops {
+	int (*start)(struct watchdog_driver *);
+	int (*stop)(struct watchdog_driver *);
+	int (*keepalive)(struct watchdog_driver *);
+	int (*get_timeout)(struct watchdog_driver *, int *);
+	int (*set_timeout)(struct watchdog_driver *, int);
+	int (*get_nowayout)(struct watchdog_driver *, int *);
+	int (*set_nowayout)(struct watchdog_driver *, int);
+	int (*get_status)(struct watchdog_driver *, int *);
+	int (*get_bootstatus)(struct watchdog_driver *, int *);
+	int (*get_options)(struct watchdog_driver *, int *);
+	int (*get_temperature)(struct watchdog_driver *, int *);
+	int (*get_temppanic)(struct watchdog_driver *, int *);
+	int (*set_temppanic)(struct watchdog_driver *, int);
+	int (*get_firmware_version)(struct watchdog_driver *, int *);
+};
+
+int  watchdog_driver_register(struct watchdog_driver *);
+void watchdog_driver_unregister(struct watchdog_driver *);
+
+#endif /* ifdef __KERNEL__ */
 #endif  /* ifndef _LINUX_WATCHDOG_H */




^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH][2.5] Sysfs enabling watchdog infrastructure
  2003-02-21 23:21 [PATCH][2.5] Sysfs enabling watchdog infrastructure Rusty Lynch
@ 2003-02-21 23:31 ` Rusty Lynch
  2003-02-21 23:36 ` Rusty Lynch
  1 sibling, 0 replies; 3+ messages in thread
From: Rusty Lynch @ 2003-02-21 23:31 UTC (permalink / raw)
  To: Rusty Lynch; +Cc: Alan Cox, lkml

[-- Attachment #1: Type: text/plain, Size: 9338 bytes --]

The following is a patch against the 2.5.60 kernel that ports the
softdog driver to register as a watchdog_driver and allow the watchdog
infrastructure to take care of details like sysfs and presenting a miscdevice
for the watchdog minor.

There are enough changes to make the patch difficult to read so I am
also attaching softdog.c to make it easier to see what the file is doing.

    --rustyl

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1040  -> 1.1041 
#	drivers/char/watchdog/softdog.c	1.14    -> 1.15   
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/02/21	rusty@penguin.co.intel.com	1.1041
# * ported driver to new sysfs based watchdog infrastructure
# * change from using old MODULE_PARM macro to the new module_param
#   function call
# --------------------------------------------
#
diff -Nru a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c
--- a/drivers/char/watchdog/softdog.c	Fri Feb 21 14:48:49 2003
+++ b/drivers/char/watchdog/softdog.c	Fri Feb 21 14:48:49 2003
@@ -1,5 +1,5 @@
 /*
- *	SoftDog	0.06:	A Software Watchdog Device
+ *	SoftDog	0.07:	A Software Watchdog Device
  *
  *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
  *				http://www.redhat.com
@@ -34,52 +34,41 @@
  *
  *  20020530 Joel Becker <joel.becker@oracle.com>
  *  	Added Matt Domsch's nowayout module option.
+ * 
+ *  20030221 Rusty Lynch <rusty@linux.co.intel.com>
+ *      Moved implementation to use the new watchdog infrastructure.  This
+ *      adds the softdog to the sysfs topography and moves the common 
+ *      miscdevice functions to the infrastructure as well.
+ *       
  */
  
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/config.h>
 #include <linux/types.h>
-#include <linux/miscdevice.h>
 #include <linux/watchdog.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
 
 #define TIMER_MARGIN	60		/* (secs) Default is 1 minute */
-
-static int expect_close = 0;
 static int soft_margin = TIMER_MARGIN;	/* in seconds */
-#ifdef ONLY_TESTING
-static int soft_noboot = 1;
-#else
-static int soft_noboot = 0;
-#endif  /* ONLY_TESTING */
+module_param(soft_margin,int,0);
+MODULE_PARM_DESC(soft_margin, "Watchdog timer margin (timeout) in seconds");
 
-MODULE_PARM(soft_margin,"i");
-MODULE_PARM(soft_noboot,"i");
-MODULE_LICENSE("GPL");
+static int soft_noboot = 0;
+module_param(soft_noboot,int,0);
+MODULE_PARM_DESC(soft_noboot, "Disable machine restarts on watchdog timeout");
 
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
 static int nowayout = 1;
 #else
 static int nowayout = 0;
 #endif
-
-MODULE_PARM(nowayout,"i");
+module_param(nowayout,int,0);
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
 
 /*
- *	Our timer
- */
- 
-static void watchdog_fire(unsigned long);
-
-static struct timer_list watchdog_ticktock =
-		TIMER_INITIALIZER(watchdog_fire, 0, 0);
-static unsigned long timer_alive;
-
-
-/*
  *	If the timer expires..
  */
  
@@ -96,137 +85,154 @@
 }
 
 /*
- *	Allow only one person to hold it open
+ *	Software timer
  */
  
-static int softdog_open(struct inode *inode, struct file *file)
+static struct timer_list watchdog_ticktock =
+		TIMER_INITIALIZER(watchdog_fire, 0, 0);
+
+
+/* 
+ *       Watchdog ops callback functions
+ */
+
+static int softdog_start(struct watchdog_driver *d)
 {
-	if(test_and_set_bit(0, &timer_alive))
-		return -EBUSY;
-	if (nowayout) {
-		MOD_INC_USE_COUNT;
-	}
-	/*
-	 *	Activate timer
-	 */
 	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
 	return 0;
 }
 
-static int softdog_release(struct inode *inode, struct file *file)
+static int softdog_stop(struct watchdog_driver *d)
 {
-	/*
-	 *	Shut off the timer.
-	 * 	Lock it in if it's a module and we set nowayout
-	 */
-	if (expect_close) {
-		del_timer(&watchdog_ticktock);
-	} else {
-		printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly.  WDT will not stop!\n");
+	if (nowayout) {
+		printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly.  "
+		       "WDT will not stop!\n");
+		return -1;
 	}
-	clear_bit(0, &timer_alive);
+
+	del_timer(&watchdog_ticktock);
 	return 0;
 }
 
-static ssize_t softdog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
+static int softdog_keepalive(struct watchdog_driver *d) 
 {
-	/*  Can't seek (pwrite) on this device  */
-	if (ppos != &file->f_pos)
-		return -ESPIPE;
+	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
+	return 0;
+}
 
-	/*
-	 *	Refresh the timer.
-	 */
-	if(len) {
-		if (!nowayout) {
-			size_t i;
+static int softdog_get_timeout(struct watchdog_driver *d, int *timeout)
+{
+	*timeout = soft_margin;
+	return 0;
+}
 
-			/* In case it was set long ago */
-			expect_close = 0;
+static int softdog_set_timeout(struct watchdog_driver *d, int timeout)
+{
+	if (timeout < 1)
+		return -1;
 
-			for (i = 0; i != len; i++) {
-				char c;
+	soft_margin = timeout;
+	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
+	return 0;
+}
 
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 1;
-			}
-		}
-		mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
-		return 1;
-	}
+static int softdog_get_options(struct watchdog_driver *d, int *options)
+{
+	*options =  WDIOF_SETTIMEOUT;
 	return 0;
 }
 
-static int softdog_ioctl(struct inode *inode, struct file *file,
-	unsigned int cmd, unsigned long arg)
+static int softdog_get_nowayout(struct watchdog_driver *d, int *n)
 {
-	int new_margin;
-	static struct watchdog_info ident = {
-		.options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
-		.identity = "Software Watchdog",
-	};
-	switch (cmd) {
-		default:
-			return -ENOTTY;
-		case WDIOC_GETSUPPORT:
-			if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
-				return -EFAULT;
-			return 0;
-		case WDIOC_GETSTATUS:
-		case WDIOC_GETBOOTSTATUS:
-			return put_user(0,(int *)arg);
-		case WDIOC_KEEPALIVE:
-			mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
-			return 0;
-		case WDIOC_SETTIMEOUT:
-			if (get_user(new_margin, (int *)arg))
-				return -EFAULT;
-			if (new_margin < 1)
-				return -EINVAL;
-			soft_margin = new_margin;
-			mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
-			/* Fall */
-		case WDIOC_GETTIMEOUT:
-			return put_user(soft_margin, (int *)arg);
-	}
+	*n = nowayout;
+	return 0;
 }
 
-static struct file_operations softdog_fops = {
-	.owner		= THIS_MODULE,
-	.write		= softdog_write,
-	.ioctl		= softdog_ioctl,
-	.open		= softdog_open,
-	.release	= softdog_release,
+static int softdog_set_nowayout(struct watchdog_driver *d, int n)
+{
+	if (n)
+		nowayout = 1;
+	else 
+		nowayout = 0;
+	return 0;
+}
+
+/*
+ *      Structures required to register as a watchdog driver
+ */
+
+static struct watchdog_ops softdog_ops = {
+	.start                 = softdog_start,
+	.stop                  = softdog_stop,
+	.keepalive             = softdog_keepalive,
+	.get_timeout           = softdog_get_timeout,
+	.set_timeout           = softdog_set_timeout,
+	.get_nowayout          = softdog_get_nowayout,
+	.set_nowayout          = softdog_set_nowayout,
+	.get_options           = softdog_get_options,
+	/* get_bootstatus not implemented */
+	/* get_status not implemented */
+	/* get/set_temppanic not implemented */
+	/* get_firmware_version not implemented */
 };
 
-static struct miscdevice softdog_miscdev = {
-	.minor		= WATCHDOG_MINOR,
-	.name		= "watchdog",
-	.fops		= &softdog_fops,
+static struct watchdog_driver softdog_driver = {
+	.ops = &softdog_ops,
+	.driver = {
+		.name		= "softdog",
+		.bus		= &system_bus_type,
+		.devclass       = &watchdog_devclass,
+	}
 };
 
-static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.06, soft_margin: %d sec, nowayout: %d\n";
+/* 
+ *      enable testing the of driver to not cause a machine restart 
+ */
+
+static ssize_t soft_noboot_show(struct device_driver * d, char * buf)
+{
+	return sprintf(buf, "%i\n",soft_noboot);
+}
+static ssize_t soft_noboot_store(struct device_driver *d,const char * buf, 
+				 size_t count)
+{
+	int tmp;
+
+	if (sscanf(buf,"%i",&tmp) != 1)
+		return -EINVAL;
+
+	if (tmp)
+		soft_noboot = 1;
+	else
+		soft_noboot = 0;
+	return count;
+}
+DRIVER_ATTR(soft_noboot,0644,soft_noboot_show,soft_noboot_store);
+
+static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07, soft_margin: %d sec, nowayout: %d\n";
 
 static int __init watchdog_init(void)
 {
 	int ret;
 
-	ret = misc_register(&softdog_miscdev);
-
+	ret = watchdog_driver_register(&softdog_driver);
 	if (ret)
 		return ret;
 
+	driver_create_file(&softdog_driver.driver, &driver_attr_soft_noboot);
 	printk(banner, soft_margin, nowayout);
-
 	return 0;
 }
 
 static void __exit watchdog_exit(void)
-{
-	misc_deregister(&softdog_miscdev);
+{     
+	driver_remove_file(&softdog_driver.driver, &driver_attr_soft_noboot);
+	watchdog_driver_unregister(&softdog_driver);
+
+	/* ensure somebody didn't leave the watchdog ticking */
+	del_timer(&watchdog_ticktock);
 }
 
 module_init(watchdog_init);
 module_exit(watchdog_exit);
+MODULE_LICENSE("GPL");

[-- Attachment #2: softdog.c --]
[-- Type: text/x-c, Size: 6276 bytes --]

/*
 *	SoftDog	0.07:	A Software Watchdog Device
 *
 *	(c) Copyright 1996 Alan Cox <alan@redhat.com>, All Rights Reserved.
 *				http://www.redhat.com
 *
 *	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.
 *	
 *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide 
 *	warranty for any of this software. This material is provided 
 *	"AS-IS" and at no charge.	
 *
 *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
 *
 *	Software only watchdog driver. Unlike its big brother the WDT501P
 *	driver this won't always recover a failed machine.
 *
 *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
 *	Modularised.
 *	Added soft_margin; use upon insmod to change the timer delay.
 *	NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
 *	    minors.
 *
 *  19980911 Alan Cox
 *	Made SMP safe for 2.3.x
 *
 *  20011127 Joel Becker (jlbec@evilplan.org>
 *	Added soft_noboot; Allows testing the softdog trigger without 
 *	requiring a recompile.
 *	Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
 *
 *  20020530 Joel Becker <joel.becker@oracle.com>
 *  	Added Matt Domsch's nowayout module option.
 * 
 *  20030221 Rusty Lynch <rusty@linux.co.intel.com>
 *      Moved implementation to use the new watchdog infrastructure.  This
 *      adds the softdog to the sysfs topography and moves the common 
 *      miscdevice functions to the infrastructure as well.
 *       
 */
 
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <asm/uaccess.h>

#define TIMER_MARGIN	60		/* (secs) Default is 1 minute */
static int soft_margin = TIMER_MARGIN;	/* in seconds */
module_param(soft_margin,int,0);
MODULE_PARM_DESC(soft_margin, "Watchdog timer margin (timeout) in seconds");

static int soft_noboot = 0;
module_param(soft_noboot,int,0);
MODULE_PARM_DESC(soft_noboot, "Disable machine restarts on watchdog timeout");

#ifdef CONFIG_WATCHDOG_NOWAYOUT
static int nowayout = 1;
#else
static int nowayout = 0;
#endif
module_param(nowayout,int,0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");

/*
 *	If the timer expires..
 */
 
static void watchdog_fire(unsigned long data)
{
	if (soft_noboot)
		printk(KERN_CRIT "SOFTDOG: Triggered - Reboot ignored.\n");
	else
	{
		printk(KERN_CRIT "SOFTDOG: Initiating system reboot.\n");
		machine_restart(NULL);
		printk("SOFTDOG: Reboot didn't ?????\n");
	}
}

/*
 *	Software timer
 */
 
static struct timer_list watchdog_ticktock =
		TIMER_INITIALIZER(watchdog_fire, 0, 0);


/* 
 *       Watchdog ops callback functions
 */

static int softdog_start(struct watchdog_driver *d)
{
	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
	return 0;
}

static int softdog_stop(struct watchdog_driver *d)
{
	if (nowayout) {
		printk(KERN_CRIT "SOFTDOG: WDT device closed unexpectedly.  "
		       "WDT will not stop!\n");
		return -1;
	}

	del_timer(&watchdog_ticktock);
	return 0;
}

static int softdog_keepalive(struct watchdog_driver *d) 
{
	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
	return 0;
}

static int softdog_get_timeout(struct watchdog_driver *d, int *timeout)
{
	*timeout = soft_margin;
	return 0;
}

static int softdog_set_timeout(struct watchdog_driver *d, int timeout)
{
	if (timeout < 1)
		return -1;

	soft_margin = timeout;
	mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
	return 0;
}

static int softdog_get_options(struct watchdog_driver *d, int *options)
{
	*options =  WDIOF_SETTIMEOUT;
	return 0;
}

static int softdog_get_nowayout(struct watchdog_driver *d, int *n)
{
	*n = nowayout;
	return 0;
}

static int softdog_set_nowayout(struct watchdog_driver *d, int n)
{
	if (n)
		nowayout = 1;
	else 
		nowayout = 0;
	return 0;
}

/*
 *      Structures required to register as a watchdog driver
 */

static struct watchdog_ops softdog_ops = {
	.start                 = softdog_start,
	.stop                  = softdog_stop,
	.keepalive             = softdog_keepalive,
	.get_timeout           = softdog_get_timeout,
	.set_timeout           = softdog_set_timeout,
	.get_nowayout          = softdog_get_nowayout,
	.set_nowayout          = softdog_set_nowayout,
	.get_options           = softdog_get_options,
	/* get_bootstatus not implemented */
	/* get_status not implemented */
	/* get/set_temppanic not implemented */
	/* get_firmware_version not implemented */
};

static struct watchdog_driver softdog_driver = {
	.ops = &softdog_ops,
	.driver = {
		.name		= "softdog",
		.bus		= &system_bus_type,
		.devclass       = &watchdog_devclass,
	}
};

/* 
 *      enable testing the of driver to not cause a machine restart 
 */

static ssize_t soft_noboot_show(struct device_driver * d, char * buf)
{
	return sprintf(buf, "%i\n",soft_noboot);
}
static ssize_t soft_noboot_store(struct device_driver *d,const char * buf, 
				 size_t count)
{
	int tmp;

	if (sscanf(buf,"%i",&tmp) != 1)
		return -EINVAL;

	if (tmp)
		soft_noboot = 1;
	else
		soft_noboot = 0;
	return count;
}
DRIVER_ATTR(soft_noboot,0644,soft_noboot_show,soft_noboot_store);

static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07, soft_margin: %d sec, nowayout: %d\n";

static int __init watchdog_init(void)
{
	int ret;

	ret = watchdog_driver_register(&softdog_driver);
	if (ret)
		return ret;

	driver_create_file(&softdog_driver.driver, &driver_attr_soft_noboot);
	printk(banner, soft_margin, nowayout);
	return 0;
}

static void __exit watchdog_exit(void)
{     
	driver_remove_file(&softdog_driver.driver, &driver_attr_soft_noboot);
	watchdog_driver_unregister(&softdog_driver);

	/* ensure somebody didn't leave the watchdog ticking */
	del_timer(&watchdog_ticktock);
}

module_init(watchdog_init);
module_exit(watchdog_exit);
MODULE_LICENSE("GPL");

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH][2.5] Sysfs enabling watchdog infrastructure
  2003-02-21 23:21 [PATCH][2.5] Sysfs enabling watchdog infrastructure Rusty Lynch
  2003-02-21 23:31 ` Rusty Lynch
@ 2003-02-21 23:36 ` Rusty Lynch
  1 sibling, 0 replies; 3+ messages in thread
From: Rusty Lynch @ 2003-02-21 23:36 UTC (permalink / raw)
  To: Rusty Lynch; +Cc: Alan Cox, lkml

The following is a patch against the 2.5.60 kernel that adds a new driver
for the hardware watchdog timers devices on the zt55xx line of cpci compute 
blades.  The driver is writen as a watchdog_driver utilizing the previously
submitted watchdog infrastructure.

    --rustyl

# This is a BitKeeper generated patch for the following project:
# Project Name: Linux kernel tree
# This patch format is intended for GNU patch command version 2.5 or higher.
# This patch includes the following deltas:
#	           ChangeSet	1.1041  -> 1.1042 
#	drivers/char/watchdog/Kconfig	1.6     -> 1.7    
#	drivers/char/watchdog/Makefile	1.8     -> 1.9    
#	               (new)	        -> 1.1     drivers/char/watchdog/ztwd.c
#
# The following is the BitKeeper ChangeSet Log
# --------------------------------------------
# 03/02/21	rusty@penguin.co.intel.com	1.1042
# Adding a new watchdog driver for the ZT55XX line of CompactPCI
# single board computers.
# --------------------------------------------
#
diff -Nru a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
--- a/drivers/char/watchdog/Kconfig	Fri Feb 21 15:06:37 2003
+++ b/drivers/char/watchdog/Kconfig	Fri Feb 21 15:06:37 2003
@@ -354,4 +354,18 @@
 	  Documentation/modules.txt. The module will be called
 	  wafer5823wdt.o
 
+config ZT55XX_WDT
+	tristate "ZT55XX Single Board Computer Watchdog"
+	depends on WATCHDOG
+	help
+          This is a driver for the hardware watchdog on the ZT55XX line 
+          of CompactPCI single board computers originally sold by Ziatech,
+          and then Intel, and then Performance Technologies.
+
+	  This driver is also available as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want).
+	  If you want to compile it as a module, say M here and read
+	  Documentation/modules.txt. The module will be called
+	  ztwd.ko
+
 endmenu
diff -Nru a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
--- a/drivers/char/watchdog/Makefile	Fri Feb 21 15:06:37 2003
+++ b/drivers/char/watchdog/Makefile	Fri Feb 21 15:06:37 2003
@@ -31,3 +31,4 @@
 obj-$(CONFIG_ALIM7101_WDT) += alim7101_wdt.o
 obj-$(CONFIG_SC1200_WDT) += sc1200wdt.o
 obj-$(CONFIG_WAFER_WDT) += wafer5823wdt.o
+obj-$(CONFIG_ZT55XX_WDT) += ztwd.o
diff -Nru a/drivers/char/watchdog/ztwd.c b/drivers/char/watchdog/ztwd.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/watchdog/ztwd.c	Fri Feb 21 15:06:37 2003
@@ -0,0 +1,391 @@
+/*
+ * ztwd.c
+ *
+ * Intel/Ziatech ZT55xx watchdog driver
+ *
+ * Copyright (C) 2003 Rusty Lynch <rusty@linux.co.intel.com>
+ * 
+ * Based on original work from SOMA Networks. Original copyright:
+ * Copyright 2001-2003 SOMA Networks, Inc.
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
+ * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Contributors:
+ * Scott Murray <scottm@somanetworks.com>
+ * Rusty Lynch <rusty@linux.co.intel.com>
+ *
+ * Send feedback to <rusty@linux.co.intel.com>
+ */
+
+/*
+ * This driver implements the IO controlled watchdog device found
+ * on the ZT55XX line of single board computers. 
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/watchdog.h>
+#include <asm/io.h>
+
+#define DEBUG 1
+#ifdef DEBUG
+#define dbg(format, arg...)				\
+		 printk (KERN_DEBUG "%s: " format "\n",	\
+			 __FUNCTION__, ## arg);
+#define trace(format, arg...)				 \
+                 printk(KERN_INFO "%s(" format ")\n",    \
+		        __FUNCTION__ , ## arg);
+#else
+#define trace(format, arg...) do { } while (0)
+#define dbg(format, arg...) do { } while (0)
+#endif
+
+#define err(format, arg...) \
+                printk(KERN_ERR "%s: " format "\n", \
+		       __FUNCTION__ , ## arg)
+#define info(format, arg...) \
+                printk(KERN_INFO "%s: " format "\n", \
+		       __FUNCTION__ , ## arg)
+#define warn(format, arg...) \
+                printk(KERN_WARNING "%s: " format "\n", \
+		       __FUNCTION__ , ## arg)
+
+#define WD_CTL_REG	       0x79
+
+#define WD_TERMINALCOUNT_MASK  0x07
+#define WD_STAGE1_ACTION_MASK  0x08
+#define WD_STAGE1_ENABLE_MASK  0x10
+#define WD_STAGE2_ENABLE_MASK  0x20
+#define WD_STAGE2_MONITOR_MASK 0x40
+#define WD_STAGE1_MONITOR_MASK 0x80
+
+#define WD_TC_250MS	       0x00
+#define WD_TC_500MS	       0x01
+#define WD_TC_1S	       0x02
+#define WD_TC_8S	       0x03
+#define WD_TC_32S	       0x04
+#define WD_TC_64S	       0x05
+#define WD_TC_128S	       0x06
+#define WD_TC_256S	       0x07
+
+#define DEFAULT_TIMEOUT          64  /* seconds */
+
+static int nowayout = 0;
+static int bootstatus = 0;
+static int status = 0;
+static int pretimeout = 0;
+
+static int ztwd_start(struct watchdog_driver *d)
+{
+    	uint8_t value;
+
+	trace("%s", d->driver.name);
+	value = inb(WD_CTL_REG);
+	if (pretimeout)
+		value |= WD_STAGE1_ENABLE_MASK;
+	value |= WD_STAGE2_ENABLE_MASK;
+	outb(value, WD_CTL_REG);
+	return 0;
+}
+
+static int ztwd_stop(struct watchdog_driver *d)
+{
+    	uint8_t value;
+
+	trace("%s", d->driver.name);
+	if (nowayout) {
+		warn("Nowayout flag is set.  Request to stop timer denied!");
+		return -1;
+	}
+
+	value = inb(WD_CTL_REG);
+	value &= ~WD_STAGE1_ENABLE_MASK;
+	value &= ~WD_STAGE2_ENABLE_MASK;
+	outb(value, WD_CTL_REG);
+	return 0;
+}
+
+static int ztwd_keepalive(struct watchdog_driver *d) 
+{
+	trace("%s", d->driver.name);
+	inb(WD_CTL_REG);
+	status |= WDIOF_KEEPALIVEPING;
+	return 0;
+}
+
+static int ztwd_get_status(struct watchdog_driver *d, int *s) 
+{
+	uint8_t value;
+
+	trace("%s, %p", d->driver.name, s);
+	*s = status;
+	value = inb(WD_CTL_REG);
+	if (value & WD_STAGE1_MONITOR_MASK || value & WD_STAGE2_MONITOR_MASK)
+		*s |= WDIOF_CARDRESET;
+	return 0;
+}
+
+static int ztwd_get_bootstatus(struct watchdog_driver *d, int *bs) 
+{
+	trace("%s, %p", d->driver.name, bs);
+	*bs = bootstatus;
+	return 0;
+}
+
+static int ztwd_get_timeout(struct watchdog_driver *d, int *timeout)
+{
+	uint8_t value;
+
+	trace("%s, %p", d->driver.name, timeout);
+	if (!timeout) {
+		dbg("recieved a null timeout pointer!");
+		return -1;
+	}
+
+	value = inb(WD_CTL_REG);
+	switch (value & WD_TERMINALCOUNT_MASK) {
+	default:
+		dbg("unexpected value in terminal count");
+		return -EFAULT;
+	case WD_TC_250MS:
+	case WD_TC_500MS:
+	case WD_TC_1S:
+		*timeout = 1;
+		break;
+	case WD_TC_8S:
+		*timeout = 8;
+		break;
+	case WD_TC_32S:
+		*timeout = 32;
+		break;
+	case WD_TC_64S:
+		*timeout = 64;
+		break;
+	case WD_TC_128S:
+		*timeout = 128;
+		break;
+	case WD_TC_256S:
+		*timeout = 256;		
+	}
+	status |= WDIOF_SETTIMEOUT;
+	return 0;
+}
+
+static int ztwd_set_timeout(struct watchdog_driver *d, int timeout)
+{
+    	uint8_t value;
+
+	trace("%p, %i", d->driver.name, timeout);
+	if (timeout < 0) {
+	    	dbg("invalid time specifed"); 
+	    	return 1;
+	}
+
+	if (timeout == 0) {
+		ztwd_stop(d);
+		return 0;
+	}
+	if (timeout == 1)
+		timeout = WD_TC_1S;
+	else if (timeout <= 8)
+		timeout = WD_TC_8S;
+	else if (timeout <= 32)
+	    	timeout = WD_TC_32S;
+	else if (timeout <= 64)
+	    	timeout = WD_TC_64S;
+	else if (timeout <= 128)
+	    	timeout = WD_TC_128S;
+	else if (timeout <= 256)
+	    	timeout = WD_TC_256S;
+	else
+	    	timeout = WD_TC_256S;
+	
+	value = inb(WD_CTL_REG);
+	value &= ~WD_TC_256S;
+	value |= timeout;
+	dbg("timeout value set to %X", value);
+	outb (value, WD_CTL_REG);
+	return 0;
+}
+
+static int ztwd_get_options(struct watchdog_driver *d, int *c)
+{
+	trace("%s, %p", d->driver.name, c);
+	*c = WDIOS_DISABLECARD|WDIOS_ENABLECARD;
+	return 0;
+}
+
+static int ztwd_get_nowayout(struct watchdog_driver *d, int *n)
+{
+	trace("%s, %p", d->driver.name, n);
+	*n = nowayout;
+	return 0;
+}
+
+static int ztwd_set_nowayout(struct watchdog_driver *d, int n)
+{
+	trace("%s, %i", d->driver.name, n);
+	nowayout = n;
+	return 0;
+}
+
+static struct watchdog_ops ztwd_ops = {
+	.start                 = ztwd_start,
+	.stop                  = ztwd_stop,
+	.keepalive             = ztwd_keepalive,
+	.get_timeout           = ztwd_get_timeout,
+	.set_timeout           = ztwd_set_timeout,
+	.get_nowayout          = ztwd_get_nowayout,
+	.set_nowayout          = ztwd_set_nowayout,
+	.get_options           = ztwd_get_options,
+	.get_bootstatus        = ztwd_get_bootstatus,
+	.get_status            = ztwd_get_status,
+	/* get/set_temppanic not implemented */
+	/* get_firmware_version not implemented */
+};
+
+static struct watchdog_driver ztwd_driver = {
+	.ops = &ztwd_ops,
+	.driver = {
+		.name		= "ztwd",
+		.bus		= &system_bus_type,
+		.devclass       = &watchdog_devclass,
+	}
+};
+
+/* 
+ * ZT55XX specific controls 
+ */
+
+/* enabling the pretimeout (stage 1 timeout) will cause either a */
+/* NMI or INIT to happen when the stage 1 timer expires */
+static ssize_t pretimeout_enable_show(struct device_driver * d, char * buf)
+{
+	trace("%s, %p", d->name, buf);
+	return sprintf(buf, "%i\n",pretimeout);
+}
+static ssize_t pretimeout_enable_store(struct device_driver *d,
+				       const char * buf, 
+				       size_t count)
+{
+	trace("%s, %p, %i", d->name, buf, count);
+	if (sscanf(buf,"%i",&pretimeout) != 1)
+		return -EINVAL;
+
+	return count;
+}
+DRIVER_ATTR(pretimeout_enable,0644,pretimeout_enable_show,
+	    pretimeout_enable_store);
+
+/* 1 = NMI; 0 = INIT */
+static ssize_t pretimeout_action_show(struct device_driver * d, char * buf)
+{
+	uint8_t value;
+	
+	trace("%s, %p", d->name, buf);
+	value = inb(WD_CTL_REG);
+	return sprintf(buf, "%i\n",value&WD_STAGE1_ACTION_MASK?1:0);
+}
+static ssize_t pretimeout_action_store(struct device_driver *d,
+				       const char * buf, 
+				       size_t count)
+{
+	int tmp;
+	uint8_t value;
+
+	trace("%s, %p, %i", d->name, buf, count);
+	if (sscanf(buf,"%i",&tmp) != 1)
+		return -EINVAL;
+
+	value = inb(WD_CTL_REG);
+	if (tmp)
+		value |= WD_STAGE1_ACTION_MASK;
+	else
+		value &= ~WD_STAGE1_ACTION_MASK;
+	outb(value, WD_CTL_REG);
+	return count;
+}
+DRIVER_ATTR(pretimeout_action,0644,pretimeout_action_show,
+	    pretimeout_action_store);
+
+static int __init ztwd_init(void)
+{
+	int ret = 0;
+    	uint8_t value;
+
+	trace();
+	if (!request_region(WD_CTL_REG, 1, "ZT55XX Watchdog")) {
+		err("Unable to reserve io region 0x%2x", WD_CTL_REG);
+		return -EBUSY;
+	}
+
+	/* determine if the watchdog was previously tripped */
+	value = inb(WD_CTL_REG);	
+	if (value & WD_STAGE2_MONITOR_MASK) {
+		dbg("previous stage 2 watchdog timeout detected");
+		bootstatus = WDIOF_CARDRESET;
+
+		/* clear the stage 1 & 2 monitor flags */
+		value &= 0x3F;
+		outb(value, WD_CTL_REG);
+	}	
+
+	/* set the timeout before we give anyone */
+	/* a chance to start the watchdog device ticking */
+	if (ztwd_set_timeout(&ztwd_driver, DEFAULT_TIMEOUT))
+		return -EFAULT;
+
+	ret = watchdog_driver_register(&ztwd_driver);
+	if (ret) {
+		err("failed to register watchdog device");
+		return -ENODEV;
+	}
+
+	/* create zt55xx specific sysfs control files */
+	driver_create_file(&ztwd_driver.driver, 
+			   &driver_attr_pretimeout_enable);
+	driver_create_file(&ztwd_driver.driver, 
+			   &driver_attr_pretimeout_action);
+	return 0;
+}
+
+static void __exit ztwd_exit(void)
+{
+	trace();
+
+	/* remove zt55xx specific sysfs control files */
+	driver_remove_file(&ztwd_driver.driver, 
+			   &driver_attr_pretimeout_enable);
+	driver_remove_file(&ztwd_driver.driver, 
+			   &driver_attr_pretimeout_action);
+
+	release_region(WD_CTL_REG, 1);
+	watchdog_driver_unregister(&ztwd_driver);
+}
+
+module_init(ztwd_init);
+module_exit(ztwd_exit);
+MODULE_LICENSE("GPL");


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2003-02-21 23:39 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-02-21 23:21 [PATCH][2.5] Sysfs enabling watchdog infrastructure Rusty Lynch
2003-02-21 23:31 ` Rusty Lynch
2003-02-21 23:36 ` Rusty Lynch

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox