linux/drivers/watchdog/mtx-1_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 *      Driver for the MTX-1 Watchdog.
   4 *
   5 *      (C) Copyright 2005 4G Systems <info@4g-systems.biz>,
   6 *                                                      All Rights Reserved.
   7 *                              http://www.4g-systems.biz
   8 *
   9 *      (C) Copyright 2007 OpenWrt.org, Florian Fainelli <florian@openwrt.org>
  10 *      (c) Copyright 2005    4G Systems <info@4g-systems.biz>
  11 *
  12 *      Release 0.01.
  13 *      Author: Michael Stickel  michael.stickel@4g-systems.biz
  14 *
  15 *      Release 0.02.
  16 *      Author: Florian Fainelli florian@openwrt.org
  17 *              use the Linux watchdog/timer APIs
  18 *
  19 *      The Watchdog is configured to reset the MTX-1
  20 *      if it is not triggered for 100 seconds.
  21 *      It should not be triggered more often than 1.6 seconds.
  22 *
  23 *      A timer triggers the watchdog every 5 seconds, until
  24 *      it is opened for the first time. After the first open
  25 *      it MUST be triggered every 2..95 seconds.
  26 */
  27
  28#include <linux/module.h>
  29#include <linux/moduleparam.h>
  30#include <linux/types.h>
  31#include <linux/errno.h>
  32#include <linux/miscdevice.h>
  33#include <linux/fs.h>
  34#include <linux/ioport.h>
  35#include <linux/timer.h>
  36#include <linux/completion.h>
  37#include <linux/jiffies.h>
  38#include <linux/watchdog.h>
  39#include <linux/platform_device.h>
  40#include <linux/io.h>
  41#include <linux/uaccess.h>
  42#include <linux/gpio/consumer.h>
  43
  44#define MTX1_WDT_INTERVAL       (5 * HZ)
  45
  46static int ticks = 100 * HZ;
  47
  48static struct {
  49        struct completion stop;
  50        spinlock_t lock;
  51        int running;
  52        struct timer_list timer;
  53        int queue;
  54        int default_ticks;
  55        unsigned long inuse;
  56        struct gpio_desc *gpiod;
  57        unsigned int gstate;
  58} mtx1_wdt_device;
  59
  60static void mtx1_wdt_trigger(struct timer_list *unused)
  61{
  62        spin_lock(&mtx1_wdt_device.lock);
  63        if (mtx1_wdt_device.running)
  64                ticks--;
  65
  66        /* toggle wdt gpio */
  67        mtx1_wdt_device.gstate = !mtx1_wdt_device.gstate;
  68        gpiod_set_value(mtx1_wdt_device.gpiod, mtx1_wdt_device.gstate);
  69
  70        if (mtx1_wdt_device.queue && ticks)
  71                mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
  72        else
  73                complete(&mtx1_wdt_device.stop);
  74        spin_unlock(&mtx1_wdt_device.lock);
  75}
  76
  77static void mtx1_wdt_reset(void)
  78{
  79        ticks = mtx1_wdt_device.default_ticks;
  80}
  81
  82
  83static void mtx1_wdt_start(void)
  84{
  85        unsigned long flags;
  86
  87        spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
  88        if (!mtx1_wdt_device.queue) {
  89                mtx1_wdt_device.queue = 1;
  90                mtx1_wdt_device.gstate = 1;
  91                gpiod_set_value(mtx1_wdt_device.gpiod, 1);
  92                mod_timer(&mtx1_wdt_device.timer, jiffies + MTX1_WDT_INTERVAL);
  93        }
  94        mtx1_wdt_device.running++;
  95        spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
  96}
  97
  98static int mtx1_wdt_stop(void)
  99{
 100        unsigned long flags;
 101
 102        spin_lock_irqsave(&mtx1_wdt_device.lock, flags);
 103        if (mtx1_wdt_device.queue) {
 104                mtx1_wdt_device.queue = 0;
 105                mtx1_wdt_device.gstate = 0;
 106                gpiod_set_value(mtx1_wdt_device.gpiod, 0);
 107        }
 108        ticks = mtx1_wdt_device.default_ticks;
 109        spin_unlock_irqrestore(&mtx1_wdt_device.lock, flags);
 110        return 0;
 111}
 112
 113/* Filesystem functions */
 114
 115static int mtx1_wdt_open(struct inode *inode, struct file *file)
 116{
 117        if (test_and_set_bit(0, &mtx1_wdt_device.inuse))
 118                return -EBUSY;
 119        return stream_open(inode, file);
 120}
 121
 122
 123static int mtx1_wdt_release(struct inode *inode, struct file *file)
 124{
 125        clear_bit(0, &mtx1_wdt_device.inuse);
 126        return 0;
 127}
 128
 129static long mtx1_wdt_ioctl(struct file *file, unsigned int cmd,
 130                                                        unsigned long arg)
 131{
 132        void __user *argp = (void __user *)arg;
 133        int __user *p = (int __user *)argp;
 134        unsigned int value;
 135        static const struct watchdog_info ident = {
 136                .options = WDIOF_CARDRESET,
 137                .identity = "MTX-1 WDT",
 138        };
 139
 140        switch (cmd) {
 141        case WDIOC_GETSUPPORT:
 142                if (copy_to_user(argp, &ident, sizeof(ident)))
 143                        return -EFAULT;
 144                break;
 145        case WDIOC_GETSTATUS:
 146        case WDIOC_GETBOOTSTATUS:
 147                put_user(0, p);
 148                break;
 149        case WDIOC_SETOPTIONS:
 150                if (get_user(value, p))
 151                        return -EFAULT;
 152                if (value & WDIOS_ENABLECARD)
 153                        mtx1_wdt_start();
 154                else if (value & WDIOS_DISABLECARD)
 155                        mtx1_wdt_stop();
 156                else
 157                        return -EINVAL;
 158                return 0;
 159        case WDIOC_KEEPALIVE:
 160                mtx1_wdt_reset();
 161                break;
 162        default:
 163                return -ENOTTY;
 164        }
 165        return 0;
 166}
 167
 168
 169static ssize_t mtx1_wdt_write(struct file *file, const char *buf,
 170                                                size_t count, loff_t *ppos)
 171{
 172        if (!count)
 173                return -EIO;
 174        mtx1_wdt_reset();
 175        return count;
 176}
 177
 178static const struct file_operations mtx1_wdt_fops = {
 179        .owner          = THIS_MODULE,
 180        .llseek         = no_llseek,
 181        .unlocked_ioctl = mtx1_wdt_ioctl,
 182        .compat_ioctl   = compat_ptr_ioctl,
 183        .open           = mtx1_wdt_open,
 184        .write          = mtx1_wdt_write,
 185        .release        = mtx1_wdt_release,
 186};
 187
 188
 189static struct miscdevice mtx1_wdt_misc = {
 190        .minor  = WATCHDOG_MINOR,
 191        .name   = "watchdog",
 192        .fops   = &mtx1_wdt_fops,
 193};
 194
 195
 196static int mtx1_wdt_probe(struct platform_device *pdev)
 197{
 198        int ret;
 199
 200        mtx1_wdt_device.gpiod = devm_gpiod_get(&pdev->dev,
 201                                               NULL, GPIOD_OUT_HIGH);
 202        if (IS_ERR(mtx1_wdt_device.gpiod)) {
 203                dev_err(&pdev->dev, "failed to request gpio");
 204                return PTR_ERR(mtx1_wdt_device.gpiod);
 205        }
 206
 207        spin_lock_init(&mtx1_wdt_device.lock);
 208        init_completion(&mtx1_wdt_device.stop);
 209        mtx1_wdt_device.queue = 0;
 210        clear_bit(0, &mtx1_wdt_device.inuse);
 211        timer_setup(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0);
 212        mtx1_wdt_device.default_ticks = ticks;
 213
 214        ret = misc_register(&mtx1_wdt_misc);
 215        if (ret < 0) {
 216                dev_err(&pdev->dev, "failed to register\n");
 217                return ret;
 218        }
 219        mtx1_wdt_start();
 220        dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");
 221        return 0;
 222}
 223
 224static int mtx1_wdt_remove(struct platform_device *pdev)
 225{
 226        /* FIXME: do we need to lock this test ? */
 227        if (mtx1_wdt_device.queue) {
 228                mtx1_wdt_device.queue = 0;
 229                wait_for_completion(&mtx1_wdt_device.stop);
 230        }
 231
 232        misc_deregister(&mtx1_wdt_misc);
 233        return 0;
 234}
 235
 236static struct platform_driver mtx1_wdt_driver = {
 237        .probe = mtx1_wdt_probe,
 238        .remove = mtx1_wdt_remove,
 239        .driver.name = "mtx1-wdt",
 240        .driver.owner = THIS_MODULE,
 241};
 242
 243module_platform_driver(mtx1_wdt_driver);
 244
 245MODULE_AUTHOR("Michael Stickel, Florian Fainelli");
 246MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");
 247MODULE_LICENSE("GPL");
 248MODULE_ALIAS("platform:mtx1-wdt");
 249