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, IRQF_DISABLED,
371 							"mpcore_wdt", wdt);
372 	if (ret) {
373 		dev_printk(KERN_ERR, wdt->dev,
374 			"cannot register IRQ%d for watchdog\n", wdt->irq);
375 		goto err_irq;
376 	}
377 
378 	mpcore_wdt_stop(wdt);
379 	platform_set_drvdata(dev, wdt);
380 	mpcore_wdt_dev = dev;
381 
382 	return 0;
383 
384 err_irq:
385 	misc_deregister(&mpcore_wdt_miscdev);
386 err_misc:
387 	iounmap(wdt->base);
388 err_free:
389 	kfree(wdt);
390 err_out:
391 	return ret;
392 }
393 
mpcore_wdt_remove(struct platform_device * dev)394 static int __devexit mpcore_wdt_remove(struct platform_device *dev)
395 {
396 	struct mpcore_wdt *wdt = platform_get_drvdata(dev);
397 
398 	platform_set_drvdata(dev, NULL);
399 
400 	misc_deregister(&mpcore_wdt_miscdev);
401 
402 	mpcore_wdt_dev = NULL;
403 
404 	free_irq(wdt->irq, wdt);
405 	iounmap(wdt->base);
406 	kfree(wdt);
407 	return 0;
408 }
409 
410 /* work with hotplug and coldplug */
411 MODULE_ALIAS("platform:mpcore_wdt");
412 
413 static struct platform_driver mpcore_wdt_driver = {
414 	.probe		= mpcore_wdt_probe,
415 	.remove		= __devexit_p(mpcore_wdt_remove),
416 	.shutdown	= mpcore_wdt_shutdown,
417 	.driver		= {
418 		.owner	= THIS_MODULE,
419 		.name	= "mpcore_wdt",
420 	},
421 };
422 
423 static char banner[] __initdata = KERN_INFO "MPcore Watchdog Timer: 0.1. "
424 		"mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n";
425 
mpcore_wdt_init(void)426 static int __init mpcore_wdt_init(void)
427 {
428 	/*
429 	 * Check that the margin value is within it's range;
430 	 * if not reset to the default
431 	 */
432 	if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
433 		mpcore_wdt_set_heartbeat(TIMER_MARGIN);
434 		printk(KERN_INFO "mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
435 			TIMER_MARGIN);
436 	}
437 
438 	printk(banner, mpcore_noboot, mpcore_margin, nowayout);
439 
440 	return platform_driver_register(&mpcore_wdt_driver);
441 }
442 
mpcore_wdt_exit(void)443 static void __exit mpcore_wdt_exit(void)
444 {
445 	platform_driver_unregister(&mpcore_wdt_driver);
446 }
447 
448 module_init(mpcore_wdt_init);
449 module_exit(mpcore_wdt_exit);
450 
451 MODULE_AUTHOR("ARM Limited");
452 MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
453 MODULE_LICENSE("GPL");
454 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
455