1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 National Instruments Corp.
6 #include <linux/acpi.h>
7 #include <linux/device.h>
8 #include <linux/interrupt.h>
10 #include <linux/module.h>
11 #include <linux/watchdog.h>
13 #define NIWD_CONTROL 0x01
14 #define NIWD_COUNTER2 0x02
15 #define NIWD_COUNTER1 0x03
16 #define NIWD_COUNTER0 0x04
17 #define NIWD_SEED2 0x05
18 #define NIWD_SEED1 0x06
19 #define NIWD_SEED0 0x07
21 #define NIWD_IO_SIZE 0x08
23 #define NIWD_CONTROL_MODE 0x80
24 #define NIWD_CONTROL_PROC_RESET 0x20
25 #define NIWD_CONTROL_PET 0x10
26 #define NIWD_CONTROL_RUNNING 0x08
27 #define NIWD_CONTROL_CAPTURECOUNTER 0x04
28 #define NIWD_CONTROL_RESET 0x02
29 #define NIWD_CONTROL_ALARM 0x01
31 #define NIWD_PERIOD_NS 30720
32 #define NIWD_MIN_TIMEOUT 1
33 #define NIWD_MAX_TIMEOUT 515
34 #define NIWD_DEFAULT_TIMEOUT 60
36 #define NIWD_NAME "ni903x_wdt"
41 struct watchdog_device wdd
;
44 static unsigned int timeout
;
45 module_param(timeout
, uint
, 0);
46 MODULE_PARM_DESC(timeout
,
47 "Watchdog timeout in seconds. (default="
48 __MODULE_STRING(NIWD_DEFAULT_TIMEOUT
) ")");
50 static int nowayout
= WATCHDOG_NOWAYOUT
;
51 module_param(nowayout
, int, S_IRUGO
);
52 MODULE_PARM_DESC(nowayout
,
53 "Watchdog cannot be stopped once started (default="
54 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
56 static void ni903x_start(struct ni903x_wdt
*wdt
)
58 u8 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
60 outb(control
| NIWD_CONTROL_RESET
, wdt
->io_base
+ NIWD_CONTROL
);
61 outb(control
| NIWD_CONTROL_PET
, wdt
->io_base
+ NIWD_CONTROL
);
64 static int ni903x_wdd_set_timeout(struct watchdog_device
*wdd
,
67 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
68 u32 counter
= timeout
* (1000000000 / NIWD_PERIOD_NS
);
70 outb(((0x00FF0000 & counter
) >> 16), wdt
->io_base
+ NIWD_SEED2
);
71 outb(((0x0000FF00 & counter
) >> 8), wdt
->io_base
+ NIWD_SEED1
);
72 outb((0x000000FF & counter
), wdt
->io_base
+ NIWD_SEED0
);
74 wdd
->timeout
= timeout
;
79 static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device
*wdd
)
81 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
82 u8 control
, counter0
, counter1
, counter2
;
85 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
86 control
|= NIWD_CONTROL_CAPTURECOUNTER
;
87 outb(control
, wdt
->io_base
+ NIWD_CONTROL
);
89 counter2
= inb(wdt
->io_base
+ NIWD_COUNTER2
);
90 counter1
= inb(wdt
->io_base
+ NIWD_COUNTER1
);
91 counter0
= inb(wdt
->io_base
+ NIWD_COUNTER0
);
93 counter
= (counter2
<< 16) | (counter1
<< 8) | counter0
;
95 return counter
/ (1000000000 / NIWD_PERIOD_NS
);
98 static int ni903x_wdd_ping(struct watchdog_device
*wdd
)
100 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
103 control
= inb(wdt
->io_base
+ NIWD_CONTROL
);
104 outb(control
| NIWD_CONTROL_PET
, wdt
->io_base
+ NIWD_CONTROL
);
109 static int ni903x_wdd_start(struct watchdog_device
*wdd
)
111 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
113 outb(NIWD_CONTROL_RESET
| NIWD_CONTROL_PROC_RESET
,
114 wdt
->io_base
+ NIWD_CONTROL
);
116 ni903x_wdd_set_timeout(wdd
, wdd
->timeout
);
122 static int ni903x_wdd_stop(struct watchdog_device
*wdd
)
124 struct ni903x_wdt
*wdt
= watchdog_get_drvdata(wdd
);
126 outb(NIWD_CONTROL_RESET
, wdt
->io_base
+ NIWD_CONTROL
);
131 static acpi_status
ni903x_resources(struct acpi_resource
*res
, void *data
)
133 struct ni903x_wdt
*wdt
= data
;
137 case ACPI_RESOURCE_TYPE_IO
:
138 if (wdt
->io_base
!= 0) {
139 dev_err(wdt
->dev
, "too many IO resources\n");
143 wdt
->io_base
= res
->data
.io
.minimum
;
144 io_size
= res
->data
.io
.address_length
;
146 if (io_size
< NIWD_IO_SIZE
) {
147 dev_err(wdt
->dev
, "memory region too small\n");
151 if (!devm_request_region(wdt
->dev
, wdt
->io_base
, io_size
,
153 dev_err(wdt
->dev
, "failed to get memory region\n");
159 case ACPI_RESOURCE_TYPE_END_TAG
:
161 /* Ignore unsupported resources, e.g. IRQ */
166 static const struct watchdog_info ni903x_wdd_info
= {
167 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
168 .identity
= "NI Watchdog",
171 static const struct watchdog_ops ni903x_wdd_ops
= {
172 .owner
= THIS_MODULE
,
173 .start
= ni903x_wdd_start
,
174 .stop
= ni903x_wdd_stop
,
175 .ping
= ni903x_wdd_ping
,
176 .set_timeout
= ni903x_wdd_set_timeout
,
177 .get_timeleft
= ni903x_wdd_get_timeleft
,
180 static int ni903x_acpi_add(struct acpi_device
*device
)
182 struct device
*dev
= &device
->dev
;
183 struct watchdog_device
*wdd
;
184 struct ni903x_wdt
*wdt
;
188 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
192 device
->driver_data
= wdt
;
195 status
= acpi_walk_resources(device
->handle
, METHOD_NAME__CRS
,
196 ni903x_resources
, wdt
);
197 if (ACPI_FAILURE(status
) || wdt
->io_base
== 0) {
198 dev_err(dev
, "failed to get resources\n");
203 wdd
->info
= &ni903x_wdd_info
;
204 wdd
->ops
= &ni903x_wdd_ops
;
205 wdd
->min_timeout
= NIWD_MIN_TIMEOUT
;
206 wdd
->max_timeout
= NIWD_MAX_TIMEOUT
;
207 wdd
->timeout
= NIWD_DEFAULT_TIMEOUT
;
209 watchdog_set_drvdata(wdd
, wdt
);
210 watchdog_set_nowayout(wdd
, nowayout
);
211 watchdog_init_timeout(wdd
, timeout
, dev
);
213 ret
= watchdog_register_device(wdd
);
217 /* Switch from boot mode to user mode */
218 outb(NIWD_CONTROL_RESET
| NIWD_CONTROL_MODE
,
219 wdt
->io_base
+ NIWD_CONTROL
);
221 dev_dbg(dev
, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
222 wdt
->io_base
, timeout
, nowayout
);
227 static int ni903x_acpi_remove(struct acpi_device
*device
)
229 struct ni903x_wdt
*wdt
= acpi_driver_data(device
);
231 ni903x_wdd_stop(&wdt
->wdd
);
232 watchdog_unregister_device(&wdt
->wdd
);
237 static const struct acpi_device_id ni903x_device_ids
[] = {
241 MODULE_DEVICE_TABLE(acpi
, ni903x_device_ids
);
243 static struct acpi_driver ni903x_acpi_driver
= {
245 .ids
= ni903x_device_ids
,
247 .add
= ni903x_acpi_add
,
248 .remove
= ni903x_acpi_remove
,
252 module_acpi_driver(ni903x_acpi_driver
);
254 MODULE_DESCRIPTION("NI 903x Watchdog");
255 MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>");
256 MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>");
257 MODULE_LICENSE("GPL");