From 9487a9cc7140ef88d532c989570ee77436ede1e0 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Wed, 22 Jun 2011 15:24:32 +0530 Subject: watchdog: s3c2410: Add support for device tree based probe This patch adds the of_match_table to enable s3c2410-wdt driver to be probed when watchdog device node is found in the device tree. Signed-off-by: Thomas Abraham Acked-by: Grant Likely Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/samsung-wdt.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/samsung-wdt.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt new file mode 100644 index 000000000000..79ead8263ae4 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/samsung-wdt.txt @@ -0,0 +1,11 @@ +* Samsung's Watchdog Timer Controller + +The Samsung's Watchdog controller is used for resuming system operation +after a preset amount of time during which the WDT reset event has not +occured. + +Required properties: +- compatible : should be "samsung,s3c2410-wdt" +- reg : base physical address of the controller and length of memory mapped + region. +- interrupts : interrupt number to the cpu. -- cgit v1.2.3 From f5a427eedeead2be82561497a12788bd1f0c07d3 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 18 Jul 2011 11:15:21 +0800 Subject: watchdog: imx2_wdt: add device tree probe support Adds device tree probe support for imx2_wdt driver. Signed-off-by: Shawn Guo Cc: Grant Likely Cc: Wolfram Sang Signed-off-by: Wim Van Sebroeck --- Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt | 14 ++++++++++++++ drivers/watchdog/imx2_wdt.c | 6 ++++++ 2 files changed, 20 insertions(+) create mode 100644 Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt (limited to 'Documentation') diff --git a/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt new file mode 100644 index 000000000000..2144af1a5264 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/fsl-imx-wdt.txt @@ -0,0 +1,14 @@ +* Freescale i.MX Watchdog Timer (WDT) Controller + +Required properties: +- compatible : Should be "fsl,-wdt" +- reg : Should contain WDT registers location and length +- interrupts : Should contain WDT interrupt + +Examples: + +wdt@73f98000 { + compatible = "fsl,imx51-wdt", "fsl,imx21-wdt"; + reg = <0x73f98000 0x4000>; + interrupts = <58>; +}; diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c index 86f7cac1026c..b8ef2c6dca7c 100644 --- a/drivers/watchdog/imx2_wdt.c +++ b/drivers/watchdog/imx2_wdt.c @@ -329,12 +329,18 @@ static void imx2_wdt_shutdown(struct platform_device *pdev) } } +static const struct of_device_id imx2_wdt_dt_ids[] = { + { .compatible = "fsl,imx21-wdt", }, + { /* sentinel */ } +}; + static struct platform_driver imx2_wdt_driver = { .remove = __exit_p(imx2_wdt_remove), .shutdown = imx2_wdt_shutdown, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .of_match_table = imx2_wdt_dt_ids, }, }; -- cgit v1.2.3 From 43316044d4f64da008d6aca7d4b60771b9a24eb8 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:55:18 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add basic framework The WatchDog Timer Driver Core is a framework that contains the common code for all watchdog-driver's. It also introduces a watchdog device structure and the operations that go with it. This is the introduction of this framework. This part supports the minimal watchdog userspace API (or with other words: the functionality to use /dev/watchdog's open, release and write functionality as defined in the simplest watchdog API). Extra functionality will follow in the next set of patches. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/00-INDEX | 2 + Documentation/watchdog/watchdog-kernel-api.txt | 119 +++++++++++++ drivers/watchdog/Kconfig | 11 ++ drivers/watchdog/Makefile | 4 + drivers/watchdog/watchdog_core.c | 101 +++++++++++ drivers/watchdog/watchdog_dev.c | 235 +++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.h | 33 ++++ include/linux/watchdog.h | 61 +++++++ 8 files changed, 566 insertions(+) create mode 100644 Documentation/watchdog/watchdog-kernel-api.txt create mode 100644 drivers/watchdog/watchdog_core.c create mode 100644 drivers/watchdog/watchdog_dev.c create mode 100644 drivers/watchdog/watchdog_dev.h (limited to 'Documentation') diff --git a/Documentation/watchdog/00-INDEX b/Documentation/watchdog/00-INDEX index ee994513a9b1..fc51128071c2 100644 --- a/Documentation/watchdog/00-INDEX +++ b/Documentation/watchdog/00-INDEX @@ -8,6 +8,8 @@ src/ - directory holding watchdog related example programs. watchdog-api.txt - description of the Linux Watchdog driver API. +watchdog-kernel-api.txt + - description of the Linux WatchDog Timer Driver Core kernel API. watchdog-parameters.txt - information on driver parameters (for drivers other than the ones that have driver-specific files here) diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt new file mode 100644 index 000000000000..3db67e74b80e --- /dev/null +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -0,0 +1,119 @@ +The Linux WatchDog Timer Driver Core kernel API. +=============================================== +Last reviewed: 22-Jul-2011 + +Wim Van Sebroeck + +Introduction +------------ +This document does not describe what a WatchDog Timer (WDT) Driver or Device is. +It also does not describe the API which can be used by user space to communicate +with a WatchDog Timer. If you want to know this then please read the following +file: Documentation/watchdog/watchdog-api.txt . + +So what does this document describe? It describes the API that can be used by +WatchDog Timer Drivers that want to use the WatchDog Timer Driver Core +Framework. This framework provides all interfacing towards user space so that +the same code does not have to be reproduced each time. This also means that +a watchdog timer driver then only needs to provide the different routines +(operations) that control the watchdog timer (WDT). + +The API +------- +Each watchdog timer driver that wants to use the WatchDog Timer Driver Core +must #include (you would have to do this anyway when +writing a watchdog device driver). This include file contains following +register/unregister routines: + +extern int watchdog_register_device(struct watchdog_device *); +extern void watchdog_unregister_device(struct watchdog_device *); + +The watchdog_register_device routine registers a watchdog timer device. +The parameter of this routine is a pointer to a watchdog_device structure. +This routine returns zero on success and a negative errno code for failure. + +The watchdog_unregister_device routine deregisters a registered watchdog timer +device. The parameter of this routine is the pointer to the registered +watchdog_device structure. + +The watchdog device structure looks like this: + +struct watchdog_device { + const struct watchdog_info *info; + const struct watchdog_ops *ops; + void *driver_data; + unsigned long status; +}; + +It contains following fields: +* info: a pointer to a watchdog_info structure. This structure gives some + additional information about the watchdog timer itself. (Like it's unique name) +* ops: a pointer to the list of watchdog operations that the watchdog supports. +* driver_data: a pointer to the drivers private data of a watchdog device. + This data should only be accessed via the watchdog_set_drvadata and + watchdog_get_drvdata routines. +* status: this field contains a number of status bits that give extra + information about the status of the device (Like: is the device opened via + the /dev/watchdog interface or not, ...). + +The list of watchdog operations is defined as: + +struct watchdog_ops { + struct module *owner; + /* mandatory operations */ + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); + /* optional operations */ + int (*ping)(struct watchdog_device *); +}; + +It is important that you first define the module owner of the watchdog timer +driver's operations. This module owner will be used to lock the module when +the watchdog is active. (This to avoid a system crash when you unload the +module and /dev/watchdog is still open). +Some operations are mandatory and some are optional. The mandatory operations +are: +* start: this is a pointer to the routine that starts the watchdog timer + device. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. +* stop: with this routine the watchdog timer device is being stopped. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Some watchdog timer hardware can only be started and not be stopped. The + driver supporting this hardware needs to make sure that a start and stop + routine is being provided. This can be done by using a timer in the driver + that regularly sends a keepalive ping to the watchdog timer hardware. + +Not all watchdog timer hardware supports the same functionality. That's why +all other routines/operations are optional. They only need to be provided if +they are supported. These optional routines/operations are: +* ping: this is the routine that sends a keepalive ping to the watchdog timer + hardware. + The routine needs a pointer to the watchdog timer device structure as a + parameter. It returns zero on success or a negative errno code for failure. + Most hardware that does not support this as a separate function uses the + start function to restart the watchdog timer hardware. And that's also what + the watchdog timer driver core does: to send a keepalive ping to the watchdog + timer hardware it will either use the ping operation (when available) or the + start operation (when the ping operation is not available). + +The status bits should (preferably) be set with the set_bit and clear_bit alike +bit-operations. The status bits that are defined are: +* WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device + was opened via /dev/watchdog. + (This bit should only be used by the WatchDog Timer Driver Core). + +To get or set driver specific data the following two helper functions should be +used: + +static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) +static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) + +The watchdog_set_drvdata function allows you to add driver specific data. The +arguments of this function are the watchdog device where you want to add the +driver specific data to and a pointer to the data itself. + +The watchdog_get_drvdata function allows you to retrieve driver specific data. +The argument of this function is the watchdog device where you want to retrieve +data from. The function retruns the pointer to the driver specific data. diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 0635e72e0794..f441726ddf2b 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -28,6 +28,17 @@ menuconfig WATCHDOG if WATCHDOG +config WATCHDOG_CORE + bool "WatchDog Timer Driver Core" + ---help--- + Say Y here if you want to use the new watchdog timer driver core. + This driver provides a framework for all watchdog timer drivers + and gives them the /dev/watchdog interface (and later also the + sysfs interface). + + To compile this driver as a module, choose M here: the module will + be called watchdog. + config WATCHDOG_NOWAYOUT bool "Disable watchdog shutdown on close" help diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 9eaa212398d7..55bd5740e910 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -2,6 +2,10 @@ # Makefile for the WatchDog device drivers. # +# The WatchDog Timer Driver Core. +watchdog-objs += watchdog_core.o watchdog_dev.o +obj-$(CONFIG_WATCHDOG_CORE) += watchdog.o + # Only one watchdog can succeed. We probe the ISA/PCI/USB based # watchdog-cards first, then the architecture specific watchdog # drivers and then the architecture independent "softdog" driver. diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c new file mode 100644 index 000000000000..47fc1267ad4e --- /dev/null +++ b/drivers/watchdog/watchdog_core.c @@ -0,0 +1,101 @@ +/* + * watchdog_core.c + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * 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, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include /* For EXPORT_SYMBOL/module stuff/... */ +#include /* For standard types */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For watchdog specific items */ +#include /* For __init/__exit/... */ + +#include "watchdog_dev.h" /* For watchdog_dev_register/... */ + +/** + * watchdog_register_device() - register a watchdog device + * @wdd: watchdog device + * + * Register a watchdog device with the kernel so that the + * watchdog timer can be accessed from userspace. + * + * A zero is returned on success and a negative errno code for + * failure. + */ +int watchdog_register_device(struct watchdog_device *wdd) +{ + int ret; + + if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL) + return -EINVAL; + + /* Mandatory operations need to be supported */ + if (wdd->ops->start == NULL || wdd->ops->stop == NULL) + return -EINVAL; + + /* + * Note: now that all watchdog_device data has been verified, we + * will not check this anymore in other functions. If data gets + * corrupted in a later stage then we expect a kernel panic! + */ + + /* We only support 1 watchdog device via the /dev/watchdog interface */ + ret = watchdog_dev_register(wdd); + if (ret) { + pr_err("error registering /dev/watchdog (err=%d).\n", ret); + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(watchdog_register_device); + +/** + * watchdog_unregister_device() - unregister a watchdog device + * @wdd: watchdog device to unregister + * + * Unregister a watchdog device that was previously successfully + * registered with watchdog_register_device(). + */ +void watchdog_unregister_device(struct watchdog_device *wdd) +{ + int ret; + + if (wdd == NULL) + return; + + ret = watchdog_dev_unregister(wdd); + if (ret) + pr_err("error unregistering /dev/watchdog (err=%d).\n", ret); +} +EXPORT_SYMBOL_GPL(watchdog_unregister_device); + +MODULE_AUTHOR("Alan Cox "); +MODULE_AUTHOR("Wim Van Sebroeck "); +MODULE_DESCRIPTION("WatchDog Timer Driver Core"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c new file mode 100644 index 000000000000..366f49ce69b8 --- /dev/null +++ b/drivers/watchdog/watchdog_dev.c @@ -0,0 +1,235 @@ +/* + * watchdog_dev.c + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * This part of the generic code takes care of the following + * misc device: /dev/watchdog. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * 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, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include /* For module stuff/... */ +#include /* For standard types (like size_t) */ +#include /* For the -ENODEV/... values */ +#include /* For printk/panic/... */ +#include /* For file operations */ +#include /* For watchdog specific items */ +#include /* For handling misc devices */ +#include /* For __init/__exit/... */ +#include /* For copy_to_user/put_user/... */ + +/* make sure we only register one /dev/watchdog device */ +static unsigned long watchdog_dev_busy; +/* the watchdog device behind /dev/watchdog */ +static struct watchdog_device *wdd; + +/* + * watchdog_ping: ping the watchdog. + * @wddev: the watchdog device to ping + * + * If the watchdog has no own ping operation then it needs to be + * restarted via the start operation. This wrapper function does + * exactly that. + */ + +static int watchdog_ping(struct watchdog_device *wddev) +{ + if (wddev->ops->ping) + return wddev->ops->ping(wddev); /* ping the watchdog */ + else + return wddev->ops->start(wddev); /* restart the watchdog */ +} + +/* + * watchdog_write: writes to the watchdog. + * @file: file from VFS + * @data: user address of data + * @len: length of data + * @ppos: pointer to the file offset + * + * A write to a watchdog device is defined as a keepalive ping. + */ + +static ssize_t watchdog_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + size_t i; + char c; + + if (len == 0) + return 0; + + for (i = 0; i != len; i++) { + if (get_user(c, data + i)) + return -EFAULT; + } + + /* someone wrote to us, so we send the watchdog a keepalive ping */ + watchdog_ping(wdd); + + return len; +} + +/* + * watchdog_open: open the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * When the /dev/watchdog device gets opened, we start the watchdog. + * Watch out: the /dev/watchdog device is single open, so we make sure + * it can only be opened once. + */ + +static int watchdog_open(struct inode *inode, struct file *file) +{ + int err = -EBUSY; + + /* the watchdog is single open! */ + if (test_and_set_bit(WDOG_DEV_OPEN, &wdd->status)) + return -EBUSY; + + /* + * If the /dev/watchdog device is open, we don't want the module + * to be unloaded. + */ + if (!try_module_get(wdd->ops->owner)) + goto out; + + err = wdd->ops->start(wdd); + if (err < 0) + goto out_mod; + + /* dev/watchdog is a virtual (and thus non-seekable) filesystem */ + return nonseekable_open(inode, file); + +out_mod: + module_put(wdd->ops->owner); +out: + clear_bit(WDOG_DEV_OPEN, &wdd->status); + return err; +} + +/* + * watchdog_release: release the /dev/watchdog device. + * @inode: inode of device + * @file: file handle to device + * + * This is the code for when /dev/watchdog gets closed. + */ + +static int watchdog_release(struct inode *inode, struct file *file) +{ + int err; + + err = wdd->ops->stop(wdd); + if (err != 0) { + pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); + watchdog_ping(wdd); + } + + /* Allow the owner module to be unloaded again */ + module_put(wdd->ops->owner); + + /* make sure that /dev/watchdog can be re-opened */ + clear_bit(WDOG_DEV_OPEN, &wdd->status); + + return 0; +} + +static const struct file_operations watchdog_fops = { + .owner = THIS_MODULE, + .write = watchdog_write, + .open = watchdog_open, + .release = watchdog_release, +}; + +static struct miscdevice watchdog_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &watchdog_fops, +}; + +/* + * watchdog_dev_register: + * @watchdog: watchdog device + * + * Register a watchdog device as /dev/watchdog. /dev/watchdog + * is actually a miscdevice and thus we set it up like that. + */ + +int watchdog_dev_register(struct watchdog_device *watchdog) +{ + int err; + + /* Only one device can register for /dev/watchdog */ + if (test_and_set_bit(0, &watchdog_dev_busy)) { + pr_err("only one watchdog can use /dev/watchdog.\n"); + return -EBUSY; + } + + wdd = watchdog; + + err = misc_register(&watchdog_miscdev); + if (err != 0) { + pr_err("%s: cannot register miscdev on minor=%d (err=%d).\n", + watchdog->info->identity, WATCHDOG_MINOR, err); + goto out; + } + + return 0; + +out: + wdd = NULL; + clear_bit(0, &watchdog_dev_busy); + return err; +} + +/* + * watchdog_dev_unregister: + * @watchdog: watchdog device + * + * Deregister the /dev/watchdog device. + */ + +int watchdog_dev_unregister(struct watchdog_device *watchdog) +{ + /* Check that a watchdog device was registered in the past */ + if (!test_bit(0, &watchdog_dev_busy) || !wdd) + return -ENODEV; + + /* We can only unregister the watchdog device that was registered */ + if (watchdog != wdd) { + pr_err("%s: watchdog was not registered as /dev/watchdog.\n", + watchdog->info->identity); + return -ENODEV; + } + + misc_deregister(&watchdog_miscdev); + wdd = NULL; + clear_bit(0, &watchdog_dev_busy); + return 0; +} diff --git a/drivers/watchdog/watchdog_dev.h b/drivers/watchdog/watchdog_dev.h new file mode 100644 index 000000000000..bc7612be25ce --- /dev/null +++ b/drivers/watchdog/watchdog_dev.h @@ -0,0 +1,33 @@ +/* + * watchdog_core.h + * + * (c) Copyright 2008-2011 Alan Cox , + * All Rights Reserved. + * + * (c) Copyright 2008-2011 Wim Van Sebroeck . + * + * This source code is part of the generic code that can be used + * by all the watchdog timer drivers. + * + * Based on source code of the following authors: + * Matt Domsch , + * Rob Radez , + * Rusty Lynch + * Satyam Sharma + * Randy Dunlap + * + * 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, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw. + * admit liability nor provide warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + */ + +/* + * Functions/procedures to be called by the core + */ +int watchdog_dev_register(struct watchdog_device *); +int watchdog_dev_unregister(struct watchdog_device *); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 011bcfeb9f09..5ab31bfd2906 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -59,6 +59,67 @@ struct watchdog_info { #define WATCHDOG_NOWAYOUT 0 #endif +struct watchdog_ops; +struct watchdog_device; + +/** struct watchdog_ops - The watchdog-devices operations + * + * @owner: The module owner. + * @start: The routine for starting the watchdog device. + * @stop: The routine for stopping the watchdog device. + * @ping: The routine that sends a keepalive ping to the watchdog device. + * + * The watchdog_ops structure contains a list of low-level operations + * that control a watchdog device. It also contains the module that owns + * these operations. The start and stop function are mandatory, all other + * functions are optonal. + */ +struct watchdog_ops { + struct module *owner; + /* mandatory operations */ + int (*start)(struct watchdog_device *); + int (*stop)(struct watchdog_device *); + /* optional operations */ + int (*ping)(struct watchdog_device *); +}; + +/** struct watchdog_device - The structure that defines a watchdog device + * + * @info: Pointer to a watchdog_info structure. + * @ops: Pointer to the list of watchdog operations. + * @driver-data:Pointer to the drivers private data. + * @status: Field that contains the devices internal status bits. + * + * The watchdog_device structure contains all information about a + * watchdog timer device. + * + * The driver-data field may not be accessed directly. It must be accessed + * via the watchdog_set_drvdata and watchdog_get_drvdata helpers. + */ +struct watchdog_device { + const struct watchdog_info *info; + const struct watchdog_ops *ops; + void *driver_data; + unsigned long status; +/* Bit numbers for status flags */ +#define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ +}; + +/* Use the following functions to manipulate watchdog driver specific data */ +static inline void watchdog_set_drvdata(struct watchdog_device *wdd, void *data) +{ + wdd->driver_data = data; +} + +static inline void *watchdog_get_drvdata(struct watchdog_device *wdd) +{ + return wdd->driver_data; +} + +/* drivers/watchdog/core/watchdog_core.c */ +extern int watchdog_register_device(struct watchdog_device *); +extern void watchdog_unregister_device(struct watchdog_device *); + #endif /* __KERNEL__ */ #endif /* ifndef _LINUX_WATCHDOG_H */ -- cgit v1.2.3 From 2fa03560ab3a6dd83cad9bfd5692179fc2ceabb3 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:56:38 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add basic ioctl functionality This part add's the basic ioctl functionality to the WatchDog Timer Driver Core framework. The supported ioctl call's are: WDIOC_GETSUPPORT WDIOC_GETSTATUS WDIOC_GETBOOTSTATUS Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 6 +++++ drivers/watchdog/watchdog_dev.c | 32 ++++++++++++++++++++++++++ include/linux/watchdog.h | 4 ++++ 3 files changed, 42 insertions(+) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 3db67e74b80e..2bdc6dc6e04c 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -41,6 +41,7 @@ The watchdog device structure looks like this: struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; + unsigned int bootstatus; void *driver_data; unsigned long status; }; @@ -49,6 +50,8 @@ It contains following fields: * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. +* bootstatus: status of the device after booting (reported with watchdog + WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. This data should only be accessed via the watchdog_set_drvadata and watchdog_get_drvdata routines. @@ -65,6 +68,7 @@ struct watchdog_ops { int (*stop)(struct watchdog_device *); /* optional operations */ int (*ping)(struct watchdog_device *); + unsigned int (*status)(struct watchdog_device *); }; It is important that you first define the module owner of the watchdog timer @@ -97,6 +101,8 @@ they are supported. These optional routines/operations are: the watchdog timer driver core does: to send a keepalive ping to the watchdog timer hardware it will either use the ping operation (when available) or the start operation (when the ping operation is not available). +* status: this routine checks the status of the watchdog timer device. The + status of the device is reported with watchdog WDIOF_* status flags/bits. The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 366f49ce69b8..00a611293065 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -94,6 +94,37 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, return len; } +/* + * watchdog_ioctl: handle the different ioctl's for the watchdog device. + * @file: file handle to the device + * @cmd: watchdog command + * @arg: argument pointer + * + * The watchdog API defines a common set of functions for all watchdogs + * according to their available features. + */ + +static long watchdog_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + unsigned int val; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, wdd->info, + sizeof(struct watchdog_info)) ? -EFAULT : 0; + case WDIOC_GETSTATUS: + val = wdd->ops->status ? wdd->ops->status(wdd) : 0; + return put_user(val, p); + case WDIOC_GETBOOTSTATUS: + return put_user(wdd->bootstatus, p); + default: + return -ENOTTY; + } +} + /* * watchdog_open: open the /dev/watchdog device. * @inode: inode of device @@ -163,6 +194,7 @@ static int watchdog_release(struct inode *inode, struct file *file) static const struct file_operations watchdog_fops = { .owner = THIS_MODULE, .write = watchdog_write, + .unlocked_ioctl = watchdog_ioctl, .open = watchdog_open, .release = watchdog_release, }; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 5ab31bfd2906..29ff80807dd4 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -68,6 +68,7 @@ struct watchdog_device; * @start: The routine for starting the watchdog device. * @stop: The routine for stopping the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device. + * @status: The routine that shows the status of the watchdog device. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -81,12 +82,14 @@ struct watchdog_ops { int (*stop)(struct watchdog_device *); /* optional operations */ int (*ping)(struct watchdog_device *); + unsigned int (*status)(struct watchdog_device *); }; /** struct watchdog_device - The structure that defines a watchdog device * * @info: Pointer to a watchdog_info structure. * @ops: Pointer to the list of watchdog operations. + * @bootstatus: Status of the watchdog device at boot. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -99,6 +102,7 @@ struct watchdog_ops { struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; + unsigned int bootstatus; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3 From c2dc00e494cc476551b9beeb883910391ff59737 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:57:23 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add WDIOC_KEEPALIVE ioctl This part add's the WDIOC_KEEPALIVE ioctl functionality to the WatchDog Timer Driver Core framework. Please note that the WDIOF_KEEPALIVEPING bit has to be set in the watchdog_info options field. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 3 +++ drivers/watchdog/watchdog_dev.c | 5 +++++ 2 files changed, 8 insertions(+) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 2bdc6dc6e04c..abbcf2ce8f62 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -101,6 +101,9 @@ they are supported. These optional routines/operations are: the watchdog timer driver core does: to send a keepalive ping to the watchdog timer hardware it will either use the ping operation (when available) or the start operation (when the ping operation is not available). + (Note: the WDIOC_KEEPALIVE ioctl call will only be active when the + WDIOF_KEEPALIVEPING bit has been set in the option field on the watchdog's + info structure). * status: this routine checks the status of the watchdog timer device. The status of the device is reported with watchdog WDIOF_* status flags/bits. diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 00a611293065..2fb4cecd50d8 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -120,6 +120,11 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return put_user(val, p); case WDIOC_GETBOOTSTATUS: return put_user(wdd->bootstatus, p); + case WDIOC_KEEPALIVE: + if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) + return -EOPNOTSUPP; + watchdog_ping(wdd); + return 0; default: return -ENOTTY; } -- cgit v1.2.3 From 234445b4e4542f3e0f216459245ab369a18adcf2 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:57:55 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add WDIOC_SETOPTIONS ioctl This part add's the WDIOC_SETOPTIONS ioctl functionality to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 9 ++- drivers/watchdog/watchdog_dev.c | 79 +++++++++++++++++++++++--- include/linux/watchdog.h | 1 + 3 files changed, 80 insertions(+), 9 deletions(-) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index abbcf2ce8f62..429f81bf0cf7 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -56,8 +56,9 @@ It contains following fields: This data should only be accessed via the watchdog_set_drvadata and watchdog_get_drvdata routines. * status: this field contains a number of status bits that give extra - information about the status of the device (Like: is the device opened via - the /dev/watchdog interface or not, ...). + information about the status of the device (Like: is the watchdog timer + running/active, is the device opened via the /dev/watchdog interface or not, + ...). The list of watchdog operations is defined as: @@ -109,6 +110,10 @@ they are supported. These optional routines/operations are: The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: +* WDOG_ACTIVE: this status bit indicates whether or not a watchdog timer device + is active or not. When the watchdog is active after booting, then you should + set this status bit (Note: when you register the watchdog timer device with + this bit set, then opening /dev/watchdog will skip the start operation) * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device was opened via /dev/watchdog. (This bit should only be used by the WatchDog Timer Driver Core). diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 2fb4cecd50d8..9f5550e16ab5 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -54,14 +54,64 @@ static struct watchdog_device *wdd; * If the watchdog has no own ping operation then it needs to be * restarted via the start operation. This wrapper function does * exactly that. + * We only ping when the watchdog device is running. */ static int watchdog_ping(struct watchdog_device *wddev) { - if (wddev->ops->ping) - return wddev->ops->ping(wddev); /* ping the watchdog */ - else - return wddev->ops->start(wddev); /* restart the watchdog */ + if (test_bit(WDOG_ACTIVE, &wdd->status)) { + if (wddev->ops->ping) + return wddev->ops->ping(wddev); /* ping the watchdog */ + else + return wddev->ops->start(wddev); /* restart watchdog */ + } + return 0; +} + +/* + * watchdog_start: wrapper to start the watchdog. + * @wddev: the watchdog device to start + * + * Start the watchdog if it is not active and mark it active. + * This function returns zero on success or a negative errno code for + * failure. + */ + +static int watchdog_start(struct watchdog_device *wddev) +{ + int err; + + if (!test_bit(WDOG_ACTIVE, &wdd->status)) { + err = wddev->ops->start(wddev); + if (err < 0) + return err; + + set_bit(WDOG_ACTIVE, &wdd->status); + } + return 0; +} + +/* + * watchdog_stop: wrapper to stop the watchdog. + * @wddev: the watchdog device to stop + * + * Stop the watchdog if it is still active and unmark it active. + * This function returns zero on success or a negative errno code for + * failure. + */ + +static int watchdog_stop(struct watchdog_device *wddev) +{ + int err; + + if (test_bit(WDOG_ACTIVE, &wdd->status)) { + err = wddev->ops->stop(wddev); + if (err < 0) + return err; + + clear_bit(WDOG_ACTIVE, &wdd->status); + } + return 0; } /* @@ -110,6 +160,7 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, void __user *argp = (void __user *)arg; int __user *p = argp; unsigned int val; + int err; switch (cmd) { case WDIOC_GETSUPPORT: @@ -120,6 +171,20 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return put_user(val, p); case WDIOC_GETBOOTSTATUS: return put_user(wdd->bootstatus, p); + case WDIOC_SETOPTIONS: + if (get_user(val, p)) + return -EFAULT; + if (val & WDIOS_DISABLECARD) { + err = watchdog_stop(wdd); + if (err < 0) + return err; + } + if (val & WDIOS_ENABLECARD) { + err = watchdog_start(wdd); + if (err < 0) + return err; + } + return 0; case WDIOC_KEEPALIVE: if (!(wdd->info->options & WDIOF_KEEPALIVEPING)) return -EOPNOTSUPP; @@ -155,7 +220,7 @@ static int watchdog_open(struct inode *inode, struct file *file) if (!try_module_get(wdd->ops->owner)) goto out; - err = wdd->ops->start(wdd); + err = watchdog_start(wdd); if (err < 0) goto out_mod; @@ -181,8 +246,8 @@ static int watchdog_release(struct inode *inode, struct file *file) { int err; - err = wdd->ops->stop(wdd); - if (err != 0) { + err = watchdog_stop(wdd); + if (err < 0) { pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); watchdog_ping(wdd); } diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 29ff80807dd4..db46fe89563e 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -106,6 +106,7 @@ struct watchdog_device { void *driver_data; unsigned long status; /* Bit numbers for status flags */ +#define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ }; -- cgit v1.2.3 From 014d694e5d59e4219803cd14deaae496d86e4910 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:58:21 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl This part add's the WDIOC_SETTIMEOUT and WDIOC_GETTIMEOUT ioctl functionality to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 10 ++++++++++ drivers/watchdog/watchdog_dev.c | 20 ++++++++++++++++++++ include/linux/watchdog.h | 4 ++++ 3 files changed, 34 insertions(+) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 429f81bf0cf7..acdee39fb08a 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -42,6 +42,7 @@ struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; unsigned int bootstatus; + unsigned int timeout; void *driver_data; unsigned long status; }; @@ -50,6 +51,7 @@ It contains following fields: * info: a pointer to a watchdog_info structure. This structure gives some additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. +* timeout: the watchdog timer's timeout value (in seconds). * bootstatus: status of the device after booting (reported with watchdog WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. @@ -70,6 +72,7 @@ struct watchdog_ops { /* optional operations */ int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); }; It is important that you first define the module owner of the watchdog timer @@ -107,6 +110,13 @@ they are supported. These optional routines/operations are: info structure). * status: this routine checks the status of the watchdog timer device. The status of the device is reported with watchdog WDIOF_* status flags/bits. +* set_timeout: this routine checks and changes the timeout of the watchdog + timer device. It returns 0 on success, -EINVAL for "parameter out of range" + and -EIO for "could not write value to the watchdog". On success the timeout + value of the watchdog_device will be changed to the value that was just used + to re-program the watchdog timer device. + (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the + watchdog's info structure). The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 9f5550e16ab5..2c0289deaadd 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -190,6 +190,26 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EOPNOTSUPP; watchdog_ping(wdd); return 0; + case WDIOC_SETTIMEOUT: + if ((wdd->ops->set_timeout == NULL) || + !(wdd->info->options & WDIOF_SETTIMEOUT)) + return -EOPNOTSUPP; + if (get_user(val, p)) + return -EFAULT; + err = wdd->ops->set_timeout(wdd, val); + if (err < 0) + return err; + wdd->timeout = val; + /* If the watchdog is active then we send a keepalive ping + * to make sure that the watchdog keep's running (and if + * possible that it takes the new timeout) */ + watchdog_ping(wdd); + /* Fall */ + case WDIOC_GETTIMEOUT: + /* timeout == 0 means that we don't know the timeout */ + if (wdd->timeout == 0) + return -EOPNOTSUPP; + return put_user(wdd->timeout, p); default: return -ENOTTY; } diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index db46fe89563e..9f33efe199d1 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -69,6 +69,7 @@ struct watchdog_device; * @stop: The routine for stopping the watchdog device. * @ping: The routine that sends a keepalive ping to the watchdog device. * @status: The routine that shows the status of the watchdog device. + * @set_timeout:The routine for setting the watchdog devices timeout value. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -83,6 +84,7 @@ struct watchdog_ops { /* optional operations */ int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); + int (*set_timeout)(struct watchdog_device *, unsigned int); }; /** struct watchdog_device - The structure that defines a watchdog device @@ -90,6 +92,7 @@ struct watchdog_ops { * @info: Pointer to a watchdog_info structure. * @ops: Pointer to the list of watchdog operations. * @bootstatus: Status of the watchdog device at boot. + * @timeout: The watchdog devices timeout value. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -103,6 +106,7 @@ struct watchdog_device { const struct watchdog_info *info; const struct watchdog_ops *ops; unsigned int bootstatus; + unsigned int timeout; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3 From 017cf0805105496ab1880e236cb3e4bf156fb915 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:58:54 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add Magic Close feature Add support for the Magic Close feature to the WatchDog Timer Driver Core framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 7 +++++++ drivers/watchdog/watchdog_dev.c | 27 +++++++++++++++++++++++--- include/linux/watchdog.h | 1 + 3 files changed, 32 insertions(+), 3 deletions(-) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index acdee39fb08a..41d552698ada 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -127,6 +127,13 @@ bit-operations. The status bits that are defined are: * WDOG_DEV_OPEN: this status bit shows whether or not the watchdog device was opened via /dev/watchdog. (This bit should only be used by the WatchDog Timer Driver Core). +* WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character + has been sent (so that we can support the magic close feature). + (This bit should only be used by the WatchDog Timer Driver Core). + +Note: The WatchDog Timer Driver Core supports the magic close feature. To use +the magic close feature you must set the WDIOF_MAGICCLOSE bit in the options +field of the watchdog's info structure. To get or set driver specific data the following two helper functions should be used: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index 2c0289deaadd..db40c6c79ef8 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -122,6 +122,8 @@ static int watchdog_stop(struct watchdog_device *wddev) * @ppos: pointer to the file offset * * A write to a watchdog device is defined as a keepalive ping. + * Writing the magic 'V' sequence allows the next close to turn + * off the watchdog. */ static ssize_t watchdog_write(struct file *file, const char __user *data, @@ -133,9 +135,18 @@ static ssize_t watchdog_write(struct file *file, const char __user *data, if (len == 0) return 0; + /* + * Note: just in case someone wrote the magic character + * five months ago... + */ + clear_bit(WDOG_ALLOW_RELEASE, &wdd->status); + + /* scan to see whether or not we got the magic character */ for (i = 0; i != len; i++) { if (get_user(c, data + i)) return -EFAULT; + if (c == 'V') + set_bit(WDOG_ALLOW_RELEASE, &wdd->status); } /* someone wrote to us, so we send the watchdog a keepalive ping */ @@ -259,14 +270,24 @@ out: * @inode: inode of device * @file: file handle to device * - * This is the code for when /dev/watchdog gets closed. + * This is the code for when /dev/watchdog gets closed. We will only + * stop the watchdog when we have received the magic char, else the + * watchdog will keep running. */ static int watchdog_release(struct inode *inode, struct file *file) { - int err; + int err = -EBUSY; + + /* + * We only stop the watchdog if we received the magic character + * or if WDIOF_MAGICCLOSE is not set + */ + if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || + !(wdd->info->options & WDIOF_MAGICCLOSE)) + err = watchdog_stop(wdd); - err = watchdog_stop(wdd); + /* If the watchdog was not stopped, send a keepalive ping */ if (err < 0) { pr_crit("%s: watchdog did not stop!\n", wdd->info->identity); watchdog_ping(wdd); diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 9f33efe199d1..e9881ca2452b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -112,6 +112,7 @@ struct watchdog_device { /* Bit numbers for status flags */ #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ +#define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ }; /* Use the following functions to manipulate watchdog driver specific data */ -- cgit v1.2.3 From 7e192b9c4234d29bdc20ac8d0a67edf7624b4206 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:59:17 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add nowayout feature Add support for the nowayout feature to the WatchDog Timer Driver Core framework. This feature prevents the watchdog timer from being stopped. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 13 ++++++++----- drivers/watchdog/watchdog_dev.c | 18 +++++++++++++----- include/linux/watchdog.h | 1 + 3 files changed, 22 insertions(+), 10 deletions(-) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 41d552698ada..785fa0c996a4 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -59,8 +59,8 @@ It contains following fields: watchdog_get_drvdata routines. * status: this field contains a number of status bits that give extra information about the status of the device (Like: is the watchdog timer - running/active, is the device opened via the /dev/watchdog interface or not, - ...). + running/active, is the nowayout bit set, is the device opened via + the /dev/watchdog interface or not, ...). The list of watchdog operations is defined as: @@ -130,10 +130,13 @@ bit-operations. The status bits that are defined are: * WDOG_ALLOW_RELEASE: this bit stores whether or not the magic close character has been sent (so that we can support the magic close feature). (This bit should only be used by the WatchDog Timer Driver Core). +* WDOG_NO_WAY_OUT: this bit stores the nowayout setting for the watchdog. + If this bit is set then the watchdog timer will not be able to stop. -Note: The WatchDog Timer Driver Core supports the magic close feature. To use -the magic close feature you must set the WDIOF_MAGICCLOSE bit in the options -field of the watchdog's info structure. +Note: The WatchDog Timer Driver Core supports the magic close feature and +the nowayout feature. To use the magic close feature you must set the +WDIOF_MAGICCLOSE bit in the options field of the watchdog's info structure. +The nowayout feature will overrule the magic close feature. To get or set driver specific data the following two helper functions should be used: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index db40c6c79ef8..ac20f92347b1 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -98,11 +98,18 @@ static int watchdog_start(struct watchdog_device *wddev) * Stop the watchdog if it is still active and unmark it active. * This function returns zero on success or a negative errno code for * failure. + * If the 'nowayout' feature was set, the watchdog cannot be stopped. */ static int watchdog_stop(struct watchdog_device *wddev) { - int err; + int err = -EBUSY; + + if (test_bit(WDOG_NO_WAY_OUT, &wdd->status)) { + pr_info("%s: nowayout prevents watchdog to be stopped!\n", + wdd->info->identity); + return err; + } if (test_bit(WDOG_ACTIVE, &wdd->status)) { err = wddev->ops->stop(wddev); @@ -123,7 +130,7 @@ static int watchdog_stop(struct watchdog_device *wddev) * * A write to a watchdog device is defined as a keepalive ping. * Writing the magic 'V' sequence allows the next close to turn - * off the watchdog. + * off the watchdog (if 'nowayout' is not set). */ static ssize_t watchdog_write(struct file *file, const char __user *data, @@ -271,8 +278,8 @@ out: * @file: file handle to device * * This is the code for when /dev/watchdog gets closed. We will only - * stop the watchdog when we have received the magic char, else the - * watchdog will keep running. + * stop the watchdog when we have received the magic char (and nowayout + * was not set), else the watchdog will keep running. */ static int watchdog_release(struct inode *inode, struct file *file) @@ -281,7 +288,8 @@ static int watchdog_release(struct inode *inode, struct file *file) /* * We only stop the watchdog if we received the magic character - * or if WDIOF_MAGICCLOSE is not set + * or if WDIOF_MAGICCLOSE is not set. If nowayout was set then + * watchdog_stop will fail. */ if (test_and_clear_bit(WDOG_ALLOW_RELEASE, &wdd->status) || !(wdd->info->options & WDIOF_MAGICCLOSE)) diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index e9881ca2452b..f719883c5141 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -113,6 +113,7 @@ struct watchdog_device { #define WDOG_ACTIVE 0 /* Is the watchdog running/active */ #define WDOG_DEV_OPEN 1 /* Opened via /dev/watchdog ? */ #define WDOG_ALLOW_RELEASE 2 /* Did we receive the magic char ? */ +#define WDOG_NO_WAY_OUT 3 /* Is 'nowayout' feature set ? */ }; /* Use the following functions to manipulate watchdog driver specific data */ -- cgit v1.2.3 From 78d88fc01202b088573c962e2885556a5e99bf74 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 18:59:49 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add ioctl call Add support for extra ioctl calls by adding a ioctl watchdog operation. This operation will be called before we do our own handling of ioctl commands. This way we can override the internal ioctl command handling and we can also add extra ioctl commands. The ioctl watchdog operation should return the appropriate error codes or -ENOIOCTLCMD if the ioctl command should be handled through the internal ioctl handling of the framework. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 5 +++++ drivers/watchdog/watchdog_dev.c | 6 ++++++ include/linux/watchdog.h | 2 ++ 3 files changed, 13 insertions(+) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 785fa0c996a4..829955bd245e 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -73,6 +73,7 @@ struct watchdog_ops { int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; It is important that you first define the module owner of the watchdog timer @@ -117,6 +118,10 @@ they are supported. These optional routines/operations are: to re-program the watchdog timer device. (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the watchdog's info structure). +* ioctl: if this routine is present then it will be called first before we do + our own internal ioctl call handling. This routine should return -ENOIOCTLCMD + if a command is not supported. The parameters that are passed to the ioctl + call are: watchdog_device, cmd and arg. The status bits should (preferably) be set with the set_bit and clear_bit alike bit-operations. The status bits that are defined are: diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index ac20f92347b1..e7134a5979c6 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -180,6 +180,12 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, unsigned int val; int err; + if (wdd->ops->ioctl) { + err = wdd->ops->ioctl(wdd, cmd, arg); + if (err != -ENOIOCTLCMD) + return err; + } + switch (cmd) { case WDIOC_GETSUPPORT: return copy_to_user(argp, wdd->info, diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index f719883c5141..325d90b6641b 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -70,6 +70,7 @@ struct watchdog_device; * @ping: The routine that sends a keepalive ping to the watchdog device. * @status: The routine that shows the status of the watchdog device. * @set_timeout:The routine for setting the watchdog devices timeout value. + * @ioctl: The routines that handles extra ioctl calls. * * The watchdog_ops structure contains a list of low-level operations * that control a watchdog device. It also contains the module that owns @@ -85,6 +86,7 @@ struct watchdog_ops { int (*ping)(struct watchdog_device *); unsigned int (*status)(struct watchdog_device *); int (*set_timeout)(struct watchdog_device *, unsigned int); + long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); }; /** struct watchdog_device - The structure that defines a watchdog device -- cgit v1.2.3 From 3f43f68e29f1dcb853d70280c7412fc0ef9a0da6 Mon Sep 17 00:00:00 2001 From: Wim Van Sebroeck Date: Fri, 22 Jul 2011 19:00:16 +0000 Subject: watchdog: WatchDog Timer Driver Core - Add minimum and max timeout Add min_timeout (minimum timeout) and max_timeout values so that the framework can check if the new timeout value is between the minimum and maximum timeout values. If both values are 0, then the framework will leave the check for the watchdog device driver itself. Signed-off-by: Alan Cox Signed-off-by: Wim Van Sebroeck Acked-by: Arnd Bergmann Acked-by: Wolfram Sang --- Documentation/watchdog/watchdog-kernel-api.txt | 4 ++++ drivers/watchdog/watchdog_core.c | 10 ++++++++++ drivers/watchdog/watchdog_dev.c | 3 +++ include/linux/watchdog.h | 4 ++++ 4 files changed, 21 insertions(+) (limited to 'Documentation') diff --git a/Documentation/watchdog/watchdog-kernel-api.txt b/Documentation/watchdog/watchdog-kernel-api.txt index 829955bd245e..4f7c894244d2 100644 --- a/Documentation/watchdog/watchdog-kernel-api.txt +++ b/Documentation/watchdog/watchdog-kernel-api.txt @@ -43,6 +43,8 @@ struct watchdog_device { const struct watchdog_ops *ops; unsigned int bootstatus; unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; void *driver_data; unsigned long status; }; @@ -52,6 +54,8 @@ It contains following fields: additional information about the watchdog timer itself. (Like it's unique name) * ops: a pointer to the list of watchdog operations that the watchdog supports. * timeout: the watchdog timer's timeout value (in seconds). +* min_timeout: the watchdog timer's minimum timeout value (in seconds). +* max_timeout: the watchdog timer's maximum timeout value (in seconds). * bootstatus: status of the device after booting (reported with watchdog WDIOF_* status bits). * driver_data: a pointer to the drivers private data of a watchdog device. diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 47fc1267ad4e..cfa1a1518aad 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -58,6 +58,16 @@ int watchdog_register_device(struct watchdog_device *wdd) if (wdd->ops->start == NULL || wdd->ops->stop == NULL) return -EINVAL; + /* + * Check that we have valid min and max timeout values, if + * not reset them both to 0 (=not used or unknown) + */ + if (wdd->min_timeout > wdd->max_timeout) { + pr_info("Invalid min and max timeout values, resetting to 0!\n"); + wdd->min_timeout = 0; + wdd->max_timeout = 0; + } + /* * Note: now that all watchdog_device data has been verified, we * will not check this anymore in other functions. If data gets diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index e7134a5979c6..d33520d0b4c9 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -220,6 +220,9 @@ static long watchdog_ioctl(struct file *file, unsigned int cmd, return -EOPNOTSUPP; if (get_user(val, p)) return -EFAULT; + if ((wdd->max_timeout != 0) && + (val < wdd->min_timeout || val > wdd->max_timeout)) + return -EINVAL; err = wdd->ops->set_timeout(wdd, val); if (err < 0) return err; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 325d90b6641b..111843f88b2a 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -95,6 +95,8 @@ struct watchdog_ops { * @ops: Pointer to the list of watchdog operations. * @bootstatus: Status of the watchdog device at boot. * @timeout: The watchdog devices timeout value. + * @min_timeout:The watchdog devices minimum timeout value. + * @max_timeout:The watchdog devices maximum timeout value. * @driver-data:Pointer to the drivers private data. * @status: Field that contains the devices internal status bits. * @@ -109,6 +111,8 @@ struct watchdog_device { const struct watchdog_ops *ops; unsigned int bootstatus; unsigned int timeout; + unsigned int min_timeout; + unsigned int max_timeout; void *driver_data; unsigned long status; /* Bit numbers for status flags */ -- cgit v1.2.3