1 /*
2  *	Watchdog driver for the mpcore watchdog timer
3  *
4  *	(c) Copyright 2004 ARM Limited
5  *
6  *	Based on the SoftDog driver:
7  *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
8  *						All Rights Reserved.
9  *
10  *	This program is free software; you can redistribute it and/or
11  *	modify it under the terms of the GNU General Public License
12  *	as published by the Free Software Foundation; either version
13  *	2 of the License, or (at your option) any later version.
14  *
15  *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
16  *	warranty for any of this software. This material is provided
17  *	"AS-IS" and at no charge.
18  *
19  *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
20  *
21  */
22 #include <linux/module.h>
23 #include <linux/moduleparam.h>
24 #include <linux/types.h>
25 #include <linux/miscdevice.h>
26 #include <linux/watchdog.h>
27 #include <linux/fs.h>
28 #include <linux/reboot.h>
29 #include <linux/init.h>
30 #include <linux/interrupt.h>
31 #include <linux/platform_device.h>
32 #include <linux/uaccess.h>
33 #include <linux/slab.h>
34 #include <linux/io.h>
35 
36 #include <asm/smp_twd.h>
37 
38 struct mpcore_wdt {
39 	unsigned long	timer_alive;
40 	struct device	*dev;
41 	void __iomem	*base;
42 	int		irq;
43 	unsigned int	perturb;
44 	char		expect_close;
45 };
46 
47 static struct platform_device *mpcore_wdt_dev;
48 static DEFINE_SPINLOCK(wdt_lock);
49 
50 #define TIMER_MARGIN	60
51 static int mpcore_margin = TIMER_MARGIN;
52 module_param(mpcore_margin, int, 0);
53 MODULE_PARM_DESC(mpcore_margin,
54 	"MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
55 				__MODULE_STRING(TIMER_MARGIN) ")");
56 
57 static int nowayout = WATCHDOG_NOWAYOUT;
58 module_param(nowayout, int, 0);
59 MODULE_PARM_DESC(nowayout,
60 	"Watchdog cannot be stopped once started (default="
61 				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
62 
63 #define ONLY_TESTING	0
64 static int mpcore_noboot = ONLY_TESTING;
65 module_param(mpcore_noboot, int, 0);
66 MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
67 	"set to 1 to ignore reboots, 0 to reboot (default="
68 					__MODULE_STRING(ONLY_TESTING) ")");
69 
70 /*
71  *	This is the interrupt handler.  Note that we only use this
72  *	in testing mode, so don't actually do a reboot here.
73  */
mpcore_wdt_fire(int irq,void * arg)74 static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
75 {
76 	struct mpcore_wdt *wdt = arg;
77 
78 	/* Check it really was our interrupt */
79 	if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
80 		dev_printk(KERN_CRIT, wdt->dev,
81 					"Triggered - Reboot ignored.\n");
82 		/* Clear the interrupt on the watchdog */
83 		writel(1, wdt->base + TWD_WDOG_INTSTAT);
84 		return IRQ_HANDLED;
85 	}
86 	return IRQ_NONE;
87 }
88 
89 /*
90  *	mpcore_wdt_keepalive - reload the timer
91  *
92  *	Note that the spec says a DIFFERENT value must be written to the reload
93  *	register each time.  The "perturb" variable deals with this by adding 1
94  *	to the count every other time the function is called.
95  */
mpcore_wdt_keepalive(struct mpcore_wdt * wdt)96 static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
97 {
98 	unsigned long count;
99 
100 	spin_lock(&wdt_lock);
101 	/* Assume prescale is set to 256 */
102 	count =  __raw_readl(wdt->base + TWD_WDOG_COUNTER);
103 	count = (0xFFFFFFFFU - count) * (HZ / 5);
104 	count = (count / 256) * mpcore_margin;
105 
106 	/* Reload the counter */
107 	writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
108 	wdt->perturb = wdt->perturb ? 0 : 1;
109 	spin_unlock(&wdt_lock);
110 }
111 
mpcore_wdt_stop(struct mpcore_wdt * wdt)112 static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
113 {
114 	spin_lock(&wdt_lock);
115 	writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
116 	writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
117 	writel(0x0, wdt->base + TWD_WDOG_CONTROL);
118 	spin_unlock(&wdt_lock);
119 }
120 
mpcore_wdt_start(struct mpcore_wdt * wdt)121 static void mpcore_wdt_start(struct mpcore_wdt *wdt)
122 {
123 	dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
124 
125 	/* This loads the count register but does NOT start the count yet */
126 	mpcore_wdt_keepalive(wdt);
127 
128 	if (mpcore_noboot) {
129 		/* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
130 		writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
131 	} else {
132 		/* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
133 		writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
134 	}
135 }
136 
mpcore_wdt_set_heartbeat(int t)137 static int mpcore_wdt_set_heartbeat(int t)
138 {
139 	if (t < 0x0001 || t > 0xFFFF)
140 		return -EINVAL;
141 
142 	mpcore_margin = t;
143 	return 0;
144 }
145 
146 /*
147  *	/dev/watchdog handling
148  */
mpcore_wdt_open(struct inode * inode,struct file * file)149 static int mpcore_wdt_open(struct inode *inode, struct file *file)
150 {
151 	struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_dev);
152 
153 	if (test_and_set_bit(0, &wdt->timer_alive))
154 		return -EBUSY;
155 
156 	if (nowayout)
157 		__module_get(THIS_MODULE);
158 
159 	file->private_data = wdt;
160 
161 	/*
162 	 *	Activate timer
163 	 */
164 	mpcore_wdt_start(wdt);
165 
166 	return nonseekable_open(inode, file);
167 }
168 
mpcore_wdt_release(struct inode * inode,struct file * file)169 static int mpcore_wdt_release(struct inode *inode, struct file *file)
170 {
171 	struct mpcore_wdt *wdt = file->private_data;
172 
173 	/*
174 	 *	Shut off the timer.
175 	 *	Lock it in if it's a module and we set nowayout
176 	 */
177 	if (wdt->expect_close == 42)
178 		mpcore_wdt_stop(wdt);
179 	else {
180 		dev_printk(KERN_CRIT, wdt->dev,
181 				"unexpected close, not stopping watchdog!\n");
182 		mpcore_wdt_keepalive(wdt);
183 	}
184 	clear_bit(0, &wdt->timer_alive);
185 	wdt->expect_close = 0;
186 	return 0;
187 }
188 
mpcore_wdt_write(struct file * file,const char * data,size_t len,loff_t * ppos)189 static ssize_t mpcore_wdt_write(struct file *file, const char *data,
190 						size_t len, loff_t *ppos)
191 {
192 	struct mpcore_wdt *wdt = file->private_data;
193 
194 	/*
195 	 *	Refresh the timer.
196 	 */
197 	if (len) {
198 		if (!nowayout) {
199 			size_t i;
200 
201 			/* In case it was set long ago */
202 			wdt->expect_close = 0;
203 
204 			for (i = 0; i != len; i++) {
205 				char c;
206 
207 				if (get_user(c, data + i))
208 					return -EFAULT;
209 				if (c == 'V')
210 					wdt->expect_close = 42;
211 			}
212 		}
213 		mpcore_wdt_keepalive(wdt);
214 	}
215 	return len;
216 }
217 
218 static const struct watchdog_info ident = {
219 	.options		= WDIOF_SETTIMEOUT |
220 				  WDIOF_KEEPALIVEPING |
221 				  WDIOF_MAGICCLOSE,
222 	.identity		= "MPcore Watchdog",
223 };
224 
mpcore_wdt_ioctl(struct file * file,unsigned int cmd,unsigned long arg)225 static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
226 							unsigned long arg)
227 {
228 	struct mpcore_wdt *wdt = file->private_data;
229 	int ret;
230 	union {
231 		struct watchdog_info ident;
232 		int i;
233 	} uarg;
234 
235 	if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
236 		return -ENOTTY;
237 
238 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
239 		ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
240 		if (ret)
241 			return -EFAULT;
242 	}
243 
244 	switch (cmd) {
245 	case WDIOC_GETSUPPORT:
246 		uarg.ident = ident;
247 		ret = 0;
248 		break;
249 
250 	case WDIOC_GETSTATUS:
251 	case WDIOC_GETBOOTSTATUS:
252 		uarg.i = 0;
253 		ret = 0;
254 		break;
255 
256 	case WDIOC_SETOPTIONS:
257 		ret = -EINVAL;
258 		if (uarg.i & WDIOS_DISABLECARD) {
259 			mpcore_wdt_stop(wdt);
260 			ret = 0;
261 		}
262 		if (uarg.i & WDIOS_ENABLECARD) {
263 			mpcore_wdt_start(wdt);
264 			ret = 0;
265 		}
266 		break;
267 
268 	case WDIOC_KEEPALIVE:
269 		mpcore_wdt_keepalive(wdt);
270 		ret = 0;
271 		break;
272 
273 	case WDIOC_SETTIMEOUT:
274 		ret = mpcore_wdt_set_heartbeat(uarg.i);
275 		if (ret)
276 			break;
277 
278 		mpcore_wdt_keepalive(wdt);
279 		/* Fall */
280 	case WDIOC_GETTIMEOUT:
281 		uarg.i = mpcore_margin;
282 		ret = 0;
283 		break;
284 
285 	default:
286 		return -ENOTTY;
287 	}
288 
289 	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
290 		ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
291 		if (ret)
292 			ret = -EFAULT;
293 	}
294 	return ret;
295 }
296 
297 /*
298  *	System shutdown handler.  Turn off the watchdog if we're
299  *	restarting or halting the system.
300  */
mpcore_wdt_shutdown(struct platform_device * dev)301 static void mpcore_wdt_shutdown(struct platform_device *dev)
302 {
303 	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
304 
305 	if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
306 		mpcore_wdt_stop(wdt);
307 }
308 
309 /*
310  *	Kernel Interfaces
311  */
312 static const struct file_operations mpcore_wdt_fops = {
313 	.owner		= THIS_MODULE,
314 	.llseek		= no_llseek,
315 	.write		= mpcore_wdt_write,
316 	.unlocked_ioctl	= mpcore_wdt_ioctl,
317 	.open		= mpcore_wdt_open,
318 	.release	= mpcore_wdt_release,
319 };
320 
321 static struct miscdevice mpcore_wdt_miscdev = {
322 	.minor		= WATCHDOG_MINOR,
323 	.name		= "watchdog",
324 	.fops		= &mpcore_wdt_fops,
325 };
326 
mpcore_wdt_probe(struct platform_device * dev)327 static int __devinit mpcore_wdt_probe(struct platform_device *dev)
328 {
329 	struct mpcore_wdt *wdt;
330 	struct resource *res;
331 	int ret;
332 
333 	/* We only accept one device, and it must have an id of -1 */
334 	if (dev->id != -1)
335 		return -ENODEV;
336 
337 	res = platform_get_resource(dev, IORESOURCE_MEM, 0);
338 	if (!res) {
339 		ret = -ENODEV;
340 		goto err_out;
341 	}
342 
343 	wdt = kzalloc(sizeof(struct mpcore_wdt), GFP_KERNEL);
344 	if (!wdt) {
345 		ret = -ENOMEM;
346 		goto err_out;
347 	}
348 
349 	wdt->dev = &dev->dev;
350 	wdt->irq = platform_get_irq(dev, 0);
351 	if (wdt->irq < 0) {
352 		ret = -ENXIO;
353 		goto err_free;
354 	}
355 	wdt->base = ioremap(res->start, resource_size(res));
356 	if (!wdt->base) {
357 		ret = -ENOMEM;
358 		goto err_free;
359 	}
360 
361 	mpcore_wdt_miscdev.parent = &dev->dev;
362 	ret = misc_register(&mpcore_wdt_miscdev);
363 	if (ret) {
364 		dev_printk(KERN_ERR, wdt->dev,
365 			"cannot register miscdev on minor=%d (err=%d)\n",
366 							WATCHDOG_MINOR, ret);
367 		goto err_misc;
368 	}
369 
370 	ret = request_irq(wdt->irq, mpcore_wdt_fire, 0, "mpcore_wdt", wdt);
371 	if (ret) {
372 		dev_printk(KERN_ERR, wdt->dev,
373 			"cannot register IRQ%d for watchdog\n", wdt->irq);
374 		goto err_irq;
375 	}
376 
377 	mpcore_wdt_stop(wdt);
378 	platform_set_drvdata(dev, wdt);
379 	mpcore_wdt_dev = dev;
380 
381 	return 0;
382 
383 err_irq:
384 	misc_deregister(&mpcore_wdt_miscdev);
385 err_misc:
386 	iounmap(wdt->base);
387 err_free:
388 	kfree(wdt);
389 err_out:
390 	return ret;
391 }
392 
mpcore_wdt_remove(struct platform_device * dev)393 static int __devexit mpcore_wdt_remove(struct platform_device *dev)
394 {
395 	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
396 
397 	platform_set_drvdata(dev, NULL);
398 
399 	misc_deregister(&mpcore_wdt_miscdev);
400 
401 	mpcore_wdt_dev = NULL;
402 
403 	free_irq(wdt->irq, wdt);
404 	iounmap(wdt->base);
405 	kfree(wdt);
406 	return 0;
407 }
408 
409 #ifdef CONFIG_PM
mpcore_wdt_suspend(struct platform_device * dev,pm_message_t msg)410 static int mpcore_wdt_suspend(struct platform_device *dev, pm_message_t msg)
411 {
412 	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
413 	mpcore_wdt_stop(wdt);		/* Turn the WDT off */
414 	return 0;
415 }
416 
mpcore_wdt_resume(struct platform_device * dev)417 static int mpcore_wdt_resume(struct platform_device *dev)
418 {
419 	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
420 	/* re-activate timer */
421 	if (test_bit(0, &wdt->timer_alive))
422 		mpcore_wdt_start(wdt);
423 	return 0;
424 }
425 #else
426 #define mpcore_wdt_suspend	NULL
427 #define mpcore_wdt_resume	NULL
428 #endif
429 
430 /* work with hotplug and coldplug */
431 MODULE_ALIAS("platform:mpcore_wdt");
432 
433 static struct platform_driver mpcore_wdt_driver = {
434 	.probe		= mpcore_wdt_probe,
435 	.remove		= __devexit_p(mpcore_wdt_remove),
436 	.suspend	= mpcore_wdt_suspend,
437 	.resume		= mpcore_wdt_resume,
438 	.shutdown	= mpcore_wdt_shutdown,
439 	.driver		= {
440 		.owner	= THIS_MODULE,
441 		.name	= "mpcore_wdt",
442 	},
443 };
444 
445 static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. "
446 		"mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n";
447 
mpcore_wdt_init(void)448 static int __init mpcore_wdt_init(void)
449 {
450 	/*
451 	 * Check that the margin value is within it's range;
452 	 * if not reset to the default
453 	 */
454 	if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
455 		mpcore_wdt_set_heartbeat(TIMER_MARGIN);
456 		printk(KERN_INFO "mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
457 			TIMER_MARGIN);
458 	}
459 
460 	printk(banner, mpcore_noboot, mpcore_margin, nowayout);
461 
462 	return platform_driver_register(&mpcore_wdt_driver);
463 }
464 
mpcore_wdt_exit(void)465 static void __exit mpcore_wdt_exit(void)
466 {
467 	platform_driver_unregister(&mpcore_wdt_driver);
468 }
469 
470 module_init(mpcore_wdt_init);
471 module_exit(mpcore_wdt_exit);
472 
473 MODULE_AUTHOR("ARM Limited");
474 MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
475 MODULE_LICENSE("GPL");
476 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
477