1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 National Instruments Corp.
6 #include <linux/acpi.h>
7 #include <linux/bitops.h>
8 #include <linux/device.h>
10 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/watchdog.h>
17 #define WDT_CTRL_RESET_EN BIT(7)
18 #define WDT_RELOAD_PORT_EN BIT(7)
21 #define WDT_RELOAD_CTRL 2
22 #define WDT_PRESET_PRESCALE 4
23 #define WDT_REG_LOCK 5
25 #define WDT_RELOAD_PORT 7
27 #define WDT_MIN_TIMEOUT 1
28 #define WDT_MAX_TIMEOUT 464
29 #define WDT_DEFAULT_TIMEOUT 80
31 #define WDT_MAX_COUNTER 15
33 static unsigned int timeout
;
34 module_param(timeout
, uint
, 0);
35 MODULE_PARM_DESC(timeout
,
36 "Watchdog timeout in seconds. (default="
37 __MODULE_STRING(WDT_DEFAULT_TIMEOUT
) ")");
39 static bool nowayout
= WATCHDOG_NOWAYOUT
;
40 module_param(nowayout
, bool, 0);
41 MODULE_PARM_DESC(nowayout
,
42 "Watchdog cannot be stopped once started. (default="
43 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
48 struct watchdog_device wdd
;
51 struct nic7018_config
{
56 static const struct nic7018_config nic7018_configs
[] = {
61 static inline u32
nic7018_timeout(u32 period
, u8 counter
)
63 return period
* counter
- period
/ 2;
66 static const struct nic7018_config
*nic7018_get_config(u32 timeout
,
69 const struct nic7018_config
*config
;
72 if (timeout
< 30 && timeout
!= 16) {
73 config
= &nic7018_configs
[0];
74 count
= timeout
/ 2 + 1;
76 config
= &nic7018_configs
[1];
77 count
= DIV_ROUND_UP(timeout
+ 16, 32);
79 if (count
> WDT_MAX_COUNTER
)
80 count
= WDT_MAX_COUNTER
;
86 static int nic7018_set_timeout(struct watchdog_device
*wdd
,
89 struct nic7018_wdt
*wdt
= watchdog_get_drvdata(wdd
);
90 const struct nic7018_config
*config
;
93 config
= nic7018_get_config(timeout
, &counter
);
95 outb(counter
<< 4 | config
->divider
,
96 wdt
->io_base
+ WDT_PRESET_PRESCALE
);
98 wdd
->timeout
= nic7018_timeout(config
->period
, counter
);
99 wdt
->period
= config
->period
;
104 static int nic7018_start(struct watchdog_device
*wdd
)
106 struct nic7018_wdt
*wdt
= watchdog_get_drvdata(wdd
);
109 nic7018_set_timeout(wdd
, wdd
->timeout
);
111 control
= inb(wdt
->io_base
+ WDT_RELOAD_CTRL
);
112 outb(control
| WDT_RELOAD_PORT_EN
, wdt
->io_base
+ WDT_RELOAD_CTRL
);
114 outb(1, wdt
->io_base
+ WDT_RELOAD_PORT
);
116 control
= inb(wdt
->io_base
+ WDT_CTRL
);
117 outb(control
| WDT_CTRL_RESET_EN
, wdt
->io_base
+ WDT_CTRL
);
122 static int nic7018_stop(struct watchdog_device
*wdd
)
124 struct nic7018_wdt
*wdt
= watchdog_get_drvdata(wdd
);
126 outb(0, wdt
->io_base
+ WDT_CTRL
);
127 outb(0, wdt
->io_base
+ WDT_RELOAD_CTRL
);
128 outb(0xF0, wdt
->io_base
+ WDT_PRESET_PRESCALE
);
133 static int nic7018_ping(struct watchdog_device
*wdd
)
135 struct nic7018_wdt
*wdt
= watchdog_get_drvdata(wdd
);
137 outb(1, wdt
->io_base
+ WDT_RELOAD_PORT
);
142 static unsigned int nic7018_get_timeleft(struct watchdog_device
*wdd
)
144 struct nic7018_wdt
*wdt
= watchdog_get_drvdata(wdd
);
147 count
= inb(wdt
->io_base
+ WDT_COUNT
) & 0xF;
151 return nic7018_timeout(wdt
->period
, count
);
154 static const struct watchdog_info nic7018_wdd_info
= {
155 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
156 .identity
= "NIC7018 Watchdog",
159 static const struct watchdog_ops nic7018_wdd_ops
= {
160 .owner
= THIS_MODULE
,
161 .start
= nic7018_start
,
162 .stop
= nic7018_stop
,
163 .ping
= nic7018_ping
,
164 .set_timeout
= nic7018_set_timeout
,
165 .get_timeleft
= nic7018_get_timeleft
,
168 static int nic7018_probe(struct platform_device
*pdev
)
170 struct device
*dev
= &pdev
->dev
;
171 struct watchdog_device
*wdd
;
172 struct nic7018_wdt
*wdt
;
173 struct resource
*io_rc
;
176 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
180 platform_set_drvdata(pdev
, wdt
);
182 io_rc
= platform_get_resource(pdev
, IORESOURCE_IO
, 0);
184 dev_err(dev
, "missing IO resources\n");
188 if (!devm_request_region(dev
, io_rc
->start
, resource_size(io_rc
),
190 dev_err(dev
, "failed to get IO region\n");
194 wdt
->io_base
= io_rc
->start
;
196 wdd
->info
= &nic7018_wdd_info
;
197 wdd
->ops
= &nic7018_wdd_ops
;
198 wdd
->min_timeout
= WDT_MIN_TIMEOUT
;
199 wdd
->max_timeout
= WDT_MAX_TIMEOUT
;
200 wdd
->timeout
= WDT_DEFAULT_TIMEOUT
;
203 watchdog_set_drvdata(wdd
, wdt
);
204 watchdog_set_nowayout(wdd
, nowayout
);
205 watchdog_init_timeout(wdd
, timeout
, dev
);
207 /* Unlock WDT register */
208 outb(UNLOCK
, wdt
->io_base
+ WDT_REG_LOCK
);
210 ret
= watchdog_register_device(wdd
);
212 outb(LOCK
, wdt
->io_base
+ WDT_REG_LOCK
);
216 dev_dbg(dev
, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
217 wdt
->io_base
, timeout
, nowayout
);
221 static int nic7018_remove(struct platform_device
*pdev
)
223 struct nic7018_wdt
*wdt
= platform_get_drvdata(pdev
);
225 watchdog_unregister_device(&wdt
->wdd
);
227 /* Lock WDT register */
228 outb(LOCK
, wdt
->io_base
+ WDT_REG_LOCK
);
233 static const struct acpi_device_id nic7018_device_ids
[] = {
237 MODULE_DEVICE_TABLE(acpi
, nic7018_device_ids
);
239 static struct platform_driver watchdog_driver
= {
240 .probe
= nic7018_probe
,
241 .remove
= nic7018_remove
,
243 .name
= KBUILD_MODNAME
,
244 .acpi_match_table
= ACPI_PTR(nic7018_device_ids
),
248 module_platform_driver(watchdog_driver
);
250 MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver");
251 MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");
252 MODULE_LICENSE("GPL");