1 // SPDX-License-Identifier: GPL-2.0-or-later
5 * (c) Copyright 2010 Novell, Inc.
8 #define DRV_NAME "xen_wdt"
10 #include <linux/bug.h>
11 #include <linux/errno.h>
13 #include <linux/hrtimer.h>
14 #include <linux/kernel.h>
15 #include <linux/ktime.h>
16 #include <linux/init.h>
17 #include <linux/module.h>
18 #include <linux/moduleparam.h>
19 #include <linux/platform_device.h>
20 #include <linux/watchdog.h>
22 #include <asm/xen/hypercall.h>
23 #include <xen/interface/sched.h>
25 static struct platform_device
*platform_device
;
26 static struct sched_watchdog wdt
;
27 static time64_t wdt_expires
;
29 #define WATCHDOG_TIMEOUT 60 /* in seconds */
30 static unsigned int timeout
;
31 module_param(timeout
, uint
, S_IRUGO
);
32 MODULE_PARM_DESC(timeout
, "Watchdog timeout in seconds "
33 "(default=" __MODULE_STRING(WATCHDOG_TIMEOUT
) ")");
35 static bool nowayout
= WATCHDOG_NOWAYOUT
;
36 module_param(nowayout
, bool, S_IRUGO
);
37 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started "
38 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
40 static inline time64_t
set_timeout(struct watchdog_device
*wdd
)
42 wdt
.timeout
= wdd
->timeout
;
43 return ktime_get_seconds() + wdd
->timeout
;
46 static int xen_wdt_start(struct watchdog_device
*wdd
)
51 expires
= set_timeout(wdd
);
53 err
= HYPERVISOR_sched_op(SCHEDOP_watchdog
, &wdt
);
58 wdt_expires
= expires
;
66 static int xen_wdt_stop(struct watchdog_device
*wdd
)
72 err
= HYPERVISOR_sched_op(SCHEDOP_watchdog
, &wdt
);
79 static int xen_wdt_kick(struct watchdog_device
*wdd
)
84 expires
= set_timeout(wdd
);
86 err
= HYPERVISOR_sched_op(SCHEDOP_watchdog
, &wdt
);
90 wdt_expires
= expires
;
95 static unsigned int xen_wdt_get_timeleft(struct watchdog_device
*wdd
)
97 return wdt_expires
- ktime_get_seconds();
100 static struct watchdog_info xen_wdt_info
= {
101 .identity
= DRV_NAME
,
102 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
105 static const struct watchdog_ops xen_wdt_ops
= {
106 .owner
= THIS_MODULE
,
107 .start
= xen_wdt_start
,
108 .stop
= xen_wdt_stop
,
109 .ping
= xen_wdt_kick
,
110 .get_timeleft
= xen_wdt_get_timeleft
,
113 static struct watchdog_device xen_wdt_dev
= {
114 .info
= &xen_wdt_info
,
116 .timeout
= WATCHDOG_TIMEOUT
,
119 static int xen_wdt_probe(struct platform_device
*pdev
)
121 struct device
*dev
= &pdev
->dev
;
122 struct sched_watchdog wd
= { .id
= ~0 };
123 int ret
= HYPERVISOR_sched_op(SCHEDOP_watchdog
, &wd
);
125 if (ret
== -ENOSYS
) {
126 dev_err(dev
, "watchdog not supported by hypervisor\n");
130 if (ret
!= -EINVAL
) {
131 dev_err(dev
, "unexpected hypervisor error (%d)\n", ret
);
135 watchdog_init_timeout(&xen_wdt_dev
, timeout
, NULL
);
136 watchdog_set_nowayout(&xen_wdt_dev
, nowayout
);
137 watchdog_stop_on_reboot(&xen_wdt_dev
);
138 watchdog_stop_on_unregister(&xen_wdt_dev
);
140 ret
= devm_watchdog_register_device(dev
, &xen_wdt_dev
);
144 dev_info(dev
, "initialized (timeout=%ds, nowayout=%d)\n",
145 xen_wdt_dev
.timeout
, nowayout
);
150 static int xen_wdt_suspend(struct platform_device
*dev
, pm_message_t state
)
152 typeof(wdt
.id
) id
= wdt
.id
;
153 int rc
= xen_wdt_stop(&xen_wdt_dev
);
159 static int xen_wdt_resume(struct platform_device
*dev
)
164 return xen_wdt_start(&xen_wdt_dev
);
167 static struct platform_driver xen_wdt_driver
= {
168 .probe
= xen_wdt_probe
,
169 .suspend
= xen_wdt_suspend
,
170 .resume
= xen_wdt_resume
,
176 static int __init
xen_wdt_init_module(void)
183 err
= platform_driver_register(&xen_wdt_driver
);
187 platform_device
= platform_device_register_simple(DRV_NAME
,
189 if (IS_ERR(platform_device
)) {
190 err
= PTR_ERR(platform_device
);
191 platform_driver_unregister(&xen_wdt_driver
);
197 static void __exit
xen_wdt_cleanup_module(void)
199 platform_device_unregister(platform_device
);
200 platform_driver_unregister(&xen_wdt_driver
);
203 module_init(xen_wdt_init_module
);
204 module_exit(xen_wdt_cleanup_module
);
206 MODULE_AUTHOR("Jan Beulich <jbeulich@novell.com>");
207 MODULE_DESCRIPTION("Xen WatchDog Timer Driver");
208 MODULE_LICENSE("GPL");