aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTomas Winkler <tomas.winkler@intel.com>2016-01-07 17:49:22 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-02-07 01:11:06 -0500
commit222818c3d84c1f3190767f5f09f2b9b9a0e0ca7f (patch)
tree71812f9021fe350bbec52494a07306410a96e8a2
parentfdd9b8655933c3eb3154fe1ed351c17b654258bd (diff)
watchdog: mei_wdt: implement MEI iAMT watchdog driver
Create a driver with the generic watchdog interface for the MEI iAMT watchdog device. Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--Documentation/misc-devices/mei/mei.txt12
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/watchdog/Kconfig15
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/mei_wdt.c404
5 files changed, 427 insertions, 6 deletions
diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt
index 91c1fa34f48b..2b80a0cd621f 100644
--- a/Documentation/misc-devices/mei/mei.txt
+++ b/Documentation/misc-devices/mei/mei.txt
@@ -231,15 +231,15 @@ IT knows when a platform crashes even when there is a hard failure on the host.
231The Intel AMT Watchdog is composed of two parts: 231The Intel AMT Watchdog is composed of two parts:
232 1) Firmware feature - receives the heartbeats 232 1) Firmware feature - receives the heartbeats
233 and sends an event when the heartbeats stop. 233 and sends an event when the heartbeats stop.
234 2) Intel MEI driver - connects to the watchdog feature, configures the 234 2) Intel MEI iAMT watchdog driver - connects to the watchdog feature,
235 watchdog and sends the heartbeats. 235 configures the watchdog and sends the heartbeats.
236 236
237The Intel MEI driver uses the kernel watchdog API to configure the Intel AMT 237The Intel iAMT watchdog MEI driver uses the kernel watchdog API to configure
238Watchdog and to send heartbeats to it. The default timeout of the 238the Intel AMT Watchdog and to send heartbeats to it. The default timeout of the
239watchdog is 120 seconds. 239watchdog is 120 seconds.
240 240
241If the Intel AMT Watchdog feature does not exist (i.e. the connection failed), 241If the Intel AMT is not enabled in the firmware then the watchdog client won't enumerate
242the Intel MEI driver will disable the sending of heartbeats. 242on the me client bus and watchdog devices won't be exposed.
243 243
244 244
245Supported Chipsets 245Supported Chipsets
diff --git a/MAINTAINERS b/MAINTAINERS
index 30aca4aa5467..d63b3c773c7d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5751,6 +5751,7 @@ S: Supported
5751F: include/uapi/linux/mei.h 5751F: include/uapi/linux/mei.h
5752F: include/linux/mei_cl_bus.h 5752F: include/linux/mei_cl_bus.h
5753F: drivers/misc/mei/* 5753F: drivers/misc/mei/*
5754F: drivers/watchdog/mei_wdt.c
5754F: Documentation/misc-devices/mei/* 5755F: Documentation/misc-devices/mei/*
5755 5756
5756INTEL MIC DRIVERS (mic) 5757INTEL MIC DRIVERS (mic)
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 4f0e7be0da34..57f872122b16 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -1212,6 +1212,21 @@ config SBC_EPX_C3_WATCHDOG
1212 To compile this driver as a module, choose M here: the 1212 To compile this driver as a module, choose M here: the
1213 module will be called sbc_epx_c3. 1213 module will be called sbc_epx_c3.
1214 1214
1215config INTEL_MEI_WDT
1216 tristate "Intel MEI iAMT Watchdog"
1217 depends on INTEL_MEI && X86
1218 select WATCHDOG_CORE
1219 ---help---
1220 A device driver for the Intel MEI iAMT watchdog.
1221
1222 The Intel AMT Watchdog is an OS Health (Hang/Crash) watchdog.
1223 Whenever the OS hangs or crashes, iAMT will send an event
1224 to any subscriber to this event. The watchdog doesn't reset the
1225 the platform.
1226
1227 To compile this driver as a module, choose M here:
1228 the module will be called mei_wdt.
1229
1215# M32R Architecture 1230# M32R Architecture
1216 1231
1217# M68K Architecture 1232# M68K Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index f566753256ab..efc4f788e0f2 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -126,6 +126,7 @@ obj-$(CONFIG_MACHZ_WDT) += machzwd.o
126obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o 126obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
127obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o 127obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o
128obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o 128obj-$(CONFIG_INTEL_MID_WATCHDOG) += intel-mid_wdt.o
129obj-$(CONFIG_INTEL_MEI_WDT) += mei_wdt.o
129 130
130# M32R Architecture 131# M32R Architecture
131 132
diff --git a/drivers/watchdog/mei_wdt.c b/drivers/watchdog/mei_wdt.c
new file mode 100644
index 000000000000..32e3e1d55ef3
--- /dev/null
+++ b/drivers/watchdog/mei_wdt.c
@@ -0,0 +1,404 @@
1/*
2 * Intel Management Engine Interface (Intel MEI) Linux driver
3 * Copyright (c) 2015, Intel Corporation.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 */
14
15#include <linux/module.h>
16#include <linux/slab.h>
17#include <linux/interrupt.h>
18#include <linux/watchdog.h>
19
20#include <linux/uuid.h>
21#include <linux/mei_cl_bus.h>
22
23/*
24 * iAMT Watchdog Device
25 */
26#define INTEL_AMT_WATCHDOG_ID "iamt_wdt"
27
28#define MEI_WDT_DEFAULT_TIMEOUT 120 /* seconds */
29#define MEI_WDT_MIN_TIMEOUT 120 /* seconds */
30#define MEI_WDT_MAX_TIMEOUT 65535 /* seconds */
31
32/* Commands */
33#define MEI_MANAGEMENT_CONTROL 0x02
34
35/* MEI Management Control version number */
36#define MEI_MC_VERSION_NUMBER 0x10
37
38/* Sub Commands */
39#define MEI_MC_START_WD_TIMER_REQ 0x13
40#define MEI_MC_STOP_WD_TIMER_REQ 0x14
41
42/**
43 * enum mei_wdt_state - internal watchdog state
44 *
45 * @MEI_WDT_IDLE: wd is idle and not opened
46 * @MEI_WDT_START: wd was opened, start was called
47 * @MEI_WDT_RUNNING: wd is expecting keep alive pings
48 * @MEI_WDT_STOPPING: wd is stopping and will move to IDLE
49 */
50enum mei_wdt_state {
51 MEI_WDT_IDLE,
52 MEI_WDT_START,
53 MEI_WDT_RUNNING,
54 MEI_WDT_STOPPING,
55};
56
57/**
58 * struct mei_wdt - mei watchdog driver
59 * @wdd: watchdog device
60 *
61 * @cldev: mei watchdog client device
62 * @state: watchdog internal state
63 * @timeout: watchdog current timeout
64 */
65struct mei_wdt {
66 struct watchdog_device wdd;
67
68 struct mei_cl_device *cldev;
69 enum mei_wdt_state state;
70 u16 timeout;
71};
72
73/*
74 * struct mei_mc_hdr - Management Control Command Header
75 *
76 * @command: Management Control (0x2)
77 * @bytecount: Number of bytes in the message beyond this byte
78 * @subcommand: Management Control Subcommand
79 * @versionnumber: Management Control Version (0x10)
80 */
81struct mei_mc_hdr {
82 u8 command;
83 u8 bytecount;
84 u8 subcommand;
85 u8 versionnumber;
86};
87
88/**
89 * struct mei_wdt_start_request watchdog start/ping
90 *
91 * @hdr: Management Control Command Header
92 * @timeout: timeout value
93 * @reserved: reserved (legacy)
94 */
95struct mei_wdt_start_request {
96 struct mei_mc_hdr hdr;
97 u16 timeout;
98 u8 reserved[17];
99} __packed;
100
101/**
102 * struct mei_wdt_stop_request - watchdog stop
103 *
104 * @hdr: Management Control Command Header
105 */
106struct mei_wdt_stop_request {
107 struct mei_mc_hdr hdr;
108} __packed;
109
110/**
111 * mei_wdt_ping - send wd start/ping command
112 *
113 * @wdt: mei watchdog device
114 *
115 * Return: 0 on success,
116 * negative errno code on failure
117 */
118static int mei_wdt_ping(struct mei_wdt *wdt)
119{
120 struct mei_wdt_start_request req;
121 const size_t req_len = sizeof(req);
122 int ret;
123
124 memset(&req, 0, req_len);
125 req.hdr.command = MEI_MANAGEMENT_CONTROL;
126 req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand);
127 req.hdr.subcommand = MEI_MC_START_WD_TIMER_REQ;
128 req.hdr.versionnumber = MEI_MC_VERSION_NUMBER;
129 req.timeout = wdt->timeout;
130
131 ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len);
132 if (ret < 0)
133 return ret;
134
135 return 0;
136}
137
138/**
139 * mei_wdt_stop - send wd stop command
140 *
141 * @wdt: mei watchdog device
142 *
143 * Return: 0 on success,
144 * negative errno code on failure
145 */
146static int mei_wdt_stop(struct mei_wdt *wdt)
147{
148 struct mei_wdt_stop_request req;
149 const size_t req_len = sizeof(req);
150 int ret;
151
152 memset(&req, 0, req_len);
153 req.hdr.command = MEI_MANAGEMENT_CONTROL;
154 req.hdr.bytecount = req_len - offsetof(struct mei_mc_hdr, subcommand);
155 req.hdr.subcommand = MEI_MC_STOP_WD_TIMER_REQ;
156 req.hdr.versionnumber = MEI_MC_VERSION_NUMBER;
157
158 ret = mei_cldev_send(wdt->cldev, (u8 *)&req, req_len);
159 if (ret < 0)
160 return ret;
161
162 return 0;
163}
164
165/**
166 * mei_wdt_ops_start - wd start command from the watchdog core.
167 *
168 * @wdd: watchdog device
169 *
170 * Return: 0 on success or -ENODEV;
171 */
172static int mei_wdt_ops_start(struct watchdog_device *wdd)
173{
174 struct mei_wdt *wdt = watchdog_get_drvdata(wdd);
175
176 wdt->state = MEI_WDT_START;
177 wdd->timeout = wdt->timeout;
178 return 0;
179}
180
181/**
182 * mei_wdt_ops_stop - wd stop command from the watchdog core.
183 *
184 * @wdd: watchdog device
185 *
186 * Return: 0 if success, negative errno code for failure
187 */
188static int mei_wdt_ops_stop(struct watchdog_device *wdd)
189{
190 struct mei_wdt *wdt = watchdog_get_drvdata(wdd);
191 int ret;
192
193 if (wdt->state != MEI_WDT_RUNNING)
194 return 0;
195
196 wdt->state = MEI_WDT_STOPPING;
197
198 ret = mei_wdt_stop(wdt);
199 if (ret)
200 return ret;
201
202 wdt->state = MEI_WDT_IDLE;
203
204 return 0;
205}
206
207/**
208 * mei_wdt_ops_ping - wd ping command from the watchdog core.
209 *
210 * @wdd: watchdog device
211 *
212 * Return: 0 if success, negative errno code on failure
213 */
214static int mei_wdt_ops_ping(struct watchdog_device *wdd)
215{
216 struct mei_wdt *wdt = watchdog_get_drvdata(wdd);
217 int ret;
218
219 if (wdt->state != MEI_WDT_START && wdt->state != MEI_WDT_RUNNING)
220 return 0;
221
222 ret = mei_wdt_ping(wdt);
223 if (ret)
224 return ret;
225
226 wdt->state = MEI_WDT_RUNNING;
227
228 return 0;
229}
230
231/**
232 * mei_wdt_ops_set_timeout - wd set timeout command from the watchdog core.
233 *
234 * @wdd: watchdog device
235 * @timeout: timeout value to set
236 *
237 * Return: 0 if success, negative errno code for failure
238 */
239static int mei_wdt_ops_set_timeout(struct watchdog_device *wdd,
240 unsigned int timeout)
241{
242
243 struct mei_wdt *wdt = watchdog_get_drvdata(wdd);
244
245 /* valid value is already checked by the caller */
246 wdt->timeout = timeout;
247 wdd->timeout = timeout;
248
249 return 0;
250}
251
252static const struct watchdog_ops wd_ops = {
253 .owner = THIS_MODULE,
254 .start = mei_wdt_ops_start,
255 .stop = mei_wdt_ops_stop,
256 .ping = mei_wdt_ops_ping,
257 .set_timeout = mei_wdt_ops_set_timeout,
258};
259
260/* not const as the firmware_version field need to be retrieved */
261static struct watchdog_info wd_info = {
262 .identity = INTEL_AMT_WATCHDOG_ID,
263 .options = WDIOF_KEEPALIVEPING |
264 WDIOF_SETTIMEOUT |
265 WDIOF_ALARMONLY,
266};
267
268/**
269 * mei_wdt_unregister - unregister from the watchdog subsystem
270 *
271 * @wdt: mei watchdog device
272 */
273static void mei_wdt_unregister(struct mei_wdt *wdt)
274{
275 watchdog_unregister_device(&wdt->wdd);
276 watchdog_set_drvdata(&wdt->wdd, NULL);
277}
278
279/**
280 * mei_wdt_register - register with the watchdog subsystem
281 *
282 * @wdt: mei watchdog device
283 *
284 * Return: 0 if success, negative errno code for failure
285 */
286static int mei_wdt_register(struct mei_wdt *wdt)
287{
288 struct device *dev;
289 int ret;
290
291 if (!wdt || !wdt->cldev)
292 return -EINVAL;
293
294 dev = &wdt->cldev->dev;
295
296 wdt->wdd.info = &wd_info;
297 wdt->wdd.ops = &wd_ops;
298 wdt->wdd.parent = dev;
299 wdt->wdd.timeout = MEI_WDT_DEFAULT_TIMEOUT;
300 wdt->wdd.min_timeout = MEI_WDT_MIN_TIMEOUT;
301 wdt->wdd.max_timeout = MEI_WDT_MAX_TIMEOUT;
302
303 watchdog_set_drvdata(&wdt->wdd, wdt);
304 ret = watchdog_register_device(&wdt->wdd);
305 if (ret) {
306 dev_err(dev, "unable to register watchdog device = %d.\n", ret);
307 watchdog_set_drvdata(&wdt->wdd, NULL);
308 }
309
310 return ret;
311}
312
313static int mei_wdt_probe(struct mei_cl_device *cldev,
314 const struct mei_cl_device_id *id)
315{
316 struct mei_wdt *wdt;
317 int ret;
318
319 wdt = kzalloc(sizeof(struct mei_wdt), GFP_KERNEL);
320 if (!wdt)
321 return -ENOMEM;
322
323 wdt->timeout = MEI_WDT_DEFAULT_TIMEOUT;
324 wdt->state = MEI_WDT_IDLE;
325 wdt->cldev = cldev;
326 mei_cldev_set_drvdata(cldev, wdt);
327
328 ret = mei_cldev_enable(cldev);
329 if (ret < 0) {
330 dev_err(&cldev->dev, "Could not enable cl device\n");
331 goto err_out;
332 }
333
334 wd_info.firmware_version = mei_cldev_ver(cldev);
335
336 ret = mei_wdt_register(wdt);
337 if (ret)
338 goto err_disable;
339
340 return 0;
341
342err_disable:
343 mei_cldev_disable(cldev);
344
345err_out:
346 kfree(wdt);
347
348 return ret;
349}
350
351static int mei_wdt_remove(struct mei_cl_device *cldev)
352{
353 struct mei_wdt *wdt = mei_cldev_get_drvdata(cldev);
354
355 mei_wdt_unregister(wdt);
356
357 mei_cldev_disable(cldev);
358
359 kfree(wdt);
360
361 return 0;
362}
363
364#define MEI_UUID_WD UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, \
365 0x89, 0x9D, 0xA9, 0x15, 0x14, 0xCB, 0x32, 0xAB)
366
367static struct mei_cl_device_id mei_wdt_tbl[] = {
368 { .uuid = MEI_UUID_WD, .version = 0x1},
369 /* required last entry */
370 { }
371};
372MODULE_DEVICE_TABLE(mei, mei_wdt_tbl);
373
374static struct mei_cl_driver mei_wdt_driver = {
375 .id_table = mei_wdt_tbl,
376 .name = KBUILD_MODNAME,
377
378 .probe = mei_wdt_probe,
379 .remove = mei_wdt_remove,
380};
381
382static int __init mei_wdt_init(void)
383{
384 int ret;
385
386 ret = mei_cldev_driver_register(&mei_wdt_driver);
387 if (ret) {
388 pr_err(KBUILD_MODNAME ": module registration failed\n");
389 return ret;
390 }
391 return 0;
392}
393
394static void __exit mei_wdt_exit(void)
395{
396 mei_cldev_driver_unregister(&mei_wdt_driver);
397}
398
399module_init(mei_wdt_init);
400module_exit(mei_wdt_exit);
401
402MODULE_AUTHOR("Intel Corporation");
403MODULE_LICENSE("GPL");
404MODULE_DESCRIPTION("Device driver for Intel MEI iAMT watchdog");