1 // SPDX-License-Identifier: GPL-2.0-only
3 * Watchdog driver for SBC-FITPC2 board
5 * Author: Denis Turischev <denis@compulab.co.il>
7 * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
11 #define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
13 #include <linux/module.h>
14 #include <linux/types.h>
15 #include <linux/miscdevice.h>
16 #include <linux/watchdog.h>
17 #include <linux/ioport.h>
18 #include <linux/delay.h>
20 #include <linux/init.h>
21 #include <linux/moduleparam.h>
22 #include <linux/dmi.h>
24 #include <linux/uaccess.h>
27 static bool nowayout
= WATCHDOG_NOWAYOUT
;
28 static unsigned int margin
= 60; /* (secs) Default is 1 minute */
29 static unsigned long wdt_status
;
30 static DEFINE_MUTEX(wdt_lock
);
33 #define WDT_OK_TO_CLOSE 1
35 #define COMMAND_PORT 0x4c
36 #define DATA_PORT 0x48
38 #define IFACE_ON_COMMAND 1
39 #define REBOOT_COMMAND 2
41 #define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
43 static void wdt_send_data(unsigned char command
, unsigned char data
)
45 outb(data
, DATA_PORT
);
47 outb(command
, COMMAND_PORT
);
51 static void wdt_enable(void)
53 mutex_lock(&wdt_lock
);
54 wdt_send_data(IFACE_ON_COMMAND
, 1);
55 wdt_send_data(REBOOT_COMMAND
, margin
);
56 mutex_unlock(&wdt_lock
);
59 static void wdt_disable(void)
61 mutex_lock(&wdt_lock
);
62 wdt_send_data(IFACE_ON_COMMAND
, 0);
63 wdt_send_data(REBOOT_COMMAND
, 0);
64 mutex_unlock(&wdt_lock
);
67 static int fitpc2_wdt_open(struct inode
*inode
, struct file
*file
)
69 if (test_and_set_bit(WDT_IN_USE
, &wdt_status
))
72 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
76 return stream_open(inode
, file
);
79 static ssize_t
fitpc2_wdt_write(struct file
*file
, const char __user
*data
,
80 size_t len
, loff_t
*ppos
)
92 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
94 for (i
= 0; i
!= len
; i
++) {
97 if (get_user(c
, data
+ i
))
101 set_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
111 static const struct watchdog_info ident
= {
112 .options
= WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT
|
114 .identity
= WATCHDOG_NAME
,
118 static long fitpc2_wdt_ioctl(struct file
*file
, unsigned int cmd
,
125 case WDIOC_GETSUPPORT
:
126 ret
= copy_to_user((struct watchdog_info __user
*)arg
, &ident
,
127 sizeof(ident
)) ? -EFAULT
: 0;
130 case WDIOC_GETSTATUS
:
131 ret
= put_user(0, (int __user
*)arg
);
134 case WDIOC_GETBOOTSTATUS
:
135 ret
= put_user(0, (int __user
*)arg
);
138 case WDIOC_KEEPALIVE
:
143 case WDIOC_SETTIMEOUT
:
144 ret
= get_user(time
, (int __user
*)arg
);
148 if (time
< 31 || time
> 255) {
157 case WDIOC_GETTIMEOUT
:
158 ret
= put_user(margin
, (int __user
*)arg
);
165 static int fitpc2_wdt_release(struct inode
*inode
, struct file
*file
)
167 if (test_bit(WDT_OK_TO_CLOSE
, &wdt_status
)) {
169 pr_info("Device disabled\n");
171 pr_warn("Device closed unexpectedly - timer will not stop\n");
175 clear_bit(WDT_IN_USE
, &wdt_status
);
176 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
182 static const struct file_operations fitpc2_wdt_fops
= {
183 .owner
= THIS_MODULE
,
184 .write
= fitpc2_wdt_write
,
185 .unlocked_ioctl
= fitpc2_wdt_ioctl
,
186 .compat_ioctl
= compat_ptr_ioctl
,
187 .open
= fitpc2_wdt_open
,
188 .release
= fitpc2_wdt_release
,
191 static struct miscdevice fitpc2_wdt_miscdev
= {
192 .minor
= WATCHDOG_MINOR
,
194 .fops
= &fitpc2_wdt_fops
,
197 static int __init
fitpc2_wdt_init(void)
200 const char *brd_name
;
202 brd_name
= dmi_get_system_info(DMI_BOARD_NAME
);
204 if (!brd_name
|| !strstr(brd_name
, "SBC-FITPC2"))
207 pr_info("%s found\n", brd_name
);
209 if (!request_region(COMMAND_PORT
, 1, WATCHDOG_NAME
)) {
210 pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT
);
214 if (!request_region(DATA_PORT
, 1, WATCHDOG_NAME
)) {
215 pr_err("I/O address 0x%04x already in use\n", DATA_PORT
);
220 if (margin
< 31 || margin
> 255) {
221 pr_err("margin must be in range 31 - 255 seconds, you tried to set %d\n",
227 err
= misc_register(&fitpc2_wdt_miscdev
);
229 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
230 WATCHDOG_MINOR
, err
);
237 release_region(DATA_PORT
, 1);
239 release_region(COMMAND_PORT
, 1);
244 static void __exit
fitpc2_wdt_exit(void)
246 misc_deregister(&fitpc2_wdt_miscdev
);
247 release_region(DATA_PORT
, 1);
248 release_region(COMMAND_PORT
, 1);
251 module_init(fitpc2_wdt_init
);
252 module_exit(fitpc2_wdt_exit
);
254 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
255 MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
257 module_param(margin
, int, 0);
258 MODULE_PARM_DESC(margin
, "Watchdog margin in seconds (default 60s)");
260 module_param(nowayout
, bool, 0);
261 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started");
263 MODULE_LICENSE("GPL");