brintos

brintos / linux-shallow public Read only

0
0
Text · 6.0 KiB · f94b840 Raw
285 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * sma cpu5 watchdog driver4 *5 * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/module.h>11#include <linux/moduleparam.h>12#include <linux/types.h>13#include <linux/errno.h>14#include <linux/miscdevice.h>15#include <linux/fs.h>16#include <linux/ioport.h>17#include <linux/timer.h>18#include <linux/completion.h>19#include <linux/jiffies.h>20#include <linux/io.h>21#include <linux/uaccess.h>22#include <linux/watchdog.h>23 24/* adjustable parameters */25 26static int verbose;27static int port = 0x91;28static int ticks = 10000;29static DEFINE_SPINLOCK(cpu5wdt_lock);30 31#define PFX			"cpu5wdt: "32 33#define CPU5WDT_EXTENT          0x0A34 35#define CPU5WDT_STATUS_REG      0x0036#define CPU5WDT_TIME_A_REG      0x0237#define CPU5WDT_TIME_B_REG      0x0338#define CPU5WDT_MODE_REG        0x0439#define CPU5WDT_TRIGGER_REG     0x0740#define CPU5WDT_ENABLE_REG      0x0841#define CPU5WDT_RESET_REG       0x0942 43#define CPU5WDT_INTERVAL	(HZ/10+1)44 45/* some device data */46 47static struct {48	struct completion stop;49	int running;50	struct timer_list timer;51	int queue;52	int default_ticks;53	unsigned long inuse;54} cpu5wdt_device;55 56/* generic helper functions */57 58static void cpu5wdt_trigger(struct timer_list *unused)59{60	if (verbose > 2)61		pr_debug("trigger at %i ticks\n", ticks);62 63	if (cpu5wdt_device.running)64		ticks--;65 66	spin_lock(&cpu5wdt_lock);67	/* keep watchdog alive */68	outb(1, port + CPU5WDT_TRIGGER_REG);69 70	/* requeue?? */71	if (cpu5wdt_device.queue && ticks)72		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);73	else {74		/* ticks doesn't matter anyway */75		complete(&cpu5wdt_device.stop);76	}77	spin_unlock(&cpu5wdt_lock);78 79}80 81static void cpu5wdt_reset(void)82{83	ticks = cpu5wdt_device.default_ticks;84 85	if (verbose)86		pr_debug("reset (%i ticks)\n", (int) ticks);87 88}89 90static void cpu5wdt_start(void)91{92	unsigned long flags;93 94	spin_lock_irqsave(&cpu5wdt_lock, flags);95	if (!cpu5wdt_device.queue) {96		cpu5wdt_device.queue = 1;97		outb(0, port + CPU5WDT_TIME_A_REG);98		outb(0, port + CPU5WDT_TIME_B_REG);99		outb(1, port + CPU5WDT_MODE_REG);100		outb(0, port + CPU5WDT_RESET_REG);101		outb(0, port + CPU5WDT_ENABLE_REG);102		mod_timer(&cpu5wdt_device.timer, jiffies + CPU5WDT_INTERVAL);103	}104	/* if process dies, counter is not decremented */105	cpu5wdt_device.running++;106	spin_unlock_irqrestore(&cpu5wdt_lock, flags);107}108 109static int cpu5wdt_stop(void)110{111	unsigned long flags;112 113	spin_lock_irqsave(&cpu5wdt_lock, flags);114	if (cpu5wdt_device.running)115		cpu5wdt_device.running = 0;116	ticks = cpu5wdt_device.default_ticks;117	spin_unlock_irqrestore(&cpu5wdt_lock, flags);118	if (verbose)119		pr_crit("stop not possible\n");120	return -EIO;121}122 123/* filesystem operations */124 125static int cpu5wdt_open(struct inode *inode, struct file *file)126{127	if (test_and_set_bit(0, &cpu5wdt_device.inuse))128		return -EBUSY;129	return stream_open(inode, file);130}131 132static int cpu5wdt_release(struct inode *inode, struct file *file)133{134	clear_bit(0, &cpu5wdt_device.inuse);135	return 0;136}137 138static long cpu5wdt_ioctl(struct file *file, unsigned int cmd,139						unsigned long arg)140{141	void __user *argp = (void __user *)arg;142	int __user *p = argp;143	unsigned int value;144	static const struct watchdog_info ident = {145		.options = WDIOF_CARDRESET,146		.identity = "CPU5 WDT",147	};148 149	switch (cmd) {150	case WDIOC_GETSUPPORT:151		if (copy_to_user(argp, &ident, sizeof(ident)))152			return -EFAULT;153		break;154	case WDIOC_GETSTATUS:155		value = inb(port + CPU5WDT_STATUS_REG);156		value = (value >> 2) & 1;157		return put_user(value, p);158	case WDIOC_GETBOOTSTATUS:159		return put_user(0, p);160	case WDIOC_SETOPTIONS:161		if (get_user(value, p))162			return -EFAULT;163		if (value & WDIOS_ENABLECARD)164			cpu5wdt_start();165		if (value & WDIOS_DISABLECARD)166			cpu5wdt_stop();167		break;168	case WDIOC_KEEPALIVE:169		cpu5wdt_reset();170		break;171	default:172		return -ENOTTY;173	}174	return 0;175}176 177static ssize_t cpu5wdt_write(struct file *file, const char __user *buf,178						size_t count, loff_t *ppos)179{180	if (!count)181		return -EIO;182	cpu5wdt_reset();183	return count;184}185 186static const struct file_operations cpu5wdt_fops = {187	.owner		= THIS_MODULE,188	.unlocked_ioctl	= cpu5wdt_ioctl,189	.compat_ioctl	= compat_ptr_ioctl,190	.open		= cpu5wdt_open,191	.write		= cpu5wdt_write,192	.release	= cpu5wdt_release,193};194 195static struct miscdevice cpu5wdt_misc = {196	.minor	= WATCHDOG_MINOR,197	.name	= "watchdog",198	.fops	= &cpu5wdt_fops,199};200 201/* init/exit function */202 203static int cpu5wdt_init(void)204{205	unsigned int val;206	int err;207 208	if (verbose)209		pr_debug("port=0x%x, verbose=%i\n", port, verbose);210 211	init_completion(&cpu5wdt_device.stop);212	cpu5wdt_device.queue = 0;213	timer_setup(&cpu5wdt_device.timer, cpu5wdt_trigger, 0);214	cpu5wdt_device.default_ticks = ticks;215 216	if (!request_region(port, CPU5WDT_EXTENT, PFX)) {217		pr_err("request_region failed\n");218		err = -EBUSY;219		goto no_port;220	}221 222	/* watchdog reboot? */223	val = inb(port + CPU5WDT_STATUS_REG);224	val = (val >> 2) & 1;225	if (!val)226		pr_info("sorry, was my fault\n");227 228	err = misc_register(&cpu5wdt_misc);229	if (err < 0) {230		pr_err("misc_register failed\n");231		goto no_misc;232	}233 234 235	pr_info("init success\n");236	return 0;237 238no_misc:239	release_region(port, CPU5WDT_EXTENT);240no_port:241	return err;242}243 244static int cpu5wdt_init_module(void)245{246	return cpu5wdt_init();247}248 249static void cpu5wdt_exit(void)250{251	if (cpu5wdt_device.queue) {252		cpu5wdt_device.queue = 0;253		wait_for_completion(&cpu5wdt_device.stop);254		timer_shutdown_sync(&cpu5wdt_device.timer);255	}256 257	misc_deregister(&cpu5wdt_misc);258 259	release_region(port, CPU5WDT_EXTENT);260 261}262 263static void cpu5wdt_exit_module(void)264{265	cpu5wdt_exit();266}267 268/* module entry points */269 270module_init(cpu5wdt_init_module);271module_exit(cpu5wdt_exit_module);272 273MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");274MODULE_DESCRIPTION("sma cpu5 watchdog driver");275MODULE_LICENSE("GPL");276 277module_param_hw(port, int, ioport, 0);278MODULE_PARM_DESC(port, "base address of watchdog card, default is 0x91");279 280module_param(verbose, int, 0);281MODULE_PARM_DESC(verbose, "be verbose, default is 0 (no)");282 283module_param(ticks, int, 0);284MODULE_PARM_DESC(ticks, "count down ticks, default is 10000");285