Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Watchdog driver for the MEN z069 IP-Core
  4 *
  5 * Copyright (C) 2018 Johannes Thumshirn <jth@kernel.org>
  6 */
  7#include <linux/io.h>
  8#include <linux/kernel.h>
  9#include <linux/mcb.h>
 10#include <linux/module.h>
 11#include <linux/watchdog.h>
 12
 13struct men_z069_drv {
 14	struct watchdog_device wdt;
 15	void __iomem *base;
 16	struct resource *mem;
 17};
 18
 19#define MEN_Z069_WTR			0x10
 20#define MEN_Z069_WTR_WDEN		BIT(15)
 21#define MEN_Z069_WTR_WDET_MASK		0x7fff
 22#define MEN_Z069_WVR			0x14
 23
 24#define MEN_Z069_TIMER_FREQ		500 /* 500 Hz */
 25#define MEN_Z069_WDT_COUNTER_MIN	1
 26#define MEN_Z069_WDT_COUNTER_MAX	0x7fff
 27#define MEN_Z069_DEFAULT_TIMEOUT	30
 28
 29static bool nowayout = WATCHDOG_NOWAYOUT;
 30module_param(nowayout, bool, 0);
 31MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 32			    __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 33
 34static int men_z069_wdt_start(struct watchdog_device *wdt)
 35{
 36	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
 37	u16 val;
 38
 39	val = readw(drv->base + MEN_Z069_WTR);
 40	val |= MEN_Z069_WTR_WDEN;
 41	writew(val, drv->base + MEN_Z069_WTR);
 42
 43	return 0;
 44}
 45
 46static int men_z069_wdt_stop(struct watchdog_device *wdt)
 47{
 48	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
 49	u16 val;
 50
 51	val = readw(drv->base + MEN_Z069_WTR);
 52	val &= ~MEN_Z069_WTR_WDEN;
 53	writew(val, drv->base + MEN_Z069_WTR);
 54
 55	return 0;
 56}
 57
 58static int men_z069_wdt_ping(struct watchdog_device *wdt)
 59{
 60	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
 61	u16 val;
 62
 63	/* The watchdog trigger value toggles between 0x5555 and 0xaaaa */
 64	val = readw(drv->base + MEN_Z069_WVR);
 65	val ^= 0xffff;
 66	writew(val, drv->base + MEN_Z069_WVR);
 67
 68	return 0;
 69}
 70
 71static int men_z069_wdt_set_timeout(struct watchdog_device *wdt,
 72				    unsigned int timeout)
 73{
 74	struct men_z069_drv *drv = watchdog_get_drvdata(wdt);
 75	u16 reg, val, ena;
 76
 77	wdt->timeout = timeout;
 78	val = timeout * MEN_Z069_TIMER_FREQ;
 79
 80	reg = readw(drv->base + MEN_Z069_WVR);
 81	ena = reg & MEN_Z069_WTR_WDEN;
 82	reg = ena | val;
 83	writew(reg, drv->base + MEN_Z069_WTR);
 84
 85	return 0;
 86}
 87
 88static const struct watchdog_info men_z069_info = {
 89	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 90	.identity = "MEN z069 Watchdog",
 91};
 92
 93static const struct watchdog_ops men_z069_ops = {
 94	.owner = THIS_MODULE,
 95	.start = men_z069_wdt_start,
 96	.stop = men_z069_wdt_stop,
 97	.ping = men_z069_wdt_ping,
 98	.set_timeout = men_z069_wdt_set_timeout,
 99};
100
101static struct watchdog_device men_z069_wdt = {
102	.info = &men_z069_info,
103	.ops = &men_z069_ops,
104	.timeout = MEN_Z069_DEFAULT_TIMEOUT,
105	.min_timeout = 1,
106	.max_timeout = MEN_Z069_WDT_COUNTER_MAX / MEN_Z069_TIMER_FREQ,
107};
108
109static int men_z069_probe(struct mcb_device *dev,
110			  const struct mcb_device_id *id)
111{
112	struct men_z069_drv *drv;
113	struct resource *mem;
114
115	drv = devm_kzalloc(&dev->dev, sizeof(struct men_z069_drv), GFP_KERNEL);
116	if (!drv)
117		return -ENOMEM;
118
119	mem = mcb_request_mem(dev, "z069-wdt");
120	if (IS_ERR(mem))
121		return PTR_ERR(mem);
122
123	drv->base = devm_ioremap(&dev->dev, mem->start, resource_size(mem));
124	if (drv->base == NULL)
125		goto release_mem;
126
127	drv->mem = mem;
128
129	drv->wdt = men_z069_wdt;
130	watchdog_init_timeout(&drv->wdt, 0, &dev->dev);
131	watchdog_set_nowayout(&drv->wdt, nowayout);
132	watchdog_set_drvdata(&drv->wdt, drv);
133	drv->wdt.parent = &dev->dev;
134	mcb_set_drvdata(dev, drv);
135
136	return watchdog_register_device(&men_z069_wdt);
137
138release_mem:
139	mcb_release_mem(mem);
140	return -ENOMEM;
141}
142
143static void men_z069_remove(struct mcb_device *dev)
144{
145	struct men_z069_drv *drv = mcb_get_drvdata(dev);
146
147	watchdog_unregister_device(&drv->wdt);
148	mcb_release_mem(drv->mem);
149}
150
151static const struct mcb_device_id men_z069_ids[] = {
152	{ .device = 0x45 },
153	{ }
154};
155MODULE_DEVICE_TABLE(mcb, men_z069_ids);
156
157static struct mcb_driver men_z069_driver = {
158	.driver = {
159		.name = "z069-wdt",
160		.owner = THIS_MODULE,
161	},
162	.probe = men_z069_probe,
163	.remove = men_z069_remove,
164	.id_table = men_z069_ids,
165};
166module_mcb_driver(men_z069_driver);
167
168MODULE_AUTHOR("Johannes Thumshirn <jth@kernel.org>");
169MODULE_LICENSE("GPL v2");
170MODULE_ALIAS("mcb:16z069");
171MODULE_IMPORT_NS(MCB);