Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2016 National Instruments Corp.
  4 */
  5
  6#include <linux/acpi.h>
  7#include <linux/device.h>
  8#include <linux/interrupt.h>
  9#include <linux/io.h>
 10#include <linux/module.h>
 11#include <linux/watchdog.h>
 12
 13#define NIWD_CONTROL	0x01
 14#define NIWD_COUNTER2	0x02
 15#define NIWD_COUNTER1	0x03
 16#define NIWD_COUNTER0	0x04
 17#define NIWD_SEED2	0x05
 18#define NIWD_SEED1	0x06
 19#define NIWD_SEED0	0x07
 20
 21#define NIWD_IO_SIZE	0x08
 22
 23#define NIWD_CONTROL_MODE		0x80
 24#define NIWD_CONTROL_PROC_RESET		0x20
 25#define NIWD_CONTROL_PET		0x10
 26#define NIWD_CONTROL_RUNNING		0x08
 27#define NIWD_CONTROL_CAPTURECOUNTER	0x04
 28#define NIWD_CONTROL_RESET		0x02
 29#define NIWD_CONTROL_ALARM		0x01
 30
 31#define NIWD_PERIOD_NS		30720
 32#define NIWD_MIN_TIMEOUT	1
 33#define NIWD_MAX_TIMEOUT	515
 34#define NIWD_DEFAULT_TIMEOUT	60
 35
 36#define NIWD_NAME		"ni903x_wdt"
 37
 38struct ni903x_wdt {
 39	struct device *dev;
 40	u16 io_base;
 41	struct watchdog_device wdd;
 42};
 43
 44static unsigned int timeout;
 45module_param(timeout, uint, 0);
 46MODULE_PARM_DESC(timeout,
 47		 "Watchdog timeout in seconds. (default="
 48		 __MODULE_STRING(NIWD_DEFAULT_TIMEOUT) ")");
 49
 50static int nowayout = WATCHDOG_NOWAYOUT;
 51module_param(nowayout, int, S_IRUGO);
 52MODULE_PARM_DESC(nowayout,
 53		 "Watchdog cannot be stopped once started (default="
 54		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 55
 56static void ni903x_start(struct ni903x_wdt *wdt)
 57{
 58	u8 control = inb(wdt->io_base + NIWD_CONTROL);
 59
 60	outb(control | NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
 61	outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
 62}
 63
 64static int ni903x_wdd_set_timeout(struct watchdog_device *wdd,
 65				  unsigned int timeout)
 66{
 67	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
 68	u32 counter = timeout * (1000000000 / NIWD_PERIOD_NS);
 69
 70	outb(((0x00FF0000 & counter) >> 16), wdt->io_base + NIWD_SEED2);
 71	outb(((0x0000FF00 & counter) >> 8), wdt->io_base + NIWD_SEED1);
 72	outb((0x000000FF & counter), wdt->io_base + NIWD_SEED0);
 73
 74	wdd->timeout = timeout;
 75
 76	return 0;
 77}
 78
 79static unsigned int ni903x_wdd_get_timeleft(struct watchdog_device *wdd)
 80{
 81	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
 82	u8 control, counter0, counter1, counter2;
 83	u32 counter;
 84
 85	control = inb(wdt->io_base + NIWD_CONTROL);
 86	control |= NIWD_CONTROL_CAPTURECOUNTER;
 87	outb(control, wdt->io_base + NIWD_CONTROL);
 88
 89	counter2 = inb(wdt->io_base + NIWD_COUNTER2);
 90	counter1 = inb(wdt->io_base + NIWD_COUNTER1);
 91	counter0 = inb(wdt->io_base + NIWD_COUNTER0);
 92
 93	counter = (counter2 << 16) | (counter1 << 8) | counter0;
 94
 95	return counter / (1000000000 / NIWD_PERIOD_NS);
 96}
 97
 98static int ni903x_wdd_ping(struct watchdog_device *wdd)
 99{
100	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
101	u8 control;
102
103	control = inb(wdt->io_base + NIWD_CONTROL);
104	outb(control | NIWD_CONTROL_PET, wdt->io_base + NIWD_CONTROL);
105
106	return 0;
107}
108
109static int ni903x_wdd_start(struct watchdog_device *wdd)
110{
111	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
112
113	outb(NIWD_CONTROL_RESET | NIWD_CONTROL_PROC_RESET,
114	     wdt->io_base + NIWD_CONTROL);
115
116	ni903x_wdd_set_timeout(wdd, wdd->timeout);
117	ni903x_start(wdt);
118
119	return 0;
120}
121
122static int ni903x_wdd_stop(struct watchdog_device *wdd)
123{
124	struct ni903x_wdt *wdt = watchdog_get_drvdata(wdd);
125
126	outb(NIWD_CONTROL_RESET, wdt->io_base + NIWD_CONTROL);
127
128	return 0;
129}
130
131static acpi_status ni903x_resources(struct acpi_resource *res, void *data)
132{
133	struct ni903x_wdt *wdt = data;
134	u16 io_size;
135
136	switch (res->type) {
137	case ACPI_RESOURCE_TYPE_IO:
138		if (wdt->io_base != 0) {
139			dev_err(wdt->dev, "too many IO resources\n");
140			return AE_ERROR;
141		}
142
143		wdt->io_base = res->data.io.minimum;
144		io_size = res->data.io.address_length;
145
146		if (io_size < NIWD_IO_SIZE) {
147			dev_err(wdt->dev, "memory region too small\n");
148			return AE_ERROR;
149		}
150
151		if (!devm_request_region(wdt->dev, wdt->io_base, io_size,
152					 NIWD_NAME)) {
153			dev_err(wdt->dev, "failed to get memory region\n");
154			return AE_ERROR;
155		}
156
157		return AE_OK;
158
159	case ACPI_RESOURCE_TYPE_END_TAG:
160	default:
161		/* Ignore unsupported resources, e.g. IRQ */
162		return AE_OK;
163	}
164}
165
166static const struct watchdog_info ni903x_wdd_info = {
167	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
168	.identity = "NI Watchdog",
169};
170
171static const struct watchdog_ops ni903x_wdd_ops = {
172	.owner = THIS_MODULE,
173	.start = ni903x_wdd_start,
174	.stop = ni903x_wdd_stop,
175	.ping = ni903x_wdd_ping,
176	.set_timeout = ni903x_wdd_set_timeout,
177	.get_timeleft = ni903x_wdd_get_timeleft,
178};
179
180static int ni903x_acpi_add(struct acpi_device *device)
181{
182	struct device *dev = &device->dev;
183	struct watchdog_device *wdd;
184	struct ni903x_wdt *wdt;
185	acpi_status status;
186	int ret;
187
188	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
189	if (!wdt)
190		return -ENOMEM;
191
192	device->driver_data = wdt;
193	wdt->dev = dev;
194
195	status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
196				     ni903x_resources, wdt);
197	if (ACPI_FAILURE(status) || wdt->io_base == 0) {
198		dev_err(dev, "failed to get resources\n");
199		return -ENODEV;
200	}
201
202	wdd = &wdt->wdd;
203	wdd->info = &ni903x_wdd_info;
204	wdd->ops = &ni903x_wdd_ops;
205	wdd->min_timeout = NIWD_MIN_TIMEOUT;
206	wdd->max_timeout = NIWD_MAX_TIMEOUT;
207	wdd->timeout = NIWD_DEFAULT_TIMEOUT;
208	wdd->parent = dev;
209	watchdog_set_drvdata(wdd, wdt);
210	watchdog_set_nowayout(wdd, nowayout);
211	watchdog_init_timeout(wdd, timeout, dev);
212
213	ret = watchdog_register_device(wdd);
214	if (ret)
215		return ret;
216
217	/* Switch from boot mode to user mode */
218	outb(NIWD_CONTROL_RESET | NIWD_CONTROL_MODE,
219	     wdt->io_base + NIWD_CONTROL);
220
221	dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",
222		wdt->io_base, timeout, nowayout);
223
224	return 0;
225}
226
227static void ni903x_acpi_remove(struct acpi_device *device)
228{
229	struct ni903x_wdt *wdt = acpi_driver_data(device);
230
231	ni903x_wdd_stop(&wdt->wdd);
232	watchdog_unregister_device(&wdt->wdd);
233}
234
235static const struct acpi_device_id ni903x_device_ids[] = {
236	{"NIC775C", 0},
237	{"", 0},
238};
239MODULE_DEVICE_TABLE(acpi, ni903x_device_ids);
240
241static struct acpi_driver ni903x_acpi_driver = {
242	.name = NIWD_NAME,
243	.ids = ni903x_device_ids,
244	.ops = {
245		.add = ni903x_acpi_add,
246		.remove = ni903x_acpi_remove,
247	},
248};
249
250module_acpi_driver(ni903x_acpi_driver);
251
252MODULE_DESCRIPTION("NI 903x Watchdog");
253MODULE_AUTHOR("Jeff Westfahl <jeff.westfahl@ni.com>");
254MODULE_AUTHOR("Kyle Roeschley <kyle.roeschley@ni.com>");
255MODULE_LICENSE("GPL");