Linux Audio

Check our new training course

Loading...
v6.13.7
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Watchdog driver for Technologic Systems TS-72xx based SBCs
  4 * (TS-7200, TS-7250 and TS-7260). These boards have external
  5 * glue logic CPLD chip, which includes programmable watchdog
  6 * timer.
  7 *
  8 * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
  9 *
 10 * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
 11 *
 
 
 
 12 */
 13
 14#include <linux/platform_device.h>
 15#include <linux/mod_devicetable.h>
 16#include <linux/module.h>
 
 
 
 
 
 17#include <linux/watchdog.h>
 18#include <linux/io.h>
 19
 20#define TS72XX_WDT_DEFAULT_TIMEOUT	30
 
 21
 22static int timeout;
 23module_param(timeout, int, 0);
 24MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds.");
 
 
 
 25
 26static bool nowayout = WATCHDOG_NOWAYOUT;
 27module_param(nowayout, bool, 0);
 28MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
 29
 30/* priv->control_reg */
 31#define TS72XX_WDT_CTRL_DISABLE		0x00
 32#define TS72XX_WDT_CTRL_250MS		0x01
 33#define TS72XX_WDT_CTRL_500MS		0x02
 34#define TS72XX_WDT_CTRL_1SEC		0x03
 35#define TS72XX_WDT_CTRL_RESERVED	0x04
 36#define TS72XX_WDT_CTRL_2SEC		0x05
 37#define TS72XX_WDT_CTRL_4SEC		0x06
 38#define TS72XX_WDT_CTRL_8SEC		0x07
 39
 40/* priv->feed_reg */
 41#define TS72XX_WDT_FEED_VAL		0x05
 
 
 
 
 42
 43struct ts72xx_wdt_priv {
 44	void __iomem	*control_reg;
 45	void __iomem	*feed_reg;
 46	struct watchdog_device wdd;
 47	unsigned char regval;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 48};
 49
 50static int ts72xx_wdt_start(struct watchdog_device *wdd)
 
 
 
 
 
 
 
 
 51{
 52	struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
 53
 54	writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
 55	writeb(priv->regval, priv->control_reg);
 56
 57	return 0;
 
 
 
 
 
 58}
 59
 60static int ts72xx_wdt_stop(struct watchdog_device *wdd)
 
 
 
 
 
 
 
 61{
 62	struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
 63
 64	writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
 65	writeb(TS72XX_WDT_CTRL_DISABLE, priv->control_reg);
 
 
 66
 67	return 0;
 68}
 69
 70static int ts72xx_wdt_ping(struct watchdog_device *wdd)
 
 
 
 
 
 
 71{
 72	struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
 73
 74	writeb(TS72XX_WDT_FEED_VAL, priv->feed_reg);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 75
 76	return 0;
 
 
 
 
 
 
 
 
 
 77}
 78
 79static int ts72xx_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
 80{
 81	struct ts72xx_wdt_priv *priv = watchdog_get_drvdata(wdd);
 
 82
 83	if (to == 1) {
 84		priv->regval = TS72XX_WDT_CTRL_1SEC;
 85	} else if (to == 2) {
 86		priv->regval = TS72XX_WDT_CTRL_2SEC;
 87	} else if (to <= 4) {
 88		priv->regval = TS72XX_WDT_CTRL_4SEC;
 89		to = 4;
 90	} else {
 91		priv->regval = TS72XX_WDT_CTRL_8SEC;
 92		if (to <= 8)
 93			to = 8;
 94	}
 95
 96	wdd->timeout = to;
 
 97
 98	if (watchdog_active(wdd)) {
 99		ts72xx_wdt_stop(wdd);
100		ts72xx_wdt_start(wdd);
101	}
102
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
103	return 0;
104}
105
106static const struct watchdog_info ts72xx_wdt_ident = {
107	.options		= WDIOF_KEEPALIVEPING |
108				  WDIOF_SETTIMEOUT |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109				  WDIOF_MAGICCLOSE,
110	.firmware_version	= 1,
111	.identity		= "TS-72XX WDT",
112};
113
114static const struct watchdog_ops ts72xx_wdt_ops = {
115	.owner		= THIS_MODULE,
116	.start		= ts72xx_wdt_start,
117	.stop		= ts72xx_wdt_stop,
118	.ping		= ts72xx_wdt_ping,
119	.set_timeout	= ts72xx_wdt_settimeout,
120};
121
122static int ts72xx_wdt_probe(struct platform_device *pdev)
123{
124	struct device *dev = &pdev->dev;
125	struct ts72xx_wdt_priv *priv;
126	struct watchdog_device *wdd;
127	int ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
129	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
130	if (!priv)
131		return -ENOMEM;
132
133	priv->control_reg = devm_platform_ioremap_resource(pdev, 0);
134	if (IS_ERR(priv->control_reg))
135		return PTR_ERR(priv->control_reg);
 
 
 
 
 
 
 
 
 
 
 
 
 
136
137	priv->feed_reg = devm_platform_ioremap_resource(pdev, 1);
138	if (IS_ERR(priv->feed_reg))
139		return PTR_ERR(priv->feed_reg);
140
141	wdd = &priv->wdd;
142	wdd->info = &ts72xx_wdt_ident;
143	wdd->ops = &ts72xx_wdt_ops;
144	wdd->min_timeout = 1;
145	wdd->max_hw_heartbeat_ms = 8000;
146	wdd->parent = dev;
 
 
147
148	watchdog_set_nowayout(wdd, nowayout);
 
 
149
150	wdd->timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
151	watchdog_init_timeout(wdd, timeout, dev);
 
 
 
 
 
 
152
153	watchdog_set_drvdata(wdd, priv);
 
 
 
 
154
155	ret = devm_watchdog_register_device(dev, wdd);
156	if (ret)
157		return ret;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
159	dev_info(dev, "TS-72xx Watchdog driver\n");
160
161	return 0;
162}
163
164static const struct of_device_id ts72xx_wdt_of_ids[] = {
165	{ .compatible = "technologic,ts7200-wdt" },
166	{ /* sentinel */ }
167};
168MODULE_DEVICE_TABLE(of, ts72xx_wdt_of_ids);
169
170static struct platform_driver ts72xx_wdt_driver = {
171	.probe		= ts72xx_wdt_probe,
 
172	.driver		= {
173		.name	= "ts72xx-wdt",
174		.of_match_table = ts72xx_wdt_of_ids,
175	},
176};
177
178module_platform_driver(ts72xx_wdt_driver);
179
180MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
181MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
182MODULE_LICENSE("GPL");
183MODULE_ALIAS("platform:ts72xx-wdt");
v4.6
 
  1/*
  2 * Watchdog driver for Technologic Systems TS-72xx based SBCs
  3 * (TS-7200, TS-7250 and TS-7260). These boards have external
  4 * glue logic CPLD chip, which includes programmable watchdog
  5 * timer.
  6 *
  7 * Copyright (c) 2009 Mika Westerberg <mika.westerberg@iki.fi>
  8 *
  9 * This driver is based on ep93xx_wdt and wm831x_wdt drivers.
 10 *
 11 * This file is licensed under the terms of the GNU General Public
 12 * License version 2. This program is licensed "as is" without any
 13 * warranty of any kind, whether express or implied.
 14 */
 15
 16#include <linux/fs.h>
 17#include <linux/io.h>
 18#include <linux/module.h>
 19#include <linux/moduleparam.h>
 20#include <linux/miscdevice.h>
 21#include <linux/mutex.h>
 22#include <linux/platform_device.h>
 23#include <linux/slab.h>
 24#include <linux/watchdog.h>
 25#include <linux/uaccess.h>
 26
 27#define TS72XX_WDT_FEED_VAL		0x05
 28#define TS72XX_WDT_DEFAULT_TIMEOUT	8
 29
 30static int timeout = TS72XX_WDT_DEFAULT_TIMEOUT;
 31module_param(timeout, int, 0);
 32MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds. "
 33			  "(1 <= timeout <= 8, default="
 34			  __MODULE_STRING(TS72XX_WDT_DEFAULT_TIMEOUT)
 35			  ")");
 36
 37static bool nowayout = WATCHDOG_NOWAYOUT;
 38module_param(nowayout, bool, 0);
 39MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
 40
 41/**
 42 * struct ts72xx_wdt - watchdog control structure
 43 * @lock: lock that protects this structure
 44 * @regval: watchdog timeout value suitable for control register
 45 * @flags: flags controlling watchdog device state
 46 * @control_reg: watchdog control register
 47 * @feed_reg: watchdog feed register
 48 * @pdev: back pointer to platform dev
 49 */
 50struct ts72xx_wdt {
 51	struct mutex	lock;
 52	int		regval;
 53
 54#define TS72XX_WDT_BUSY_FLAG		1
 55#define TS72XX_WDT_EXPECT_CLOSE_FLAG	2
 56	int		flags;
 57
 
 58	void __iomem	*control_reg;
 59	void __iomem	*feed_reg;
 60
 61	struct platform_device *pdev;
 62};
 63
 64static struct platform_device *ts72xx_wdt_pdev;
 65
 66/*
 67 * TS-72xx Watchdog supports following timeouts (value written
 68 * to control register):
 69 *	value	description
 70 *	-------------------------
 71 *	0x00	watchdog disabled
 72 *	0x01	250ms
 73 *	0x02	500ms
 74 *	0x03	1s
 75 *	0x04	reserved
 76 *	0x05	2s
 77 *	0x06	4s
 78 *	0x07	8s
 79 *
 80 * Timeouts below 1s are not very usable so we don't
 81 * allow them at all.
 82 *
 83 * We provide two functions that convert between these:
 84 * timeout_to_regval() and regval_to_timeout().
 85 */
 86static const struct {
 87	int	timeout;
 88	int	regval;
 89} ts72xx_wdt_map[] = {
 90	{ 1, 3 },
 91	{ 2, 5 },
 92	{ 4, 6 },
 93	{ 8, 7 },
 94};
 95
 96/**
 97 * timeout_to_regval() - converts given timeout to control register value
 98 * @new_timeout: timeout in seconds to be converted
 99 *
100 * Function converts given @new_timeout into valid value that can
101 * be programmed into watchdog control register. When conversion is
102 * not possible, function returns %-EINVAL.
103 */
104static int timeout_to_regval(int new_timeout)
105{
106	int i;
107
108	/* first limit it to 1 - 8 seconds */
109	new_timeout = clamp_val(new_timeout, 1, 8);
110
111	for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
112		if (ts72xx_wdt_map[i].timeout >= new_timeout)
113			return ts72xx_wdt_map[i].regval;
114	}
115
116	return -EINVAL;
117}
118
119/**
120 * regval_to_timeout() - converts control register value to timeout
121 * @regval: control register value to be converted
122 *
123 * Function converts given @regval to timeout in seconds (1, 2, 4 or 8).
124 * If @regval cannot be converted, function returns %-EINVAL.
125 */
126static int regval_to_timeout(int regval)
127{
128	int i;
129
130	for (i = 0; i < ARRAY_SIZE(ts72xx_wdt_map); i++) {
131		if (ts72xx_wdt_map[i].regval == regval)
132			return ts72xx_wdt_map[i].timeout;
133	}
134
135	return -EINVAL;
136}
137
138/**
139 * ts72xx_wdt_kick() - kick the watchdog
140 * @wdt: watchdog to be kicked
141 *
142 * Called with @wdt->lock held.
143 */
144static inline void ts72xx_wdt_kick(struct ts72xx_wdt *wdt)
145{
146	__raw_writeb(TS72XX_WDT_FEED_VAL, wdt->feed_reg);
147}
148
149/**
150 * ts72xx_wdt_start() - starts the watchdog timer
151 * @wdt: watchdog to be started
152 *
153 * This function programs timeout to watchdog timer
154 * and starts it.
155 *
156 * Called with @wdt->lock held.
157 */
158static void ts72xx_wdt_start(struct ts72xx_wdt *wdt)
159{
160	/*
161	 * To program the wdt, it first must be "fed" and
162	 * only after that (within 30 usecs) the configuration
163	 * can be changed.
164	 */
165	ts72xx_wdt_kick(wdt);
166	__raw_writeb((u8)wdt->regval, wdt->control_reg);
167}
168
169/**
170 * ts72xx_wdt_stop() - stops the watchdog timer
171 * @wdt: watchdog to be stopped
172 *
173 * Called with @wdt->lock held.
174 */
175static void ts72xx_wdt_stop(struct ts72xx_wdt *wdt)
176{
177	ts72xx_wdt_kick(wdt);
178	__raw_writeb(0, wdt->control_reg);
179}
180
181static int ts72xx_wdt_open(struct inode *inode, struct file *file)
182{
183	struct ts72xx_wdt *wdt = platform_get_drvdata(ts72xx_wdt_pdev);
184	int regval;
185
186	/*
187	 * Try to convert default timeout to valid register
188	 * value first.
189	 */
190	regval = timeout_to_regval(timeout);
191	if (regval < 0) {
192		dev_err(&wdt->pdev->dev,
193			"failed to convert timeout (%d) to register value\n",
194			timeout);
195		return regval;
 
196	}
197
198	if (mutex_lock_interruptible(&wdt->lock))
199		return -ERESTARTSYS;
200
201	if ((wdt->flags & TS72XX_WDT_BUSY_FLAG) != 0) {
202		mutex_unlock(&wdt->lock);
203		return -EBUSY;
204	}
205
206	wdt->flags = TS72XX_WDT_BUSY_FLAG;
207	wdt->regval = regval;
208	file->private_data = wdt;
209
210	ts72xx_wdt_start(wdt);
211
212	mutex_unlock(&wdt->lock);
213	return nonseekable_open(inode, file);
214}
215
216static int ts72xx_wdt_release(struct inode *inode, struct file *file)
217{
218	struct ts72xx_wdt *wdt = file->private_data;
219
220	if (mutex_lock_interruptible(&wdt->lock))
221		return -ERESTARTSYS;
222
223	if ((wdt->flags & TS72XX_WDT_EXPECT_CLOSE_FLAG) != 0) {
224		ts72xx_wdt_stop(wdt);
225	} else {
226		dev_warn(&wdt->pdev->dev,
227			 "TS-72XX WDT device closed unexpectly. "
228			 "Watchdog timer will not stop!\n");
229		/*
230		 * Kick it one more time, to give userland some time
231		 * to recover (for example, respawning the kicker
232		 * daemon).
233		 */
234		ts72xx_wdt_kick(wdt);
235	}
236
237	wdt->flags = 0;
238
239	mutex_unlock(&wdt->lock);
240	return 0;
241}
242
243static ssize_t ts72xx_wdt_write(struct file *file,
244				const char __user *data,
245				size_t len,
246				loff_t *ppos)
247{
248	struct ts72xx_wdt *wdt = file->private_data;
249
250	if (!len)
251		return 0;
252
253	if (mutex_lock_interruptible(&wdt->lock))
254		return -ERESTARTSYS;
255
256	ts72xx_wdt_kick(wdt);
257
258	/*
259	 * Support for magic character closing. User process
260	 * writes 'V' into the device, just before it is closed.
261	 * This means that we know that the wdt timer can be
262	 * stopped after user closes the device.
263	 */
264	if (!nowayout) {
265		int i;
266
267		for (i = 0; i < len; i++) {
268			char c;
269
270			/* In case it was set long ago */
271			wdt->flags &= ~TS72XX_WDT_EXPECT_CLOSE_FLAG;
272
273			if (get_user(c, data + i)) {
274				mutex_unlock(&wdt->lock);
275				return -EFAULT;
276			}
277			if (c == 'V') {
278				wdt->flags |= TS72XX_WDT_EXPECT_CLOSE_FLAG;
279				break;
280			}
281		}
282	}
283
284	mutex_unlock(&wdt->lock);
285	return len;
286}
287
288static const struct watchdog_info winfo = {
289	.options		= WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
290				  WDIOF_MAGICCLOSE,
291	.firmware_version	= 1,
292	.identity		= "TS-72XX WDT",
293};
294
295static long ts72xx_wdt_ioctl(struct file *file, unsigned int cmd,
296			     unsigned long arg)
 
 
 
 
 
 
 
297{
298	struct ts72xx_wdt *wdt = file->private_data;
299	void __user *argp = (void __user *)arg;
300	int __user *p = (int __user *)argp;
301	int error = 0;
302
303	if (mutex_lock_interruptible(&wdt->lock))
304		return -ERESTARTSYS;
305
306	switch (cmd) {
307	case WDIOC_GETSUPPORT:
308		if (copy_to_user(argp, &winfo, sizeof(winfo)))
309			error = -EFAULT;
310		break;
311
312	case WDIOC_GETSTATUS:
313	case WDIOC_GETBOOTSTATUS:
314		error = put_user(0, p);
315		break;
316
317	case WDIOC_KEEPALIVE:
318		ts72xx_wdt_kick(wdt);
319		break;
320
321	case WDIOC_SETOPTIONS: {
322		int options;
323
324		error = get_user(options, p);
325		if (error)
326			break;
327
328		error = -EINVAL;
329
330		if ((options & WDIOS_DISABLECARD) != 0) {
331			ts72xx_wdt_stop(wdt);
332			error = 0;
333		}
334		if ((options & WDIOS_ENABLECARD) != 0) {
335			ts72xx_wdt_start(wdt);
336			error = 0;
337		}
338
339		break;
340	}
 
341
342	case WDIOC_SETTIMEOUT: {
343		int new_timeout;
344		int regval;
345
346		error = get_user(new_timeout, p);
347		if (error)
348			break;
349
350		regval = timeout_to_regval(new_timeout);
351		if (regval < 0) {
352			error = regval;
353			break;
354		}
355		ts72xx_wdt_stop(wdt);
356		wdt->regval = regval;
357		ts72xx_wdt_start(wdt);
358
359		/*FALLTHROUGH*/
360	}
 
361
362	case WDIOC_GETTIMEOUT:
363		error = put_user(regval_to_timeout(wdt->regval), p);
364		break;
365
366	default:
367		error = -ENOTTY;
368		break;
369	}
370
371	mutex_unlock(&wdt->lock);
372	return error;
373}
374
375static const struct file_operations ts72xx_wdt_fops = {
376	.owner		= THIS_MODULE,
377	.llseek		= no_llseek,
378	.open		= ts72xx_wdt_open,
379	.release	= ts72xx_wdt_release,
380	.write		= ts72xx_wdt_write,
381	.unlocked_ioctl	= ts72xx_wdt_ioctl,
382};
383
384static struct miscdevice ts72xx_wdt_miscdev = {
385	.minor		= WATCHDOG_MINOR,
386	.name		= "watchdog",
387	.fops		= &ts72xx_wdt_fops,
388};
389
390static int ts72xx_wdt_probe(struct platform_device *pdev)
391{
392	struct ts72xx_wdt *wdt;
393	struct resource *r1, *r2;
394	int error = 0;
395
396	wdt = devm_kzalloc(&pdev->dev, sizeof(struct ts72xx_wdt), GFP_KERNEL);
397	if (!wdt)
398		return -ENOMEM;
399
400	r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
401	wdt->control_reg = devm_ioremap_resource(&pdev->dev, r1);
402	if (IS_ERR(wdt->control_reg))
403		return PTR_ERR(wdt->control_reg);
404
405	r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
406	wdt->feed_reg = devm_ioremap_resource(&pdev->dev, r2);
407	if (IS_ERR(wdt->feed_reg))
408		return PTR_ERR(wdt->feed_reg);
409
410	platform_set_drvdata(pdev, wdt);
411	ts72xx_wdt_pdev = pdev;
412	wdt->pdev = pdev;
413	mutex_init(&wdt->lock);
414
415	/* make sure that the watchdog is disabled */
416	ts72xx_wdt_stop(wdt);
417
418	error = misc_register(&ts72xx_wdt_miscdev);
419	if (error) {
420		dev_err(&pdev->dev, "failed to register miscdev\n");
421		return error;
422	}
423
424	dev_info(&pdev->dev, "TS-72xx Watchdog driver\n");
425
426	return 0;
427}
428
429static int ts72xx_wdt_remove(struct platform_device *pdev)
430{
431	misc_deregister(&ts72xx_wdt_miscdev);
432	return 0;
433}
434
435static struct platform_driver ts72xx_wdt_driver = {
436	.probe		= ts72xx_wdt_probe,
437	.remove		= ts72xx_wdt_remove,
438	.driver		= {
439		.name	= "ts72xx-wdt",
 
440	},
441};
442
443module_platform_driver(ts72xx_wdt_driver);
444
445MODULE_AUTHOR("Mika Westerberg <mika.westerberg@iki.fi>");
446MODULE_DESCRIPTION("TS-72xx SBC Watchdog");
447MODULE_LICENSE("GPL");
448MODULE_ALIAS("platform:ts72xx-wdt");