linux/drivers/watchdog/watchdog_core.c
<<
>>
Prefs
   1/*
   2 *      watchdog_core.c
   3 *
   4 *      (c) Copyright 2008-2011 Alan Cox <alan@lxorguk.ukuu.org.uk>,
   5 *                                              All Rights Reserved.
   6 *
   7 *      (c) Copyright 2008-2011 Wim Van Sebroeck <wim@iguana.be>.
   8 *
   9 *      This source code is part of the generic code that can be used
  10 *      by all the watchdog timer drivers.
  11 *
  12 *      Based on source code of the following authors:
  13 *        Matt Domsch <Matt_Domsch@dell.com>,
  14 *        Rob Radez <rob@osinvestor.com>,
  15 *        Rusty Lynch <rusty@linux.co.intel.com>
  16 *        Satyam Sharma <satyam@infradead.org>
  17 *        Randy Dunlap <randy.dunlap@oracle.com>
  18 *
  19 *      This program is free software; you can redistribute it and/or
  20 *      modify it under the terms of the GNU General Public License
  21 *      as published by the Free Software Foundation; either version
  22 *      2 of the License, or (at your option) any later version.
  23 *
  24 *      Neither Alan Cox, CymruNet Ltd., Wim Van Sebroeck nor Iguana vzw.
  25 *      admit liability nor provide warranty for any of this software.
  26 *      This material is provided "AS-IS" and at no charge.
  27 */
  28
  29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  30
  31#include <linux/module.h>       /* For EXPORT_SYMBOL/module stuff/... */
  32#include <linux/types.h>        /* For standard types */
  33#include <linux/errno.h>        /* For the -ENODEV/... values */
  34#include <linux/kernel.h>       /* For printk/panic/... */
  35#include <linux/watchdog.h>     /* For watchdog specific items */
  36#include <linux/init.h>         /* For __init/__exit/... */
  37#include <linux/idr.h>          /* For ida_* macros */
  38#include <linux/err.h>          /* For IS_ERR macros */
  39#include <linux/of.h>           /* For of_get_timeout_sec */
  40
  41#include "watchdog_core.h"      /* For watchdog_dev_register/... */
  42
  43static DEFINE_IDA(watchdog_ida);
  44static struct class *watchdog_class;
  45
  46static void watchdog_check_min_max_timeout(struct watchdog_device *wdd)
  47{
  48        /*
  49         * Check that we have valid min and max timeout values, if
  50         * not reset them both to 0 (=not used or unknown)
  51         */
  52        if (wdd->min_timeout > wdd->max_timeout) {
  53                pr_info("Invalid min and max timeout values, resetting to 0!\n");
  54                wdd->min_timeout = 0;
  55                wdd->max_timeout = 0;
  56        }
  57}
  58
  59/**
  60 * watchdog_init_timeout() - initialize the timeout field
  61 * @timeout_parm: timeout module parameter
  62 * @dev: Device that stores the timeout-sec property
  63 *
  64 * Initialize the timeout field of the watchdog_device struct with either the
  65 * timeout module parameter (if it is valid value) or the timeout-sec property
  66 * (only if it is a valid value and the timeout_parm is out of bounds).
  67 * If none of them are valid then we keep the old value (which should normally
  68 * be the default timeout value.
  69 *
  70 * A zero is returned on success and -EINVAL for failure.
  71 */
  72int watchdog_init_timeout(struct watchdog_device *wdd,
  73                                unsigned int timeout_parm, struct device *dev)
  74{
  75        unsigned int t = 0;
  76        int ret = 0;
  77
  78        watchdog_check_min_max_timeout(wdd);
  79
  80        /* try to get the timeout module parameter first */
  81        if (!watchdog_timeout_invalid(wdd, timeout_parm) && timeout_parm) {
  82                wdd->timeout = timeout_parm;
  83                return ret;
  84        }
  85        if (timeout_parm)
  86                ret = -EINVAL;
  87
  88        /* try to get the timeout_sec property */
  89        if (dev == NULL || dev->of_node == NULL)
  90                return ret;
  91        of_property_read_u32(dev->of_node, "timeout-sec", &t);
  92        if (!watchdog_timeout_invalid(wdd, t) && t)
  93                wdd->timeout = t;
  94        else
  95                ret = -EINVAL;
  96
  97        return ret;
  98}
  99EXPORT_SYMBOL_GPL(watchdog_init_timeout);
 100
 101/**
 102 * watchdog_register_device() - register a watchdog device
 103 * @wdd: watchdog device
 104 *
 105 * Register a watchdog device with the kernel so that the
 106 * watchdog timer can be accessed from userspace.
 107 *
 108 * A zero is returned on success and a negative errno code for
 109 * failure.
 110 */
 111int watchdog_register_device(struct watchdog_device *wdd)
 112{
 113        int ret, id, devno;
 114
 115        if (wdd == NULL || wdd->info == NULL || wdd->ops == NULL)
 116                return -EINVAL;
 117
 118        /* Mandatory operations need to be supported */
 119        if (wdd->ops->start == NULL || wdd->ops->stop == NULL)
 120                return -EINVAL;
 121
 122        watchdog_check_min_max_timeout(wdd);
 123
 124        /*
 125         * Note: now that all watchdog_device data has been verified, we
 126         * will not check this anymore in other functions. If data gets
 127         * corrupted in a later stage then we expect a kernel panic!
 128         */
 129
 130        mutex_init(&wdd->lock);
 131        id = ida_simple_get(&watchdog_ida, 0, MAX_DOGS, GFP_KERNEL);
 132        if (id < 0)
 133                return id;
 134        wdd->id = id;
 135
 136        ret = watchdog_dev_register(wdd);
 137        if (ret) {
 138                ida_simple_remove(&watchdog_ida, id);
 139                if (!(id == 0 && ret == -EBUSY))
 140                        return ret;
 141
 142                /* Retry in case a legacy watchdog module exists */
 143                id = ida_simple_get(&watchdog_ida, 1, MAX_DOGS, GFP_KERNEL);
 144                if (id < 0)
 145                        return id;
 146                wdd->id = id;
 147
 148                ret = watchdog_dev_register(wdd);
 149                if (ret) {
 150                        ida_simple_remove(&watchdog_ida, id);
 151                        return ret;
 152                }
 153        }
 154
 155        devno = wdd->cdev.dev;
 156        wdd->dev = device_create(watchdog_class, wdd->parent, devno,
 157                                        NULL, "watchdog%d", wdd->id);
 158        if (IS_ERR(wdd->dev)) {
 159                watchdog_dev_unregister(wdd);
 160                ida_simple_remove(&watchdog_ida, id);
 161                ret = PTR_ERR(wdd->dev);
 162                return ret;
 163        }
 164
 165        return 0;
 166}
 167EXPORT_SYMBOL_GPL(watchdog_register_device);
 168
 169/**
 170 * watchdog_unregister_device() - unregister a watchdog device
 171 * @wdd: watchdog device to unregister
 172 *
 173 * Unregister a watchdog device that was previously successfully
 174 * registered with watchdog_register_device().
 175 */
 176void watchdog_unregister_device(struct watchdog_device *wdd)
 177{
 178        int ret;
 179        int devno;
 180
 181        if (wdd == NULL)
 182                return;
 183
 184        devno = wdd->cdev.dev;
 185        ret = watchdog_dev_unregister(wdd);
 186        if (ret)
 187                pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
 188        device_destroy(watchdog_class, devno);
 189        ida_simple_remove(&watchdog_ida, wdd->id);
 190        wdd->dev = NULL;
 191}
 192EXPORT_SYMBOL_GPL(watchdog_unregister_device);
 193
 194static int __init watchdog_init(void)
 195{
 196        int err;
 197
 198        watchdog_class = class_create(THIS_MODULE, "watchdog");
 199        if (IS_ERR(watchdog_class)) {
 200                pr_err("couldn't create class\n");
 201                return PTR_ERR(watchdog_class);
 202        }
 203
 204        err = watchdog_dev_init();
 205        if (err < 0) {
 206                class_destroy(watchdog_class);
 207                return err;
 208        }
 209
 210        return 0;
 211}
 212
 213static void __exit watchdog_exit(void)
 214{
 215        watchdog_dev_exit();
 216        class_destroy(watchdog_class);
 217        ida_destroy(&watchdog_ida);
 218}
 219
 220subsys_initcall(watchdog_init);
 221module_exit(watchdog_exit);
 222
 223MODULE_AUTHOR("Alan Cox <alan@lxorguk.ukuu.org.uk>");
 224MODULE_AUTHOR("Wim Van Sebroeck <wim@iguana.be>");
 225MODULE_DESCRIPTION("WatchDog Timer Driver Core");
 226MODULE_LICENSE("GPL");
 227