Linux Audio

Check our new training course

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