2 * Watchdog driver for SBC-FITPC2 board
4 * Author: Denis Turischev <denis@compulab.co.il>
6 * Adapted from the IXP2000 watchdog driver by Deepak Saxena.
8 * This file is licensed under the terms of the GNU General Public
9 * License version 2. This program is licensed "as is" without any
10 * warranty of any kind, whether express or implied.
13 #define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt
15 #include <linux/module.h>
16 #include <linux/types.h>
17 #include <linux/miscdevice.h>
18 #include <linux/watchdog.h>
19 #include <linux/ioport.h>
20 #include <linux/delay.h>
22 #include <linux/init.h>
23 #include <linux/moduleparam.h>
24 #include <linux/dmi.h>
26 #include <linux/uaccess.h>
29 static bool nowayout
= WATCHDOG_NOWAYOUT
;
30 static unsigned int margin
= 60; /* (secs) Default is 1 minute */
31 static unsigned long wdt_status
;
32 static DEFINE_MUTEX(wdt_lock
);
35 #define WDT_OK_TO_CLOSE 1
37 #define COMMAND_PORT 0x4c
38 #define DATA_PORT 0x48
40 #define IFACE_ON_COMMAND 1
41 #define REBOOT_COMMAND 2
43 #define WATCHDOG_NAME "SBC-FITPC2 Watchdog"
45 static void wdt_send_data(unsigned char command
, unsigned char data
)
47 outb(data
, DATA_PORT
);
49 outb(command
, COMMAND_PORT
);
53 static void wdt_enable(void)
55 mutex_lock(&wdt_lock
);
56 wdt_send_data(IFACE_ON_COMMAND
, 1);
57 wdt_send_data(REBOOT_COMMAND
, margin
);
58 mutex_unlock(&wdt_lock
);
61 static void wdt_disable(void)
63 mutex_lock(&wdt_lock
);
64 wdt_send_data(IFACE_ON_COMMAND
, 0);
65 wdt_send_data(REBOOT_COMMAND
, 0);
66 mutex_unlock(&wdt_lock
);
69 static int fitpc2_wdt_open(struct inode
*inode
, struct file
*file
)
71 if (test_and_set_bit(WDT_IN_USE
, &wdt_status
))
74 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
78 return nonseekable_open(inode
, file
);
81 static ssize_t
fitpc2_wdt_write(struct file
*file
, const char *data
,
82 size_t len
, loff_t
*ppos
)
94 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
96 for (i
= 0; i
!= len
; i
++) {
99 if (get_user(c
, data
+ i
))
103 set_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
113 static const struct watchdog_info ident
= {
114 .options
= WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT
|
116 .identity
= WATCHDOG_NAME
,
120 static long fitpc2_wdt_ioctl(struct file
*file
, unsigned int cmd
,
127 case WDIOC_GETSUPPORT
:
128 ret
= copy_to_user((struct watchdog_info
*)arg
, &ident
,
129 sizeof(ident
)) ? -EFAULT
: 0;
132 case WDIOC_GETSTATUS
:
133 ret
= put_user(0, (int *)arg
);
136 case WDIOC_GETBOOTSTATUS
:
137 ret
= put_user(0, (int *)arg
);
140 case WDIOC_KEEPALIVE
:
145 case WDIOC_SETTIMEOUT
:
146 ret
= get_user(time
, (int *)arg
);
150 if (time
< 31 || time
> 255) {
159 case WDIOC_GETTIMEOUT
:
160 ret
= put_user(margin
, (int *)arg
);
167 static int fitpc2_wdt_release(struct inode
*inode
, struct file
*file
)
169 if (test_bit(WDT_OK_TO_CLOSE
, &wdt_status
)) {
171 pr_info("Device disabled\n");
173 pr_warn("Device closed unexpectedly - timer will not stop\n");
177 clear_bit(WDT_IN_USE
, &wdt_status
);
178 clear_bit(WDT_OK_TO_CLOSE
, &wdt_status
);
184 static const struct file_operations fitpc2_wdt_fops
= {
185 .owner
= THIS_MODULE
,
187 .write
= fitpc2_wdt_write
,
188 .unlocked_ioctl
= fitpc2_wdt_ioctl
,
189 .open
= fitpc2_wdt_open
,
190 .release
= fitpc2_wdt_release
,
193 static struct miscdevice fitpc2_wdt_miscdev
= {
194 .minor
= WATCHDOG_MINOR
,
196 .fops
= &fitpc2_wdt_fops
,
199 static int __init
fitpc2_wdt_init(void)
202 const char *brd_name
;
204 brd_name
= dmi_get_system_info(DMI_BOARD_NAME
);
206 if (!brd_name
|| !strstr(brd_name
, "SBC-FITPC2"))
209 pr_info("%s found\n", brd_name
);
211 if (!request_region(COMMAND_PORT
, 1, WATCHDOG_NAME
)) {
212 pr_err("I/O address 0x%04x already in use\n", COMMAND_PORT
);
216 if (!request_region(DATA_PORT
, 1, WATCHDOG_NAME
)) {
217 pr_err("I/O address 0x%04x already in use\n", DATA_PORT
);
222 if (margin
< 31 || margin
> 255) {
223 pr_err("margin must be in range 31 - 255 seconds, you tried to set %d\n",
229 err
= misc_register(&fitpc2_wdt_miscdev
);
231 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
232 WATCHDOG_MINOR
, err
);
239 release_region(DATA_PORT
, 1);
241 release_region(COMMAND_PORT
, 1);
246 static void __exit
fitpc2_wdt_exit(void)
248 misc_deregister(&fitpc2_wdt_miscdev
);
249 release_region(DATA_PORT
, 1);
250 release_region(COMMAND_PORT
, 1);
253 module_init(fitpc2_wdt_init
);
254 module_exit(fitpc2_wdt_exit
);
256 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
257 MODULE_DESCRIPTION("SBC-FITPC2 Watchdog");
259 module_param(margin
, int, 0);
260 MODULE_PARM_DESC(margin
, "Watchdog margin in seconds (default 60s)");
262 module_param(nowayout
, bool, 0);
263 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started");
265 MODULE_LICENSE("GPL");
266 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);