Linux Audio

Check our new training course

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