Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.14 245 lines 5.9 kB view raw
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 .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 = 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");