xref: /linux/drivers/watchdog/pseries-wdt.c (revision df822f2e36cf2c2864d166d90cf002df4d8689e5)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2022 International Business Machines, Inc.
4  */
5 
6 #include <linux/bitops.h>
7 #include <linux/kernel.h>
8 #include <linux/limits.h>
9 #include <linux/math.h>
10 #include <linux/module.h>
11 #include <linux/moduleparam.h>
12 #include <linux/platform_device.h>
13 #include <linux/time64.h>
14 #include <linux/watchdog.h>
15 
16 #define DRV_NAME "pseries-wdt"
17 
18 /*
19  * H_WATCHDOG Input
20  *
21  * R4: "flags":
22  *
23  *         Bits 48-55: "operation"
24  */
25 #define PSERIES_WDTF_OP_START	0x100UL		/* start timer */
26 #define PSERIES_WDTF_OP_STOP	0x200UL		/* stop timer */
27 #define PSERIES_WDTF_OP_QUERY	0x300UL		/* query timer capabilities */
28 
29 /*
30  *         Bits 56-63: "timeoutAction" (for "Start Watchdog" only)
31  */
32 #define PSERIES_WDTF_ACTION_HARD_POWEROFF	0x1UL	/* poweroff */
33 #define PSERIES_WDTF_ACTION_HARD_RESTART	0x2UL	/* restart */
34 #define PSERIES_WDTF_ACTION_DUMP_RESTART	0x3UL	/* dump + restart */
35 
36 /*
37  * H_WATCHDOG Output
38  *
39  * R3: Return code
40  *
41  *     H_SUCCESS    The operation completed.
42  *
43  *     H_BUSY	    The hypervisor is too busy; retry the operation.
44  *
45  *     H_PARAMETER  The given "flags" are somehow invalid.  Either the
46  *                  "operation" or "timeoutAction" is invalid, or a
47  *                  reserved bit is set.
48  *
49  *     H_P2         The given "watchdogNumber" is zero or exceeds the
50  *                  supported maximum value.
51  *
52  *     H_P3         The given "timeoutInMs" is below the supported
53  *                  minimum value.
54  *
55  *     H_NOOP       The given "watchdogNumber" is already stopped.
56  *
57  *     H_HARDWARE   The operation failed for ineffable reasons.
58  *
59  *     H_FUNCTION   The H_WATCHDOG hypercall is not supported by this
60  *                  hypervisor.
61  *
62  * R4:
63  *
64  * - For the "Query Watchdog Capabilities" operation, a 64-bit
65  *   structure:
66  */
67 #define PSERIES_WDTQ_MIN_TIMEOUT(cap)	(((cap) >> 48) & 0xffff)
68 #define PSERIES_WDTQ_MAX_NUMBER(cap)	(((cap) >> 32) & 0xffff)
69 
70 static const unsigned long pseries_wdt_action[] = {
71 	[0] = PSERIES_WDTF_ACTION_HARD_POWEROFF,
72 	[1] = PSERIES_WDTF_ACTION_HARD_RESTART,
73 	[2] = PSERIES_WDTF_ACTION_DUMP_RESTART,
74 };
75 
76 #define WATCHDOG_ACTION 1
77 static unsigned int action = WATCHDOG_ACTION;
78 module_param(action, uint, 0444);
79 MODULE_PARM_DESC(action, "Action taken when watchdog expires (default="
80 		 __MODULE_STRING(WATCHDOG_ACTION) ")");
81 
82 static bool nowayout = WATCHDOG_NOWAYOUT;
83 module_param(nowayout, bool, 0444);
84 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
85 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
86 
87 #define WATCHDOG_TIMEOUT 60
88 static unsigned int timeout = WATCHDOG_TIMEOUT;
89 module_param(timeout, uint, 0444);
90 MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds (default="
91 		 __MODULE_STRING(WATCHDOG_TIMEOUT) ")");
92 
93 struct pseries_wdt {
94 	struct watchdog_device wd;
95 	unsigned long action;
96 	unsigned long num;		/* Watchdog numbers are 1-based */
97 };
98 
99 static int pseries_wdt_start(struct watchdog_device *wdd)
100 {
101 	struct pseries_wdt *pw = watchdog_get_drvdata(wdd);
102 	struct device *dev = wdd->parent;
103 	unsigned long flags, msecs;
104 	long rc;
105 
106 	flags = pw->action | PSERIES_WDTF_OP_START;
107 	msecs = wdd->timeout * MSEC_PER_SEC;
108 	rc = plpar_hcall_norets(H_WATCHDOG, flags, pw->num, msecs);
109 	if (rc != H_SUCCESS) {
110 		dev_crit(dev, "H_WATCHDOG: %ld: failed to start timer %lu",
111 			 rc, pw->num);
112 		return -EIO;
113 	}
114 	return 0;
115 }
116 
117 static int pseries_wdt_stop(struct watchdog_device *wdd)
118 {
119 	struct pseries_wdt *pw = watchdog_get_drvdata(wdd);
120 	struct device *dev = wdd->parent;
121 	long rc;
122 
123 	rc = plpar_hcall_norets(H_WATCHDOG, PSERIES_WDTF_OP_STOP, pw->num);
124 	if (rc != H_SUCCESS && rc != H_NOOP) {
125 		dev_crit(dev, "H_WATCHDOG: %ld: failed to stop timer %lu",
126 			 rc, pw->num);
127 		return -EIO;
128 	}
129 	return 0;
130 }
131 
132 static struct watchdog_info pseries_wdt_info = {
133 	.identity = DRV_NAME,
134 	.options = WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT
135 	    | WDIOF_PRETIMEOUT,
136 };
137 
138 static const struct watchdog_ops pseries_wdt_ops = {
139 	.owner = THIS_MODULE,
140 	.start = pseries_wdt_start,
141 	.stop = pseries_wdt_stop,
142 };
143 
144 static int pseries_wdt_probe(struct platform_device *pdev)
145 {
146 	unsigned long ret[PLPAR_HCALL_BUFSIZE] = { 0 };
147 	struct pseries_wdt *pw;
148 	unsigned long cap;
149 	long msecs, rc;
150 	int err;
151 
152 	rc = plpar_hcall(H_WATCHDOG, ret, PSERIES_WDTF_OP_QUERY);
153 	if (rc == H_FUNCTION)
154 		return -ENODEV;
155 	if (rc != H_SUCCESS)
156 		return -EIO;
157 	cap = ret[0];
158 
159 	pw = devm_kzalloc(&pdev->dev, sizeof(*pw), GFP_KERNEL);
160 	if (!pw)
161 		return -ENOMEM;
162 
163 	/*
164 	 * Assume watchdogNumber 1 for now.  If we ever support
165 	 * multiple timers we will need to devise a way to choose a
166 	 * distinct watchdogNumber for each platform device at device
167 	 * registration time.
168 	 */
169 	pw->num = 1;
170 	if (PSERIES_WDTQ_MAX_NUMBER(cap) < pw->num)
171 		return -ENODEV;
172 
173 	if (action >= ARRAY_SIZE(pseries_wdt_action))
174 		return -EINVAL;
175 	pw->action = pseries_wdt_action[action];
176 
177 	pw->wd.parent = &pdev->dev;
178 	pw->wd.info = &pseries_wdt_info;
179 	pw->wd.ops = &pseries_wdt_ops;
180 	msecs = PSERIES_WDTQ_MIN_TIMEOUT(cap);
181 	pw->wd.min_timeout = DIV_ROUND_UP(msecs, MSEC_PER_SEC);
182 	pw->wd.max_timeout = UINT_MAX / 1000;	/* from linux/watchdog.h */
183 	pw->wd.timeout = timeout;
184 	if (watchdog_init_timeout(&pw->wd, 0, NULL))
185 		return -EINVAL;
186 	watchdog_set_nowayout(&pw->wd, nowayout);
187 	watchdog_stop_on_reboot(&pw->wd);
188 	watchdog_stop_on_unregister(&pw->wd);
189 	watchdog_set_drvdata(&pw->wd, pw);
190 
191 	err = devm_watchdog_register_device(&pdev->dev, &pw->wd);
192 	if (err)
193 		return err;
194 
195 	platform_set_drvdata(pdev, &pw->wd);
196 
197 	return 0;
198 }
199 
200 static int pseries_wdt_suspend(struct platform_device *pdev, pm_message_t state)
201 {
202 	struct watchdog_device *wd = platform_get_drvdata(pdev);
203 
204 	if (watchdog_active(wd))
205 		return pseries_wdt_stop(wd);
206 	return 0;
207 }
208 
209 static int pseries_wdt_resume(struct platform_device *pdev)
210 {
211 	struct watchdog_device *wd = platform_get_drvdata(pdev);
212 
213 	if (watchdog_active(wd))
214 		return pseries_wdt_start(wd);
215 	return 0;
216 }
217 
218 static const struct platform_device_id pseries_wdt_id[] = {
219 	{ .name = "pseries-wdt" },
220 	{}
221 };
222 MODULE_DEVICE_TABLE(platform, pseries_wdt_id);
223 
224 static struct platform_driver pseries_wdt_driver = {
225 	.driver = {
226 		.name = DRV_NAME,
227 	},
228 	.id_table = pseries_wdt_id,
229 	.probe = pseries_wdt_probe,
230 	.resume = pseries_wdt_resume,
231 	.suspend = pseries_wdt_suspend,
232 };
233 module_platform_driver(pseries_wdt_driver);
234 
235 MODULE_AUTHOR("Alexey Kardashevskiy");
236 MODULE_AUTHOR("Scott Cheloha");
237 MODULE_DESCRIPTION("POWER Architecture Platform Watchdog Driver");
238 MODULE_LICENSE("GPL");
239