246 lines · c
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.biz8 *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.biz14 *15 * Release 0.02.16 * Author: Florian Fainelli florian@openwrt.org17 * use the Linux watchdog/timer APIs18 *19 * The Watchdog is configured to reset the MTX-120 * 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, until24 * it is opened for the first time. After the first open25 * 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 else73 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 else157 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 .unlocked_ioctl = mtx1_wdt_ioctl,181 .compat_ioctl = compat_ptr_ioctl,182 .open = mtx1_wdt_open,183 .write = mtx1_wdt_write,184 .release = mtx1_wdt_release,185};186 187 188static struct miscdevice mtx1_wdt_misc = {189 .minor = WATCHDOG_MINOR,190 .name = "watchdog",191 .fops = &mtx1_wdt_fops,192};193 194 195static int mtx1_wdt_probe(struct platform_device *pdev)196{197 int ret;198 199 mtx1_wdt_device.gpiod = devm_gpiod_get(&pdev->dev,200 NULL, GPIOD_OUT_HIGH);201 if (IS_ERR(mtx1_wdt_device.gpiod)) {202 dev_err(&pdev->dev, "failed to request gpio");203 return PTR_ERR(mtx1_wdt_device.gpiod);204 }205 206 spin_lock_init(&mtx1_wdt_device.lock);207 init_completion(&mtx1_wdt_device.stop);208 mtx1_wdt_device.queue = 0;209 clear_bit(0, &mtx1_wdt_device.inuse);210 timer_setup(&mtx1_wdt_device.timer, mtx1_wdt_trigger, 0);211 mtx1_wdt_device.default_ticks = ticks;212 213 ret = misc_register(&mtx1_wdt_misc);214 if (ret < 0) {215 dev_err(&pdev->dev, "failed to register\n");216 return ret;217 }218 mtx1_wdt_start();219 dev_info(&pdev->dev, "MTX-1 Watchdog driver\n");220 return 0;221}222 223static void mtx1_wdt_remove(struct platform_device *pdev)224{225 /* FIXME: do we need to lock this test ? */226 if (mtx1_wdt_device.queue) {227 mtx1_wdt_device.queue = 0;228 wait_for_completion(&mtx1_wdt_device.stop);229 }230 231 misc_deregister(&mtx1_wdt_misc);232}233 234static struct platform_driver mtx1_wdt_driver = {235 .probe = mtx1_wdt_probe,236 .remove_new = mtx1_wdt_remove,237 .driver.name = "mtx1-wdt",238};239 240module_platform_driver(mtx1_wdt_driver);241 242MODULE_AUTHOR("Michael Stickel, Florian Fainelli");243MODULE_DESCRIPTION("Driver for the MTX-1 watchdog");244MODULE_LICENSE("GPL");245MODULE_ALIAS("platform:mtx1-wdt");246