Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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, NULL);
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, NULL);
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			if (IS_ERR(reg))
355				return PTR_ERR(reg);
356		} else if (resource_type(res) == IORESOURCE_IO) {
357			reg = devm_ioport_map(&pdev->dev, res->start, 1);
358			if (!reg)
359				return -ENOMEM;
360		} else {
361			dev_err(&pdev->dev, "Unsupported resource\n");
362			return -EINVAL;
363		}
364
365		regs[i] = reg;
366	}
367
368	entries = (struct acpi_wdat_entry *)(tbl + 1);
369	for (i = 0; i < tbl->entries; i++) {
370		const struct acpi_generic_address *gas;
371		struct wdat_instruction *instr;
372		struct list_head *instructions;
373		unsigned int action;
374		struct resource r;
375		int j;
376
377		action = entries[i].action;
378		if (action >= MAX_WDAT_ACTIONS) {
379			dev_dbg(&pdev->dev, "Skipping unknown action: %u\n",
380				action);
381			continue;
382		}
383
384		instr = devm_kzalloc(&pdev->dev, sizeof(*instr), GFP_KERNEL);
385		if (!instr)
386			return -ENOMEM;
387
388		INIT_LIST_HEAD(&instr->node);
389		instr->entry = entries[i];
390
391		gas = &entries[i].register_region;
392
393		memset(&r, 0, sizeof(r));
394		r.start = gas->address;
395		r.end = r.start + gas->access_width - 1;
396		if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
397			r.flags = IORESOURCE_MEM;
398		} else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
399			r.flags = IORESOURCE_IO;
400		} else {
401			dev_dbg(&pdev->dev, "Unsupported address space: %d\n",
402				gas->space_id);
403			continue;
404		}
405
406		/* Find the matching resource */
407		for (j = 0; j < pdev->num_resources; j++) {
408			res = &pdev->resource[j];
409			if (resource_contains(res, &r)) {
410				instr->reg = regs[j] + r.start - res->start;
411				break;
412			}
413		}
414
415		if (!instr->reg) {
416			dev_err(&pdev->dev, "I/O resource not found\n");
417			return -EINVAL;
418		}
419
420		instructions = wdat->instructions[action];
421		if (!instructions) {
422			instructions = devm_kzalloc(&pdev->dev,
423					sizeof(*instructions), GFP_KERNEL);
424			if (!instructions)
425				return -ENOMEM;
426
427			INIT_LIST_HEAD(instructions);
428			wdat->instructions[action] = instructions;
429		}
430
431		list_add_tail(&instr->node, instructions);
432	}
433
434	wdat_wdt_boot_status(wdat);
435	wdat_wdt_set_running(wdat);
436
437	ret = wdat_wdt_enable_reboot(wdat);
438	if (ret)
439		return ret;
440
441	platform_set_drvdata(pdev, wdat);
442
443	watchdog_set_nowayout(&wdat->wdd, nowayout);
444	return devm_watchdog_register_device(&pdev->dev, &wdat->wdd);
445}
446
447#ifdef CONFIG_PM_SLEEP
448static int wdat_wdt_suspend_noirq(struct device *dev)
449{
450	struct platform_device *pdev = to_platform_device(dev);
451	struct wdat_wdt *wdat = platform_get_drvdata(pdev);
452	int ret;
453
454	if (!watchdog_active(&wdat->wdd))
455		return 0;
456
457	/*
458	 * We need to stop the watchdog if firmare is not doing it or if we
459	 * are going suspend to idle (where firmware is not involved). If
460	 * firmware is stopping the watchdog we kick it here one more time
461	 * to give it some time.
462	 */
463	wdat->stopped = false;
464	if (acpi_target_system_state() == ACPI_STATE_S0 ||
465	    !wdat->stopped_in_sleep) {
466		ret = wdat_wdt_stop(&wdat->wdd);
467		if (!ret)
468			wdat->stopped = true;
469	} else {
470		ret = wdat_wdt_ping(&wdat->wdd);
471	}
472
473	return ret;
474}
475
476static int wdat_wdt_resume_noirq(struct device *dev)
477{
478	struct platform_device *pdev = to_platform_device(dev);
479	struct wdat_wdt *wdat = platform_get_drvdata(pdev);
480	int ret;
481
482	if (!watchdog_active(&wdat->wdd))
483		return 0;
484
485	if (!wdat->stopped) {
486		/*
487		 * Looks like the boot firmware reinitializes the watchdog
488		 * before it hands off to the OS on resume from sleep so we
489		 * stop and reprogram the watchdog here.
490		 */
491		ret = wdat_wdt_stop(&wdat->wdd);
492		if (ret)
493			return ret;
494
495		ret = wdat_wdt_set_timeout(&wdat->wdd, wdat->wdd.timeout);
496		if (ret)
497			return ret;
498
499		ret = wdat_wdt_enable_reboot(wdat);
500		if (ret)
501			return ret;
502
503		ret = wdat_wdt_ping(&wdat->wdd);
504		if (ret)
505			return ret;
506	}
507
508	return wdat_wdt_start(&wdat->wdd);
509}
510#endif
511
512static const struct dev_pm_ops wdat_wdt_pm_ops = {
513	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(wdat_wdt_suspend_noirq,
514				      wdat_wdt_resume_noirq)
515};
516
517static struct platform_driver wdat_wdt_driver = {
518	.probe = wdat_wdt_probe,
519	.driver = {
520		.name = "wdat_wdt",
521		.pm = &wdat_wdt_pm_ops,
522	},
523};
524
525module_platform_driver(wdat_wdt_driver);
526
527MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
528MODULE_DESCRIPTION("ACPI Hardware Watchdog (WDAT) driver");
529MODULE_LICENSE("GPL v2");
530MODULE_ALIAS("platform:wdat_wdt");