summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2016-09-20 08:30:51 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2016-09-23 20:10:04 -0400
commit058dfc7670086edda8d34f0dbe93c596db5d4a6b (patch)
treea7f469b35b797f90a6cdc7c1c9859f62b36f4a14 /drivers/watchdog
parent3be7988674ab33565700a37b210f502563d932e6 (diff)
ACPI / watchdog: Add support for WDAT hardware watchdog
Starting from Intel Skylake the iTCO watchdog timer registers were moved to reside in the same register space with SMBus host controller. Not all needed registers are available though and we need to unhide P2SB (Primary to Sideband) device briefly to be able to read status of required NO_REBOOT bit. The i2c-i801.c SMBus driver used to handle this and creation of the iTCO watchdog platform device. Windows, on the other hand, does not use the iTCO watchdog hardware directly even if it is available. Instead it relies on ACPI Watchdog Action Table (WDAT) table to describe the watchdog hardware to the OS. This table contains necessary information about the the hardware and also set of actions which are executed by a driver as needed. This patch implements a new watchdog driver that takes advantage of the ACPI WDAT table. We split the functionality into two parts: first part enumerates the WDAT table and if found, populates resources and creates platform device for the actual driver. The second part is the driver itself. The reason for the split is that this way we can make the driver itself to be a module and loaded automatically if the WDAT table is found. Otherwise the module is not loaded. Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Reviewed-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/wdat_wdt.c525
3 files changed, 539 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 1bffe006ca9a..50dbaa805658 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -152,6 +152,19 @@ config TANGOX_WATCHDOG
152 152
153 This driver can be built as a module. The module name is tangox_wdt. 153 This driver can be built as a module. The module name is tangox_wdt.
154 154
155config WDAT_WDT
156 tristate "ACPI Watchdog Action Table (WDAT)"
157 depends on ACPI
158 select ACPI_WATCHDOG
159 help
160 This driver adds support for systems with ACPI Watchdog Action
161 Table (WDAT) table. Servers typically have this but it can be
162 found on some desktop machines as well. This driver will take
163 over the native iTCO watchdog driver found on many Intel CPUs.
164
165 To compile this driver as module, choose M here: the module will
166 be called wdat_wdt.
167
155config WM831X_WATCHDOG 168config WM831X_WATCHDOG
156 tristate "WM831x watchdog" 169 tristate "WM831x watchdog"
157 depends on MFD_WM831X 170 depends on MFD_WM831X
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index c22ad3ea3539..cba00430151b 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -202,6 +202,7 @@ obj-$(CONFIG_DA9062_WATCHDOG) += da9062_wdt.o
202obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o 202obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
203obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o 203obj-$(CONFIG_GPIO_WATCHDOG) += gpio_wdt.o
204obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o 204obj-$(CONFIG_TANGOX_WATCHDOG) += tangox_wdt.o
205obj-$(CONFIG_WDAT_WDT) += wdat_wdt.o
205obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o 206obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
206obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o 207obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
207obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o 208obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
diff --git a/drivers/watchdog/wdat_wdt.c b/drivers/watchdog/wdat_wdt.c
new file mode 100644
index 000000000000..6b6464596674
--- /dev/null
+++ b/drivers/watchdog/wdat_wdt.c
@@ -0,0 +1,525 @@
1/*
2 * ACPI Hardware Watchdog (WDAT) driver.
3 *
4 * Copyright (C) 2016, Intel Corporation
5 * Author: Mika Westerberg <mika.westerberg@linux.intel.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/acpi.h>
13#include <linux/ioport.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/pm.h>
17#include <linux/watchdog.h>
18
19#define MAX_WDAT_ACTIONS ACPI_WDAT_ACTION_RESERVED
20
21/**
22 * struct wdat_instruction - Single ACPI WDAT instruction
23 * @entry: Copy of the ACPI table instruction
24 * @reg: Register the instruction is accessing
25 * @node: Next instruction in action sequence
26 */
27struct wdat_instruction {
28 struct acpi_wdat_entry entry;
29 void __iomem *reg;
30 struct list_head node;
31};
32
33/**
34 * struct wdat_wdt - ACPI WDAT watchdog device
35 * @pdev: Parent platform device
36 * @wdd: Watchdog core device
37 * @period: How long is one watchdog period in ms
38 * @stopped_in_sleep: Is this watchdog stopped by the firmware in S1-S5
39 * @stopped: Was the watchdog stopped by the driver in suspend
40 * @actions: An array of instruction lists indexed by an action number from
41 * the WDAT table. There can be %NULL entries for not implemented
42 * actions.
43 */
44struct wdat_wdt {
45 struct platform_device *pdev;
46 struct watchdog_device wdd;
47 unsigned int period;
48 bool stopped_in_sleep;
49 bool stopped;
50 struct list_head *instructions[MAX_WDAT_ACTIONS];
51};
52
53#define to_wdat_wdt(wdd) container_of(wdd, struct wdat_wdt, wdd)
54
55static bool nowayout = WATCHDOG_NOWAYOUT;
56module_param(nowayout, bool, 0);
57MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
58 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
59
60static int wdat_wdt_read(struct wdat_wdt *wdat,
61 const struct wdat_instruction *instr, u32 *value)
62{
63 const struct acpi_generic_address *gas = &instr->entry.register_region;
64
65 switch (gas->access_width) {
66 case 1:
67 *value = ioread8(instr->reg);
68 break;
69 case 2:
70 *value = ioread16(instr->reg);
71 break;
72 case 3:
73 *value = ioread32(instr->reg);
74 break;
75 default:
76 return -EINVAL;
77 }
78
79 dev_dbg(&wdat->pdev->dev, "Read %#x from 0x%08llx\n", *value,
80 gas->address);
81
82 return 0;
83}
84
85static int wdat_wdt_write(struct wdat_wdt *wdat,
86 const struct wdat_instruction *instr, u32 value)
87{
88 const struct acpi_generic_address *gas = &instr->entry.register_region;
89
90 switch (gas->access_width) {
91 case 1:
92 iowrite8((u8)value, instr->reg);
93 break;
94 case 2:
95 iowrite16((u16)value, instr->reg);
96 break;
97 case 3:
98 iowrite32(value, instr->reg);
99 break;
100 default:
101 return -EINVAL;
102 }
103
104 dev_dbg(&wdat->pdev->dev, "Wrote %#x to 0x%08llx\n", value,
105 gas->address);
106
107 return 0;
108}
109
110static int wdat_wdt_run_action(struct wdat_wdt *wdat, unsigned int action,
111 u32 param, u32 *retval)
112{
113 struct wdat_instruction *instr;
114
115 if (action >= ARRAY_SIZE(wdat->instructions))
116 return -EINVAL;
117
118 if (!wdat->instructions[action])
119 return -EOPNOTSUPP;
120
121 dev_dbg(&wdat->pdev->dev, "Running action %#x\n", action);
122
123 /* Run each instruction sequentially */
124 list_for_each_entry(instr, wdat->instructions[action], node) {
125 const struct acpi_wdat_entry *entry = &instr->entry;
126 const struct acpi_generic_address *gas;
127 u32 flags, value, mask, x, y;
128 bool preserve;
129 int ret;
130
131 gas = &entry->register_region;
132
133 preserve = entry->instruction & ACPI_WDAT_PRESERVE_REGISTER;
134 flags = entry->instruction & ~ACPI_WDAT_PRESERVE_REGISTER;
135 value = entry->value;
136 mask = entry->mask;
137
138 switch (flags) {
139 case ACPI_WDAT_READ_VALUE:
140 ret = wdat_wdt_read(wdat, instr, &x);
141 if (ret)
142 return ret;
143 x >>= gas->bit_offset;
144 x &= mask;
145 if (retval)
146 *retval = x == value;
147 break;
148
149 case ACPI_WDAT_READ_COUNTDOWN:
150 ret = wdat_wdt_read(wdat, instr, &x);
151 if (ret)
152 return ret;
153 x >>= gas->bit_offset;
154 x &= mask;
155 if (retval)
156 *retval = x;
157 break;
158
159 case ACPI_WDAT_WRITE_VALUE:
160 x = value & mask;
161 x <<= gas->bit_offset;
162 if (preserve) {
163 ret = wdat_wdt_read(wdat, instr, &y);
164 if (ret)
165 return ret;
166 y = y & ~(mask << gas->bit_offset);
167 x |= y;
168 }
169 ret = wdat_wdt_write(wdat, instr, x);
170 if (ret)
171 return ret;
172 break;
173
174 case ACPI_WDAT_WRITE_COUNTDOWN:
175 x = param;
176 x &= mask;
177 x <<= gas->bit_offset;
178 if (preserve) {
179 ret = wdat_wdt_read(wdat, instr, &y);
180 if (ret)
181 return ret;
182 y = y & ~(mask << gas->bit_offset);
183 x |= y;
184 }
185 ret = wdat_wdt_write(wdat, instr, x);
186 if (ret)
187 return ret;
188 break;
189
190 default:
191 dev_err(&wdat->pdev->dev, "Unknown instruction: %u\n",
192 flags);
193 return -EINVAL;
194 }
195 }
196
197 return 0;
198}
199
200static int wdat_wdt_enable_reboot(struct wdat_wdt *wdat)
201{
202 int ret;
203
204 /*
205 * WDAT specification says that the watchdog is required to reboot
206 * the system when it fires. However, it also states that it is
207 * recommeded to make it configurable through hardware register. We
208 * enable reboot now if it is configrable, just in case.
209 */
210 ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_REBOOT, 0, 0);
211 if (ret && ret != -EOPNOTSUPP) {
212 dev_err(&wdat->pdev->dev,
213 "Failed to enable reboot when watchdog triggers\n");
214 return ret;
215 }
216
217 return 0;
218}
219
220static void wdat_wdt_boot_status(struct wdat_wdt *wdat)
221{
222 u32 boot_status = 0;
223 int ret;
224
225 ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_STATUS, 0, &boot_status);
226 if (ret && ret != -EOPNOTSUPP) {
227 dev_err(&wdat->pdev->dev, "Failed to read boot status\n");
228 return;
229 }
230
231 if (boot_status)
232 wdat->wdd.bootstatus = WDIOF_CARDRESET;
233
234 /* Clear the boot status in case BIOS did not do it */
235 ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_STATUS, 0, 0);
236 if (ret && ret != -EOPNOTSUPP)
237 dev_err(&wdat->pdev->dev, "Failed to clear boot status\n");
238}
239
240static void wdat_wdt_set_running(struct wdat_wdt *wdat)
241{
242 u32 running = 0;
243 int ret;
244
245 ret = wdat_wdt_run_action(wdat, ACPI_WDAT_GET_RUNNING_STATE, 0,
246 &running);
247 if (ret && ret != -EOPNOTSUPP)
248 dev_err(&wdat->pdev->dev, "Failed to read running state\n");
249
250 if (running)
251 set_bit(WDOG_HW_RUNNING, &wdat->wdd.status);
252}
253
254static int wdat_wdt_start(struct watchdog_device *wdd)
255{
256 return wdat_wdt_run_action(to_wdat_wdt(wdd),
257 ACPI_WDAT_SET_RUNNING_STATE, 0, NULL);
258}
259
260static int wdat_wdt_stop(struct watchdog_device *wdd)
261{
262 return wdat_wdt_run_action(to_wdat_wdt(wdd),
263 ACPI_WDAT_SET_STOPPED_STATE, 0, NULL);
264}
265
266static int wdat_wdt_ping(struct watchdog_device *wdd)
267{
268 return wdat_wdt_run_action(to_wdat_wdt(wdd), ACPI_WDAT_RESET, 0, NULL);
269}
270
271static int wdat_wdt_set_timeout(struct watchdog_device *wdd,
272 unsigned int timeout)
273{
274 struct wdat_wdt *wdat = to_wdat_wdt(wdd);
275 unsigned int periods;
276 int ret;
277
278 periods = timeout * 1000 / wdat->period;
279 ret = wdat_wdt_run_action(wdat, ACPI_WDAT_SET_COUNTDOWN, periods, NULL);
280 if (!ret)
281 wdd->timeout = timeout;
282 return ret;
283}
284
285static unsigned int wdat_wdt_get_timeleft(struct watchdog_device *wdd)
286{
287 struct wdat_wdt *wdat = to_wdat_wdt(wdd);
288 u32 periods = 0;
289
290 wdat_wdt_run_action(wdat, ACPI_WDAT_GET_COUNTDOWN, 0, &periods);
291 return periods * wdat->period / 1000;
292}
293
294static const struct watchdog_info wdat_wdt_info = {
295 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
296 .firmware_version = 0,
297 .identity = "wdat_wdt",
298};
299
300static const struct watchdog_ops wdat_wdt_ops = {
301 .owner = THIS_MODULE,
302 .start = wdat_wdt_start,
303 .stop = wdat_wdt_stop,
304 .ping = wdat_wdt_ping,
305 .set_timeout = wdat_wdt_set_timeout,
306 .get_timeleft = wdat_wdt_get_timeleft,
307};
308
309static int wdat_wdt_probe(struct platform_device *pdev)
310{
311 const struct acpi_wdat_entry *entries;
312 const struct acpi_table_wdat *tbl;
313 struct wdat_wdt *wdat;
314 struct resource *res;
315 void __iomem **regs;
316 acpi_status status;
317 int i, ret;
318
319 status = acpi_get_table(ACPI_SIG_WDAT, 0,
320 (struct acpi_table_header **)&tbl);
321 if (ACPI_FAILURE(status))
322 return -ENODEV;
323
324 wdat = devm_kzalloc(&pdev->dev, sizeof(*wdat), GFP_KERNEL);
325 if (!wdat)
326 return -ENOMEM;
327
328 regs = devm_kcalloc(&pdev->dev, pdev->num_resources, sizeof(*regs),
329 GFP_KERNEL);
330 if (!regs)
331 return -ENOMEM;
332
333 /* WDAT specification wants to have >= 1ms period */
334 if (tbl->timer_period < 1)
335 return -EINVAL;
336 if (tbl->min_count > tbl->max_count)
337 return -EINVAL;
338
339 wdat->period = tbl->timer_period;
340 wdat->wdd.min_hw_heartbeat_ms = wdat->period * tbl->min_count;
341 wdat->wdd.max_hw_heartbeat_ms = wdat->period * tbl->max_count;
342 wdat->stopped_in_sleep = tbl->flags & ACPI_WDAT_STOPPED;
343 wdat->wdd.info = &wdat_wdt_info;
344 wdat->wdd.ops = &wdat_wdt_ops;
345 wdat->pdev = pdev;
346
347 /* Request and map all resources */
348 for (i = 0; i < pdev->num_resources; i++) {
349 void __iomem *reg;
350
351 res = &pdev->resource[i];
352 if (resource_type(res) == IORESOURCE_MEM) {
353 reg = devm_ioremap_resource(&pdev->dev, res);
354 } else if (resource_type(res) == IORESOURCE_IO) {
355 reg = devm_ioport_map(&pdev->dev, res->start, 1);
356 } else {
357 dev_err(&pdev->dev, "Unsupported resource\n");
358 return -EINVAL;
359 }
360
361 if (!reg)
362 return -ENOMEM;
363
364 regs[i] = reg;
365 }
366
367 entries = (struct acpi_wdat_entry *)(tbl + 1);
368 for (i = 0; i < tbl->entries; i++) {
369 const struct acpi_generic_address *gas;
370 struct wdat_instruction *instr;
371 struct list_head *instructions;
372 unsigned int action;
373 struct resource r;
374 int j;
375
376 action = entries[i].action;
377 if (action >= MAX_WDAT_ACTIONS) {
378 dev_dbg(&pdev->dev, "Skipping unknown action: %u\n",
379 action);
380 continue;
381 }
382
383 instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL);
384 if (!instr)
385 return -ENOMEM;
386
387 INIT_LIST_HEAD(&instr->node);
388 instr->entry = entries[i];
389
390 gas = &entries[i].register_region;
391
392 memset(&r, 0, sizeof(r));
393 r.start = gas->address;
394 r.end = r.start + gas->access_width;
395 if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
396 r.flags = IORESOURCE_MEM;
397 } else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
398 r.flags = IORESOURCE_IO;
399 } else {
400 dev_dbg(&pdev->dev, "Unsupported address space: %d\n",
401 gas->space_id);
402 continue;
403 }
404
405 /* Find the matching resource */
406 for (j = 0; j < pdev->num_resources; j++) {
407 res = &pdev->resource[j];
408 if (resource_contains(res, &r)) {
409 instr->reg = regs[j] + r.start - res->start;
410 break;
411 }
412 }
413
414 if (!instr->reg) {
415 dev_err(&pdev->dev, "I/O resource not found\n");
416 return -EINVAL;
417 }
418
419 instructions = wdat->instructions[action];
420 if (!instructions) {
421 instructions = devm_kzalloc(&pdev->dev,
422 sizeof(*instructions), GFP_KERNEL);
423 if (!instructions)
424 return -ENOMEM;
425
426 INIT_LIST_HEAD(instructions);
427 wdat->instructions[action] = instructions;
428 }
429
430 list_add_tail(&instr->node, instructions);
431 }
432
433 wdat_wdt_boot_status(wdat);
434 wdat_wdt_set_running(wdat);
435
436 ret = wdat_wdt_enable_reboot(wdat);
437 if (ret)
438 return ret;
439
440 platform_set_drvdata(pdev, wdat);
441
442 watchdog_set_nowayout(&wdat->wdd, nowayout);
443 return devm_watchdog_register_device(&pdev->dev, &wdat->wdd);
444}
445
446#ifdef CONFIG_PM_SLEEP
447static int wdat_wdt_suspend_noirq(struct device *dev)
448{
449 struct platform_device *pdev = to_platform_device(dev);
450 struct wdat_wdt *wdat = platform_get_drvdata(pdev);
451 int ret;
452
453 if (!watchdog_active(&wdat->wdd))
454 return 0;
455
456 /*
457 * We need to stop the watchdog if firmare is not doing it or if we
458 * are going suspend to idle (where firmware is not involved). If
459 * firmware is stopping the watchdog we kick it here one more time
460 * to give it some time.
461 */
462 wdat->stopped = false;
463 if (acpi_target_system_state() == ACPI_STATE_S0 ||
464 !wdat->stopped_in_sleep) {
465 ret = wdat_wdt_stop(&wdat->wdd);
466 if (!ret)
467 wdat->stopped = true;
468 } else {
469 ret = wdat_wdt_ping(&wdat->wdd);
470 }
471
472 return ret;
473}
474
475static int wdat_wdt_resume_noirq(struct device *dev)
476{
477 struct platform_device *pdev = to_platform_device(dev);
478 struct wdat_wdt *wdat = platform_get_drvdata(pdev);
479 int ret;
480
481 if (!watchdog_active(&wdat->wdd))
482 return 0;
483
484 if (!wdat->stopped) {
485 /*
486 * Looks like the boot firmware reinitializes the watchdog
487 * before it hands off to the OS on resume from sleep so we
488 * stop and reprogram the watchdog here.
489 */
490 ret = wdat_wdt_stop(&wdat->wdd);
491 if (ret)
492 return ret;
493
494 ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout);
495 if (ret)
496 return ret;
497
498 ret = wdat_wdt_enable_reboot(wdat);
499 if (ret)
500 return ret;
501 }
502
503 return wdat_wdt_start(&wdat->wdd);
504}
505#endif
506
507static const struct dev_pm_ops wdat_wdt_pm_ops = {
508 SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq,
509 wdat_wdt_resume_noirq)
510};
511
512static struct platform_driver wdat_wdt_driver = {
513 .probe = wdat_wdt_probe,
514 .driver = {
515 .name = "wdat_wdt",
516 .pm = &wdat_wdt_pm_ops,
517 },
518};
519
520module_platform_driver(wdat_wdt_driver);
521
522MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
523MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver");
524MODULE_LICENSE("GPL v2");
525MODULE_ALIAS("platform:wdat_wdt");