brintos

brintos / linux-shallow public Read only

0
0
Text · 6.1 KiB · 045bb72 Raw
256 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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	0x0114#define NIWD_COUNTER2	0x0215#define NIWD_COUNTER1	0x0316#define NIWD_COUNTER0	0x0417#define NIWD_SEED2	0x0518#define NIWD_SEED1	0x0619#define NIWD_SEED0	0x0720 21#define NIWD_IO_SIZE	0x0822 23#define NIWD_CONTROL_MODE		0x8024#define NIWD_CONTROL_PROC_RESET		0x2025#define NIWD_CONTROL_PET		0x1026#define NIWD_CONTROL_RUNNING		0x0827#define NIWD_CONTROL_CAPTURECOUNTER	0x0428#define NIWD_CONTROL_RESET		0x0229#define NIWD_CONTROL_ALARM		0x0130 31#define NIWD_PERIOD_NS		3072032#define NIWD_MIN_TIMEOUT	133#define NIWD_MAX_TIMEOUT	51534#define NIWD_DEFAULT_TIMEOUT	6035 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");256