2 * Copyright (C) 2016 National Instruments Corp.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
15 #include <linux/acpi.h>
16 #include <linux/device.h>
17 #include <linux/interrupt.h>
19 #include <linux/module.h>
20 #include <linux/watchdog.h>
22 #define NIWD_CONTROL 0x01
23 #define NIWD_COUNTER2 0x02
24 #define NIWD_COUNTER1 0x03
25 #define NIWD_COUNTER0 0x04
26 #define NIWD_SEED2 0x05
27 #define NIWD_SEED1 0x06
28 #define NIWD_SEED0 0x07
30 #define NIWD_IO_SIZE 0x08
32 #define NIWD_CONTROL_MODE 0x80
33 #define NIWD_CONTROL_PROC_RESET 0x20
34 #define NIWD_CONTROL_PET 0x10
35 #define NIWD_CONTROL_RUNNING 0x08
36 #define NIWD_CONTROL_CAPTURECOUNTER 0x04
37 #define NIWD_CONTROL_RESET 0x02
38 #define NIWD_CONTROL_ALARM 0x01
40 #define NIWD_PERIOD_NS 30720
41 #define NIWD_MIN_TIMEOUT 1
42 #define NIWD_MAX_TIMEOUT 515
43 #define NIWD_DEFAULT_TIMEOUT 60
45 #define NIWD_NAME "ni903x_wdt"
50 struct watchdog_device wdd
;
53 static unsigned int timeout
;
54 module_param(timeout
, uint
, 0);
55 MODULE_PARM_DESC(timeout
,
56 "Watchdog timeout in seconds. (default="
57 __MODULE_STRING(NIWD_DEFAULT_TIMEOUT
) ")");
59 static int nowayout
= WATCHDOG_NOWAYOUT
;
60 module_param(nowayout
, int, S_IRUGO
);
61 MODULE_PARM_DESC(nowayout
,
62 "Watchdog cannot be stopped once started (default="
63 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
65 static void ni903x_start(struct ni903x_wdt
*wdt
)
67 u8 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
69 outb(control
| NIWD_CONTROL_RESET
, wdt
->io_base
+ NIWD_CONTROL
);
70 outb(control
| NIWD_CONTROL_PET
, wdt
->io_base
+ NIWD_CONTROL
);
73 static int ni903x_wdd_set_timeout(struct watchdog_device
*wdd
,
76 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
77 u32 counter
= timeout
* (1000000000 / NIWD_PERIOD_NS
);
79 outb(((0x00FF0000 & counter
) >> 16), wdt
->io_base
+ NIWD_SEED2
);
80 outb(((0x0000FF00 & counter
) >> 8), wdt
->io_base
+ NIWD_SEED1
);
81 outb((0x000000FF & counter
), wdt
->io_base
+ NIWD_SEED0
);
83 wdd
->timeout
= timeout
;
88 static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device
*wdd
)
90 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
91 u8 control
, counter0
, counter1
, counter2
;
94 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
95 control
|= NIWD_CONTROL_CAPTURECOUNTER
;
96 outb(control
, wdt
->io_base
+ NIWD_CONTROL
);
98 counter2
= inb(wdt
->io_base
+ NIWD_COUNTER2
);
99 counter1
= inb(wdt
->io_base
+ NIWD_COUNTER1
);
100 counter0
= inb(wdt
->io_base
+ NIWD_COUNTER0
);
102 counter
= (counter2
<< 16) | (counter1
<< 8) | counter0
;
104 return counter
/ (1000000000 / NIWD_PERIOD_NS
);
107 static int ni903x_wdd_ping(struct watchdog_device
*wdd
)
109 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
112 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
113 outb(control
| NIWD_CONTROL_PET
, wdt
->io_base
+ NIWD_CONTROL
);
118 static int ni903x_wdd_start(struct watchdog_device
*wdd
)
120 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
122 outb(NIWD_CONTROL_RESET
| NIWD_CONTROL_PROC_RESET
,
123 wdt
->io_base
+ NIWD_CONTROL
);
125 ni903x_wdd_set_timeout(wdd
, wdd
->timeout
);
131 static int ni903x_wdd_stop(struct watchdog_device
*wdd
)
133 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
135 outb(NIWD_CONTROL_RESET
, wdt
->io_base
+ NIWD_CONTROL
);
140 static acpi_status
ni903x_resources(struct acpi_resource
*res
, void *data
)
142 struct ni903x_wdt
*wdt
= data
;
146 case ACPI_RESOURCE_TYPE_IO
:
147 if (wdt
->io_base
!= 0) {
148 dev_err(wdt
->dev
, "too many IO resources\n");
152 wdt
->io_base
= res
->data
.io
.minimum
;
153 io_size
= res
->data
.io
.address_length
;
155 if (io_size
< NIWD_IO_SIZE
) {
156 dev_err(wdt
->dev
, "memory region too small\n");
160 if (!devm_request_region(wdt
->dev
, wdt
->io_base
, io_size
,
162 dev_err(wdt
->dev
, "failed to get memory region\n");
168 case ACPI_RESOURCE_TYPE_END_TAG
:
170 /* Ignore unsupported resources, e.g. IRQ */
175 static const struct watchdog_info ni903x_wdd_info
= {
176 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
177 .identity
= "NI Watchdog",
180 static const struct watchdog_ops ni903x_wdd_ops
= {
181 .owner
= THIS_MODULE
,
182 .start
= ni903x_wdd_start
,
183 .stop
= ni903x_wdd_stop
,
184 .ping
= ni903x_wdd_ping
,
185 .set_timeout
= ni903x_wdd_set_timeout
,
186 .get_timeleft
= ni903x_wdd_get_timeleft
,
189 static int ni903x_acpi_add(struct acpi_device
*device
)
191 struct device
*dev
= &device
->dev
;
192 struct watchdog_device
*wdd
;
193 struct ni903x_wdt
*wdt
;
197 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
201 device
->driver_data
= wdt
;
204 status
= acpi_walk_resources(device
->handle
, METHOD_NAME__CRS
,
205 ni903x_resources
, wdt
);
206 if (ACPI_FAILURE(status
) || wdt
->io_base
== 0) {
207 dev_err(dev
, "failed to get resources\n");
212 wdd
->info
= &ni903x_wdd_info
;
213 wdd
->ops
= &ni903x_wdd_ops
;
214 wdd
->min_timeout
= NIWD_MIN_TIMEOUT
;
215 wdd
->max_timeout
= NIWD_MAX_TIMEOUT
;
216 wdd
->timeout
= NIWD_DEFAULT_TIMEOUT
;
218 watchdog_set_drvdata(wdd
, wdt
);
219 watchdog_set_nowayout(wdd
, nowayout
);
220 ret
= watchdog_init_timeout(wdd
, timeout
, dev
);
222 dev_err(dev
, "unable to set timeout value, using default\n");
224 ret
= watchdog_register_device(wdd
);
226 dev_err(dev
, "failed to register watchdog\n");
230 /* Switch from boot mode to user mode */
231 outb(NIWD_CONTROL_RESET
| NIWD_CONTROL_MODE
,
232 wdt
->io_base
+ NIWD_CONTROL
);
234 dev_dbg(dev
, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
235 wdt
->io_base
, timeout
, nowayout
);
240 static int ni903x_acpi_remove(struct acpi_device
*device
)
242 struct ni903x_wdt
*wdt
= acpi_driver_data(device
);
244 ni903x_wdd_stop(&wdt
->wdd
);
245 watchdog_unregister_device(&wdt
->wdd
);
250 static const struct acpi_device_id ni903x_device_ids
[] = {
254 MODULE_DEVICE_TABLE(acpi
, ni903x_device_ids
);
256 static struct acpi_driver ni903x_acpi_driver
= {
258 .ids
= ni903x_device_ids
,
260 .add
= ni903x_acpi_add
,
261 .remove
= ni903x_acpi_remove
,
265 module_acpi_driver(ni903x_acpi_driver
);
267 MODULE_DESCRIPTION("NI 903x Watchdog");
268 MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>");
269 MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>");
270 MODULE_LICENSE("GPL");