brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · c3f0a49 Raw
251 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/bitops.h>8#include <linux/device.h>9#include <linux/io.h>10#include <linux/module.h>11#include <linux/platform_device.h>12#include <linux/watchdog.h>13 14#define LOCK			0xA515#define UNLOCK			0x5A16 17#define WDT_CTRL_RESET_EN	BIT(7)18#define WDT_RELOAD_PORT_EN	BIT(7)19 20#define WDT_CTRL		121#define WDT_RELOAD_CTRL		222#define WDT_PRESET_PRESCALE	423#define WDT_REG_LOCK		524#define WDT_COUNT		625#define WDT_RELOAD_PORT		726 27#define WDT_MIN_TIMEOUT		128#define WDT_MAX_TIMEOUT		46429#define WDT_DEFAULT_TIMEOUT	8030 31#define WDT_MAX_COUNTER		1532 33static unsigned int timeout;34module_param(timeout, uint, 0);35MODULE_PARM_DESC(timeout,36		 "Watchdog timeout in seconds. (default="37		 __MODULE_STRING(WDT_DEFAULT_TIMEOUT) ")");38 39static bool nowayout = WATCHDOG_NOWAYOUT;40module_param(nowayout, bool, 0);41MODULE_PARM_DESC(nowayout,42		 "Watchdog cannot be stopped once started. (default="43		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");44 45struct nic7018_wdt {46	u16 io_base;47	u32 period;48	struct watchdog_device wdd;49};50 51struct nic7018_config {52	u32 period;53	u8 divider;54};55 56static const struct nic7018_config nic7018_configs[] = {57	{  2, 4 },58	{ 32, 5 },59};60 61static inline u32 nic7018_timeout(u32 period, u8 counter)62{63	return period * counter - period / 2;64}65 66static const struct nic7018_config *nic7018_get_config(u32 timeout,67						       u8 *counter)68{69	const struct nic7018_config *config;70	u8 count;71 72	if (timeout < 30 && timeout != 16) {73		config = &nic7018_configs[0];74		count = timeout / 2 + 1;75	} else {76		config = &nic7018_configs[1];77		count = DIV_ROUND_UP(timeout + 16, 32);78 79		if (count > WDT_MAX_COUNTER)80			count = WDT_MAX_COUNTER;81	}82	*counter = count;83	return config;84}85 86static int nic7018_set_timeout(struct watchdog_device *wdd,87			       unsigned int timeout)88{89	struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);90	const struct nic7018_config *config;91	u8 counter;92 93	config = nic7018_get_config(timeout, &counter);94 95	outb(counter << 4 | config->divider,96	     wdt->io_base + WDT_PRESET_PRESCALE);97 98	wdd->timeout = nic7018_timeout(config->period, counter);99	wdt->period = config->period;100 101	return 0;102}103 104static int nic7018_start(struct watchdog_device *wdd)105{106	struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);107	u8 control;108 109	nic7018_set_timeout(wdd, wdd->timeout);110 111	control = inb(wdt->io_base + WDT_RELOAD_CTRL);112	outb(control | WDT_RELOAD_PORT_EN, wdt->io_base + WDT_RELOAD_CTRL);113 114	outb(1, wdt->io_base + WDT_RELOAD_PORT);115 116	control = inb(wdt->io_base + WDT_CTRL);117	outb(control | WDT_CTRL_RESET_EN, wdt->io_base + WDT_CTRL);118 119	return 0;120}121 122static int nic7018_stop(struct watchdog_device *wdd)123{124	struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);125 126	outb(0, wdt->io_base + WDT_CTRL);127	outb(0, wdt->io_base + WDT_RELOAD_CTRL);128	outb(0xF0, wdt->io_base + WDT_PRESET_PRESCALE);129 130	return 0;131}132 133static int nic7018_ping(struct watchdog_device *wdd)134{135	struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);136 137	outb(1, wdt->io_base + WDT_RELOAD_PORT);138 139	return 0;140}141 142static unsigned int nic7018_get_timeleft(struct watchdog_device *wdd)143{144	struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd);145	u8 count;146 147	count = inb(wdt->io_base + WDT_COUNT) & 0xF;148	if (!count)149		return 0;150 151	return nic7018_timeout(wdt->period, count);152}153 154static const struct watchdog_info nic7018_wdd_info = {155	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,156	.identity = "NIC7018 Watchdog",157};158 159static const struct watchdog_ops nic7018_wdd_ops = {160	.owner = THIS_MODULE,161	.start = nic7018_start,162	.stop = nic7018_stop,163	.ping = nic7018_ping,164	.set_timeout = nic7018_set_timeout,165	.get_timeleft = nic7018_get_timeleft,166};167 168static int nic7018_probe(struct platform_device *pdev)169{170	struct device *dev = &pdev->dev;171	struct watchdog_device *wdd;172	struct nic7018_wdt *wdt;173	struct resource *io_rc;174	int ret;175 176	wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);177	if (!wdt)178		return -ENOMEM;179 180	platform_set_drvdata(pdev, wdt);181 182	io_rc = platform_get_resource(pdev, IORESOURCE_IO, 0);183	if (!io_rc) {184		dev_err(dev, "missing IO resources\n");185		return -EINVAL;186	}187 188	if (!devm_request_region(dev, io_rc->start, resource_size(io_rc),189				 KBUILD_MODNAME)) {190		dev_err(dev, "failed to get IO region\n");191		return -EBUSY;192	}193 194	wdt->io_base = io_rc->start;195	wdd = &wdt->wdd;196	wdd->info = &nic7018_wdd_info;197	wdd->ops = &nic7018_wdd_ops;198	wdd->min_timeout = WDT_MIN_TIMEOUT;199	wdd->max_timeout = WDT_MAX_TIMEOUT;200	wdd->timeout = WDT_DEFAULT_TIMEOUT;201	wdd->parent = dev;202 203	watchdog_set_drvdata(wdd, wdt);204	watchdog_set_nowayout(wdd, nowayout);205	watchdog_init_timeout(wdd, timeout, dev);206 207	/* Unlock WDT register */208	outb(UNLOCK, wdt->io_base + WDT_REG_LOCK);209 210	ret = watchdog_register_device(wdd);211	if (ret) {212		outb(LOCK, wdt->io_base + WDT_REG_LOCK);213		return ret;214	}215 216	dev_dbg(dev, "io_base=0x%04X, timeout=%d, nowayout=%d\n",217		wdt->io_base, timeout, nowayout);218	return 0;219}220 221static void nic7018_remove(struct platform_device *pdev)222{223	struct nic7018_wdt *wdt = platform_get_drvdata(pdev);224 225	watchdog_unregister_device(&wdt->wdd);226 227	/* Lock WDT register */228	outb(LOCK, wdt->io_base + WDT_REG_LOCK);229}230 231static const struct acpi_device_id nic7018_device_ids[] = {232	{"NIC7018", 0},233	{"", 0},234};235MODULE_DEVICE_TABLE(acpi, nic7018_device_ids);236 237static struct platform_driver watchdog_driver = {238	.probe = nic7018_probe,239	.remove_new = nic7018_remove,240	.driver = {241		.name = KBUILD_MODNAME,242		.acpi_match_table = ACPI_PTR(nic7018_device_ids),243	},244};245 246module_platform_driver(watchdog_driver);247 248MODULE_DESCRIPTION("National Instruments NIC7018 Watchdog driver");249MODULE_AUTHOR("Hui Chun Ong <hui.chun.ong@ni.com>");250MODULE_LICENSE("GPL");251