File Index
Files and directories
Sign in
FILE

diag288_wdt.c

File preview with syntax highlighting.

linux-6.19.14/drivers/watchdog/diag288_wdt.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Watchdog driver for z/VM and LPAR using the diag 288 interface.
  4 *
  5 * Under z/VM, expiration of the watchdog will send a "system restart" command
  6 * to CP.
  7 *
  8 * The command can be altered using the module parameter "cmd". This is
  9 * not recommended because it's only supported on z/VM but not with LPAR.
 10 *
 11 * On LPAR, the watchdog will always trigger a system restart. The module
 12 * parameter cmd is meaningless here.
 13 *
 14 *
 15 * Copyright IBM Corp. 2004, 2013
 16 * Author(s): Arnd Bergmann (arndb@de.ibm.com)
 17 *	      Philipp Hachtmann (phacht@de.ibm.com)
 18 *
 19 */
 20
 21#define pr_fmt(fmt) "diag288_wdt: " fmt
 22
 23#include <linux/init.h>
 24#include <linux/kernel.h>
 25#include <linux/module.h>
 26#include <linux/moduleparam.h>
 27#include <linux/slab.h>
 28#include <linux/watchdog.h>
 29#include <asm/machine.h>
 30#include <asm/ebcdic.h>
 31#include <asm/diag288.h>
 32#include <asm/diag.h>
 33#include <linux/io.h>
 34
 35#define MAX_CMDLEN 240
 36#define DEFAULT_CMD "SYSTEM RESTART"
 37
 38static char wdt_cmd[MAX_CMDLEN] = DEFAULT_CMD;
 39static bool conceal_on;
 40static bool nowayout_info = WATCHDOG_NOWAYOUT;
 41
 42MODULE_LICENSE("GPL");
 43MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
 44MODULE_AUTHOR("Philipp Hachtmann <phacht@de.ibm.com>");
 45
 46MODULE_DESCRIPTION("System z diag288  Watchdog Timer");
 47
 48module_param_string(cmd, wdt_cmd, MAX_CMDLEN, 0644);
 49MODULE_PARM_DESC(cmd, "CP command that is run when the watchdog triggers (z/VM only)");
 50
 51module_param_named(conceal, conceal_on, bool, 0644);
 52MODULE_PARM_DESC(conceal, "Enable the CONCEAL CP option while the watchdog is active (z/VM only)");
 53
 54module_param_named(nowayout, nowayout_info, bool, 0444);
 55MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default = CONFIG_WATCHDOG_NOWAYOUT)");
 56
 57MODULE_ALIAS("vmwatchdog");
 58
 59static char *cmd_buf;
 60
 61static int diag288(unsigned int func, unsigned int timeout,
 62		   unsigned long action, unsigned int len)
 63{
 64	diag_stat_inc(DIAG_STAT_X288);
 65	return __diag288(func, timeout, action, len);
 66}
 67
 68static int diag288_str(unsigned int func, unsigned int timeout, char *cmd)
 69{
 70	ssize_t len;
 71
 72	len = strscpy(cmd_buf, cmd, MAX_CMDLEN);
 73	if (len < 0)
 74		return len;
 75	ASCEBC(cmd_buf, MAX_CMDLEN);
 76	EBC_TOUPPER(cmd_buf, MAX_CMDLEN);
 77
 78	return diag288(func, timeout, virt_to_phys(cmd_buf), len);
 79}
 80
 81static int wdt_start(struct watchdog_device *dev)
 82{
 83	int ret;
 84	unsigned int func;
 85
 86	if (machine_is_vm()) {
 87		func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
 88			: WDT_FUNC_INIT;
 89		ret = diag288_str(func, dev->timeout, wdt_cmd);
 90		WARN_ON(ret != 0);
 91	} else {
 92		ret = diag288(WDT_FUNC_INIT, dev->timeout, LPARWDT_RESTART, 0);
 93	}
 94
 95	if (ret) {
 96		pr_err("The watchdog cannot be activated\n");
 97		return ret;
 98	}
 99	return 0;
100}
101
102static int wdt_stop(struct watchdog_device *dev)
103{
104	return diag288(WDT_FUNC_CANCEL, 0, 0, 0);
105}
106
107static int wdt_ping(struct watchdog_device *dev)
108{
109	int ret;
110	unsigned int func;
111
112	if (machine_is_vm()) {
113		/*
114		 * It seems to be ok to z/VM to use the init function to
115		 * retrigger the watchdog. On LPAR WDT_FUNC_CHANGE must
116		 * be used when the watchdog is running.
117		 */
118		func = conceal_on ? (WDT_FUNC_INIT | WDT_FUNC_CONCEAL)
119			: WDT_FUNC_INIT;
120
121		ret = diag288_str(func, dev->timeout, wdt_cmd);
122		WARN_ON(ret != 0);
123	} else {
124		ret = diag288(WDT_FUNC_CHANGE, dev->timeout, 0, 0);
125	}
126
127	if (ret)
128		pr_err("The watchdog timer cannot be started or reset\n");
129	return ret;
130}
131
132static int wdt_set_timeout(struct watchdog_device * dev, unsigned int new_to)
133{
134	dev->timeout = new_to;
135	return wdt_ping(dev);
136}
137
138static const struct watchdog_ops wdt_ops = {
139	.owner = THIS_MODULE,
140	.start = wdt_start,
141	.stop = wdt_stop,
142	.ping = wdt_ping,
143	.set_timeout = wdt_set_timeout,
144};
145
146static const struct watchdog_info wdt_info = {
147	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
148	.firmware_version = 0,
149	.identity = "z Watchdog",
150};
151
152static struct watchdog_device wdt_dev = {
153	.parent = NULL,
154	.info = &wdt_info,
155	.ops = &wdt_ops,
156	.bootstatus = 0,
157	.timeout = WDT_DEFAULT_TIMEOUT,
158	.min_timeout = MIN_INTERVAL,
159	.max_timeout = MAX_INTERVAL,
160};
161
162static int __init diag288_init(void)
163{
164	watchdog_set_nowayout(&wdt_dev, nowayout_info);
165
166	if (machine_is_vm()) {
167		cmd_buf = kmalloc(MAX_CMDLEN, GFP_KERNEL);
168		if (!cmd_buf) {
169			pr_err("The watchdog cannot be initialized\n");
170			return -ENOMEM;
171		}
172	}
173
174	return watchdog_register_device(&wdt_dev);
175}
176
177static void __exit diag288_exit(void)
178{
179	watchdog_unregister_device(&wdt_dev);
180	kfree(cmd_buf);
181}
182
183module_cpu_feature_match(S390_CPU_FEATURE_D288, diag288_init);
184module_exit(diag288_exit);
~/ Read-only file preview.