188 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * sun4v watchdog timer4 * (c) Copyright 2016 Oracle Corporation5 *6 * Implement a simple watchdog driver using the built-in sun4v hypervisor7 * watchdog support. If time expires, the hypervisor stops or bounces8 * the guest domain.9 */10 11#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt12 13#include <linux/errno.h>14#include <linux/init.h>15#include <linux/kernel.h>16#include <linux/module.h>17#include <linux/moduleparam.h>18#include <linux/watchdog.h>19#include <asm/hypervisor.h>20#include <asm/mdesc.h>21 22#define WDT_TIMEOUT 6023#define WDT_MAX_TIMEOUT 3153600024#define WDT_MIN_TIMEOUT 125#define WDT_DEFAULT_RESOLUTION_MS 1000 /* 1 second */26 27static unsigned int timeout;28module_param(timeout, uint, 0);29MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="30 __MODULE_STRING(WDT_TIMEOUT) ")");31 32static bool nowayout = WATCHDOG_NOWAYOUT;33module_param(nowayout, bool, S_IRUGO);34MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="35 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");36 37static int sun4v_wdt_stop(struct watchdog_device *wdd)38{39 sun4v_mach_set_watchdog(0, NULL);40 41 return 0;42}43 44static int sun4v_wdt_ping(struct watchdog_device *wdd)45{46 int hverr;47 48 /*49 * HV watchdog timer will round up the timeout50 * passed in to the nearest multiple of the51 * watchdog resolution in milliseconds.52 */53 hverr = sun4v_mach_set_watchdog(wdd->timeout * 1000, NULL);54 if (hverr == HV_EINVAL)55 return -EINVAL;56 57 return 0;58}59 60static int sun4v_wdt_set_timeout(struct watchdog_device *wdd,61 unsigned int timeout)62{63 wdd->timeout = timeout;64 65 return 0;66}67 68static const struct watchdog_info sun4v_wdt_ident = {69 .options = WDIOF_SETTIMEOUT |70 WDIOF_MAGICCLOSE |71 WDIOF_KEEPALIVEPING,72 .identity = "sun4v hypervisor watchdog",73 .firmware_version = 0,74};75 76static const struct watchdog_ops sun4v_wdt_ops = {77 .owner = THIS_MODULE,78 .start = sun4v_wdt_ping,79 .stop = sun4v_wdt_stop,80 .ping = sun4v_wdt_ping,81 .set_timeout = sun4v_wdt_set_timeout,82};83 84static struct watchdog_device wdd = {85 .info = &sun4v_wdt_ident,86 .ops = &sun4v_wdt_ops,87 .min_timeout = WDT_MIN_TIMEOUT,88 .max_timeout = WDT_MAX_TIMEOUT,89 .timeout = WDT_TIMEOUT,90};91 92static int __init sun4v_wdt_init(void)93{94 struct mdesc_handle *handle;95 u64 node;96 const u64 *value;97 int err = 0;98 unsigned long major = 1, minor = 1;99 100 /*101 * There are 2 properties that can be set from the control102 * domain for the watchdog.103 * watchdog-resolution104 * watchdog-max-timeout105 *106 * We can expect a handle to be returned otherwise something107 * serious is wrong. Correct to return -ENODEV here.108 */109 110 handle = mdesc_grab();111 if (!handle)112 return -ENODEV;113 114 node = mdesc_node_by_name(handle, MDESC_NODE_NULL, "platform");115 err = -ENODEV;116 if (node == MDESC_NODE_NULL)117 goto out_release;118 119 /*120 * This is a safe way to validate if we are on the right121 * platform.122 */123 if (sun4v_hvapi_register(HV_GRP_CORE, major, &minor))124 goto out_hv_unreg;125 126 /* Allow value of watchdog-resolution up to 1s (default) */127 value = mdesc_get_property(handle, node, "watchdog-resolution", NULL);128 err = -EINVAL;129 if (value) {130 if (*value == 0 ||131 *value > WDT_DEFAULT_RESOLUTION_MS)132 goto out_hv_unreg;133 }134 135 value = mdesc_get_property(handle, node, "watchdog-max-timeout", NULL);136 if (value) {137 /*138 * If the property value (in ms) is smaller than139 * min_timeout, return -EINVAL.140 */141 if (*value < wdd.min_timeout * 1000)142 goto out_hv_unreg;143 144 /*145 * If the property value is smaller than146 * default max_timeout then set watchdog max_timeout to147 * the value of the property in seconds.148 */149 if (*value < wdd.max_timeout * 1000)150 wdd.max_timeout = *value / 1000;151 }152 153 watchdog_init_timeout(&wdd, timeout, NULL);154 155 watchdog_set_nowayout(&wdd, nowayout);156 157 err = watchdog_register_device(&wdd);158 if (err)159 goto out_hv_unreg;160 161 pr_info("initialized (timeout=%ds, nowayout=%d)\n",162 wdd.timeout, nowayout);163 164 mdesc_release(handle);165 166 return 0;167 168out_hv_unreg:169 sun4v_hvapi_unregister(HV_GRP_CORE);170 171out_release:172 mdesc_release(handle);173 return err;174}175 176static void __exit sun4v_wdt_exit(void)177{178 sun4v_hvapi_unregister(HV_GRP_CORE);179 watchdog_unregister_device(&wdd);180}181 182module_init(sun4v_wdt_init);183module_exit(sun4v_wdt_exit);184 185MODULE_AUTHOR("Wim Coekaerts <wim.coekaerts@oracle.com>");186MODULE_DESCRIPTION("sun4v watchdog driver");187MODULE_LICENSE("GPL");188