1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * sma cpu5 watchdog driver
5 * Copyright (C) 2003 Heiko Ronsdorf <hero@ihg.uni-duisburg.de>
8 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
16 #include <linux/ioport.h>
17 #include <linux/timer.h>
18 #include <linux/completion.h>
19 #include <linux/jiffies.h>
21 #include <linux/uaccess.h>
22 #include <linux/watchdog.h>
24 /* adjustable parameters */
27 static int port
= 0x91;
28 static int ticks
= 10000;
29 static DEFINE_SPINLOCK(cpu5wdt_lock
);
31 #define PFX "cpu5wdt: "
33 #define CPU5WDT_EXTENT 0x0A
35 #define CPU5WDT_STATUS_REG 0x00
36 #define CPU5WDT_TIME_A_REG 0x02
37 #define CPU5WDT_TIME_B_REG 0x03
38 #define CPU5WDT_MODE_REG 0x04
39 #define CPU5WDT_TRIGGER_REG 0x07
40 #define CPU5WDT_ENABLE_REG 0x08
41 #define CPU5WDT_RESET_REG 0x09
43 #define CPU5WDT_INTERVAL (HZ/10+1)
45 /* some device data */
48 struct completion stop
;
50 struct timer_list timer
;
56 /* generic helper functions */
58 static void cpu5wdt_trigger(struct timer_list
*unused
)
61 pr_debug("trigger at %i ticks\n", ticks
);
63 if (cpu5wdt_device
.running
)
66 spin_lock(&cpu5wdt_lock
);
67 /* keep watchdog alive */
68 outb(1, port
+ CPU5WDT_TRIGGER_REG
);
71 if (cpu5wdt_device
.queue
&& ticks
)
72 mod_timer(&cpu5wdt_device
.timer
, jiffies
+ CPU5WDT_INTERVAL
);
74 /* ticks doesn't matter anyway */
75 complete(&cpu5wdt_device
.stop
);
77 spin_unlock(&cpu5wdt_lock
);
81 static void cpu5wdt_reset(void)
83 ticks
= cpu5wdt_device
.default_ticks
;
86 pr_debug("reset (%i ticks)\n", (int) ticks
);
90 static void cpu5wdt_start(void)
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
);
104 /* if process dies, counter is not decremented */
105 cpu5wdt_device
.running
++;
106 spin_unlock_irqrestore(&cpu5wdt_lock
, flags
);
109 static int cpu5wdt_stop(void)
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
);
119 pr_crit("stop not possible\n");
123 /* filesystem operations */
125 static int cpu5wdt_open(struct inode
*inode
, struct file
*file
)
127 if (test_and_set_bit(0, &cpu5wdt_device
.inuse
))
129 return stream_open(inode
, file
);
132 static int cpu5wdt_release(struct inode
*inode
, struct file
*file
)
134 clear_bit(0, &cpu5wdt_device
.inuse
);
138 static long cpu5wdt_ioctl(struct file
*file
, unsigned int cmd
,
141 void __user
*argp
= (void __user
*)arg
;
142 int __user
*p
= argp
;
144 static const struct watchdog_info ident
= {
145 .options
= WDIOF_CARDRESET
,
146 .identity
= "CPU5 WDT",
150 case WDIOC_GETSUPPORT
:
151 if (copy_to_user(argp
, &ident
, sizeof(ident
)))
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
))
163 if (value
& WDIOS_ENABLECARD
)
165 if (value
& WDIOS_DISABLECARD
)
168 case WDIOC_KEEPALIVE
:
177 static ssize_t
cpu5wdt_write(struct file
*file
, const char __user
*buf
,
178 size_t count
, loff_t
*ppos
)
186 static const struct file_operations cpu5wdt_fops
= {
187 .owner
= THIS_MODULE
,
189 .unlocked_ioctl
= cpu5wdt_ioctl
,
190 .open
= cpu5wdt_open
,
191 .write
= cpu5wdt_write
,
192 .release
= cpu5wdt_release
,
195 static struct miscdevice cpu5wdt_misc
= {
196 .minor
= WATCHDOG_MINOR
,
198 .fops
= &cpu5wdt_fops
,
201 /* init/exit function */
203 static int cpu5wdt_init(void)
209 pr_debug("port=0x%x, verbose=%i\n", port
, verbose
);
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
;
216 if (!request_region(port
, CPU5WDT_EXTENT
, PFX
)) {
217 pr_err("request_region failed\n");
222 /* watchdog reboot? */
223 val
= inb(port
+ CPU5WDT_STATUS_REG
);
224 val
= (val
>> 2) & 1;
226 pr_info("sorry, was my fault\n");
228 err
= misc_register(&cpu5wdt_misc
);
230 pr_err("misc_register failed\n");
235 pr_info("init success\n");
239 release_region(port
, CPU5WDT_EXTENT
);
244 static int cpu5wdt_init_module(void)
246 return cpu5wdt_init();
249 static void cpu5wdt_exit(void)
251 if (cpu5wdt_device
.queue
) {
252 cpu5wdt_device
.queue
= 0;
253 wait_for_completion(&cpu5wdt_device
.stop
);
254 del_timer(&cpu5wdt_device
.timer
);
257 misc_deregister(&cpu5wdt_misc
);
259 release_region(port
, CPU5WDT_EXTENT
);
263 static void cpu5wdt_exit_module(void)
268 /* module entry points */
270 module_init(cpu5wdt_init_module
);
271 module_exit(cpu5wdt_exit_module
);
273 MODULE_AUTHOR("Heiko Ronsdorf <hero@ihg.uni-duisburg.de>");
274 MODULE_DESCRIPTION("sma cpu5 watchdog driver");
275 MODULE_SUPPORTED_DEVICE("sma cpu5 watchdog");
276 MODULE_LICENSE("GPL");
278 module_param_hw(port
, int, ioport
, 0);
279 MODULE_PARM_DESC(port
, "base address of watchdog card, default is 0x91");
281 module_param(verbose
, int, 0);
282 MODULE_PARM_DESC(verbose
, "be verbose, default is 0 (no)");
284 module_param(ticks
, int, 0);
285 MODULE_PARM_DESC(ticks
, "count down ticks, default is 10000");