brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 11f0502 Raw
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