1b9d36b851a5085d35b9598235a456604532f306eRussell King/*
2b9d36b851a5085d35b9598235a456604532f306eRussell King *	Watchdog driver for the mpcore watchdog timer
3b9d36b851a5085d35b9598235a456604532f306eRussell King *
4b9d36b851a5085d35b9598235a456604532f306eRussell King *	(c) Copyright 2004 ARM Limited
5b9d36b851a5085d35b9598235a456604532f306eRussell King *
6b9d36b851a5085d35b9598235a456604532f306eRussell King *	Based on the SoftDog driver:
729fa0586de4fe518f122a915b8c6e92d12e8ca7fAlan Cox *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
8143a2e54bf53216674eada16e8953f48b159e08aWim Van Sebroeck *						All Rights Reserved.
9b9d36b851a5085d35b9598235a456604532f306eRussell King *
10b9d36b851a5085d35b9598235a456604532f306eRussell King *	This program is free software; you can redistribute it and/or
11b9d36b851a5085d35b9598235a456604532f306eRussell King *	modify it under the terms of the GNU General Public License
12b9d36b851a5085d35b9598235a456604532f306eRussell King *	as published by the Free Software Foundation; either version
13b9d36b851a5085d35b9598235a456604532f306eRussell King *	2 of the License, or (at your option) any later version.
14b9d36b851a5085d35b9598235a456604532f306eRussell King *
15b9d36b851a5085d35b9598235a456604532f306eRussell King *	Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
16b9d36b851a5085d35b9598235a456604532f306eRussell King *	warranty for any of this software. This material is provided
17b9d36b851a5085d35b9598235a456604532f306eRussell King *	"AS-IS" and at no charge.
18b9d36b851a5085d35b9598235a456604532f306eRussell King *
19b9d36b851a5085d35b9598235a456604532f306eRussell King *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
20b9d36b851a5085d35b9598235a456604532f306eRussell King *
21b9d36b851a5085d35b9598235a456604532f306eRussell King */
2227c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
2327c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2427c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches
25b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/module.h>
26b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/moduleparam.h>
27b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/types.h>
28b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/miscdevice.h>
29b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/watchdog.h>
30b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/fs.h>
31b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/reboot.h>
32b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/init.h>
33b9d36b851a5085d35b9598235a456604532f306eRussell King#include <linux/interrupt.h>
34d052d1beff706920e82c5d55006b08e256b5df09Russell King#include <linux/platform_device.h>
3583ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox#include <linux/uaccess.h>
365a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
3798af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar#include <linux/io.h>
38ad4162f3712ddf25e148cff1e7dc37eafdff3e51Russell King
3998af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar#include <asm/smp_twd.h>
40b9d36b851a5085d35b9598235a456604532f306eRussell King
41b9d36b851a5085d35b9598235a456604532f306eRussell Kingstruct mpcore_wdt {
42b9d36b851a5085d35b9598235a456604532f306eRussell King	unsigned long	timer_alive;
43b9d36b851a5085d35b9598235a456604532f306eRussell King	struct device	*dev;
44b9d36b851a5085d35b9598235a456604532f306eRussell King	void __iomem	*base;
45b9d36b851a5085d35b9598235a456604532f306eRussell King	int		irq;
46b9d36b851a5085d35b9598235a456604532f306eRussell King	unsigned int	perturb;
47b9d36b851a5085d35b9598235a456604532f306eRussell King	char		expect_close;
48b9d36b851a5085d35b9598235a456604532f306eRussell King};
49b9d36b851a5085d35b9598235a456604532f306eRussell King
50aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic struct platform_device *mpcore_wdt_pdev;
5198af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagarstatic DEFINE_SPINLOCK(wdt_lock);
52b9d36b851a5085d35b9598235a456604532f306eRussell King
53b9d36b851a5085d35b9598235a456604532f306eRussell King#define TIMER_MARGIN	60
54b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_margin = TIMER_MARGIN;
55b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_param(mpcore_margin, int, 0);
5683ab1a53f219c8139199633f60ab0ef88ef18c54Alan CoxMODULE_PARM_DESC(mpcore_margin,
5783ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	"MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
5883ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox				__MODULE_STRING(TIMER_MARGIN) ")");
59b9d36b851a5085d35b9598235a456604532f306eRussell King
6086a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckstatic bool nowayout = WATCHDOG_NOWAYOUT;
6186a1e1896c2710402e29a875d8d830244274244dWim Van Sebroeckmodule_param(nowayout, bool, 0);
6283ab1a53f219c8139199633f60ab0ef88ef18c54Alan CoxMODULE_PARM_DESC(nowayout,
6383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	"Watchdog cannot be stopped once started (default="
6483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
65b9d36b851a5085d35b9598235a456604532f306eRussell King
66b9d36b851a5085d35b9598235a456604532f306eRussell King#define ONLY_TESTING	0
67b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_noboot = ONLY_TESTING;
68b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_param(mpcore_noboot, int, 0);
69a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van SebroeckMODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
70a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck	"set to 1 to ignore reboots, 0 to reboot (default="
71a77dba7e444a6618cbb666d1b42b79842b9c0171Wim Van Sebroeck					__MODULE_STRING(ONLY_TESTING) ")");
72b9d36b851a5085d35b9598235a456604532f306eRussell King
73b9d36b851a5085d35b9598235a456604532f306eRussell King/*
74b9d36b851a5085d35b9598235a456604532f306eRussell King *	This is the interrupt handler.  Note that we only use this
75b9d36b851a5085d35b9598235a456604532f306eRussell King *	in testing mode, so don't actually do a reboot here.
76b9d36b851a5085d35b9598235a456604532f306eRussell King */
777d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t mpcore_wdt_fire(int irq, void *arg)
78b9d36b851a5085d35b9598235a456604532f306eRussell King{
79b9d36b851a5085d35b9598235a456604532f306eRussell King	struct mpcore_wdt *wdt = arg;
80b9d36b851a5085d35b9598235a456604532f306eRussell King
81b9d36b851a5085d35b9598235a456604532f306eRussell King	/* Check it really was our interrupt */
82b9d36b851a5085d35b9598235a456604532f306eRussell King	if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
8383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox		dev_printk(KERN_CRIT, wdt->dev,
8483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox					"Triggered - Reboot ignored.\n");
85b9d36b851a5085d35b9598235a456604532f306eRussell King		/* Clear the interrupt on the watchdog */
86b9d36b851a5085d35b9598235a456604532f306eRussell King		writel(1, wdt->base + TWD_WDOG_INTSTAT);
87b9d36b851a5085d35b9598235a456604532f306eRussell King		return IRQ_HANDLED;
88b9d36b851a5085d35b9598235a456604532f306eRussell King	}
89b9d36b851a5085d35b9598235a456604532f306eRussell King	return IRQ_NONE;
90b9d36b851a5085d35b9598235a456604532f306eRussell King}
91b9d36b851a5085d35b9598235a456604532f306eRussell King
92b9d36b851a5085d35b9598235a456604532f306eRussell King/*
93b9d36b851a5085d35b9598235a456604532f306eRussell King *	mpcore_wdt_keepalive - reload the timer
94b9d36b851a5085d35b9598235a456604532f306eRussell King *
95b9d36b851a5085d35b9598235a456604532f306eRussell King *	Note that the spec says a DIFFERENT value must be written to the reload
96b9d36b851a5085d35b9598235a456604532f306eRussell King *	register each time.  The "perturb" variable deals with this by adding 1
97b9d36b851a5085d35b9598235a456604532f306eRussell King *	to the count every other time the function is called.
98b9d36b851a5085d35b9598235a456604532f306eRussell King */
99b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
100b9d36b851a5085d35b9598235a456604532f306eRussell King{
10198af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar	unsigned long count;
102b9d36b851a5085d35b9598235a456604532f306eRussell King
10398af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar	spin_lock(&wdt_lock);
104b9d36b851a5085d35b9598235a456604532f306eRussell King	/* Assume prescale is set to 256 */
10598af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar	count =  __raw_readl(wdt->base + TWD_WDOG_COUNTER);
10698af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar	count = (0xFFFFFFFFU - count) * (HZ / 5);
10798af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar	count = (count / 256) * mpcore_margin;
108b9d36b851a5085d35b9598235a456604532f306eRussell King
109b9d36b851a5085d35b9598235a456604532f306eRussell King	/* Reload the counter */
110b9d36b851a5085d35b9598235a456604532f306eRussell King	writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
111b9d36b851a5085d35b9598235a456604532f306eRussell King	wdt->perturb = wdt->perturb ? 0 : 1;
11283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	spin_unlock(&wdt_lock);
113b9d36b851a5085d35b9598235a456604532f306eRussell King}
114b9d36b851a5085d35b9598235a456604532f306eRussell King
115b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_stop(struct mpcore_wdt *wdt)
116b9d36b851a5085d35b9598235a456604532f306eRussell King{
11783ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	spin_lock(&wdt_lock);
118b9d36b851a5085d35b9598235a456604532f306eRussell King	writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
119b9d36b851a5085d35b9598235a456604532f306eRussell King	writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
120b9d36b851a5085d35b9598235a456604532f306eRussell King	writel(0x0, wdt->base + TWD_WDOG_CONTROL);
12183ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	spin_unlock(&wdt_lock);
122b9d36b851a5085d35b9598235a456604532f306eRussell King}
123b9d36b851a5085d35b9598235a456604532f306eRussell King
124b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void mpcore_wdt_start(struct mpcore_wdt *wdt)
125b9d36b851a5085d35b9598235a456604532f306eRussell King{
126b9d36b851a5085d35b9598235a456604532f306eRussell King	dev_printk(KERN_INFO, wdt->dev, "enabling watchdog.\n");
127b9d36b851a5085d35b9598235a456604532f306eRussell King
128b9d36b851a5085d35b9598235a456604532f306eRussell King	/* This loads the count register but does NOT start the count yet */
129b9d36b851a5085d35b9598235a456604532f306eRussell King	mpcore_wdt_keepalive(wdt);
130b9d36b851a5085d35b9598235a456604532f306eRussell King
131b9d36b851a5085d35b9598235a456604532f306eRussell King	if (mpcore_noboot) {
132b9d36b851a5085d35b9598235a456604532f306eRussell King		/* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
133b9d36b851a5085d35b9598235a456604532f306eRussell King		writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
134b9d36b851a5085d35b9598235a456604532f306eRussell King	} else {
135b9d36b851a5085d35b9598235a456604532f306eRussell King		/* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
136b9d36b851a5085d35b9598235a456604532f306eRussell King		writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
137b9d36b851a5085d35b9598235a456604532f306eRussell King	}
138b9d36b851a5085d35b9598235a456604532f306eRussell King}
139b9d36b851a5085d35b9598235a456604532f306eRussell King
140b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_set_heartbeat(int t)
141b9d36b851a5085d35b9598235a456604532f306eRussell King{
142b9d36b851a5085d35b9598235a456604532f306eRussell King	if (t < 0x0001 || t > 0xFFFF)
143b9d36b851a5085d35b9598235a456604532f306eRussell King		return -EINVAL;
144b9d36b851a5085d35b9598235a456604532f306eRussell King
145b9d36b851a5085d35b9598235a456604532f306eRussell King	mpcore_margin = t;
146b9d36b851a5085d35b9598235a456604532f306eRussell King	return 0;
147b9d36b851a5085d35b9598235a456604532f306eRussell King}
148b9d36b851a5085d35b9598235a456604532f306eRussell King
149b9d36b851a5085d35b9598235a456604532f306eRussell King/*
150b9d36b851a5085d35b9598235a456604532f306eRussell King *	/dev/watchdog handling
151b9d36b851a5085d35b9598235a456604532f306eRussell King */
152b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_open(struct inode *inode, struct file *file)
153b9d36b851a5085d35b9598235a456604532f306eRussell King{
154aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
155b9d36b851a5085d35b9598235a456604532f306eRussell King
156b9d36b851a5085d35b9598235a456604532f306eRussell King	if (test_and_set_bit(0, &wdt->timer_alive))
157b9d36b851a5085d35b9598235a456604532f306eRussell King		return -EBUSY;
158b9d36b851a5085d35b9598235a456604532f306eRussell King
159b9d36b851a5085d35b9598235a456604532f306eRussell King	if (nowayout)
160b9d36b851a5085d35b9598235a456604532f306eRussell King		__module_get(THIS_MODULE);
161b9d36b851a5085d35b9598235a456604532f306eRussell King
162b9d36b851a5085d35b9598235a456604532f306eRussell King	file->private_data = wdt;
163b9d36b851a5085d35b9598235a456604532f306eRussell King
164b9d36b851a5085d35b9598235a456604532f306eRussell King	/*
165b9d36b851a5085d35b9598235a456604532f306eRussell King	 *	Activate timer
166b9d36b851a5085d35b9598235a456604532f306eRussell King	 */
167b9d36b851a5085d35b9598235a456604532f306eRussell King	mpcore_wdt_start(wdt);
168b9d36b851a5085d35b9598235a456604532f306eRussell King
169b9d36b851a5085d35b9598235a456604532f306eRussell King	return nonseekable_open(inode, file);
170b9d36b851a5085d35b9598235a456604532f306eRussell King}
171b9d36b851a5085d35b9598235a456604532f306eRussell King
172b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int mpcore_wdt_release(struct inode *inode, struct file *file)
173b9d36b851a5085d35b9598235a456604532f306eRussell King{
174b9d36b851a5085d35b9598235a456604532f306eRussell King	struct mpcore_wdt *wdt = file->private_data;
175b9d36b851a5085d35b9598235a456604532f306eRussell King
176b9d36b851a5085d35b9598235a456604532f306eRussell King	/*
177b9d36b851a5085d35b9598235a456604532f306eRussell King	 *	Shut off the timer.
1785f3b27569fc0286a51f8d0655c7fb4f5b36aea65Wim Van Sebroeck	 *	Lock it in if it's a module and we set nowayout
179b9d36b851a5085d35b9598235a456604532f306eRussell King	 */
18083ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	if (wdt->expect_close == 42)
181b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_stop(wdt);
18283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	else {
18383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox		dev_printk(KERN_CRIT, wdt->dev,
18483ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox				"unexpected close, not stopping watchdog!\n");
185b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_keepalive(wdt);
186b9d36b851a5085d35b9598235a456604532f306eRussell King	}
187b9d36b851a5085d35b9598235a456604532f306eRussell King	clear_bit(0, &wdt->timer_alive);
188b9d36b851a5085d35b9598235a456604532f306eRussell King	wdt->expect_close = 0;
189b9d36b851a5085d35b9598235a456604532f306eRussell King	return 0;
190b9d36b851a5085d35b9598235a456604532f306eRussell King}
191b9d36b851a5085d35b9598235a456604532f306eRussell King
19283ab1a53f219c8139199633f60ab0ef88ef18c54Alan Coxstatic ssize_t mpcore_wdt_write(struct file *file, const char *data,
19383ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox						size_t len, loff_t *ppos)
194b9d36b851a5085d35b9598235a456604532f306eRussell King{
195b9d36b851a5085d35b9598235a456604532f306eRussell King	struct mpcore_wdt *wdt = file->private_data;
196b9d36b851a5085d35b9598235a456604532f306eRussell King
197b9d36b851a5085d35b9598235a456604532f306eRussell King	/*
198b9d36b851a5085d35b9598235a456604532f306eRussell King	 *	Refresh the timer.
199b9d36b851a5085d35b9598235a456604532f306eRussell King	 */
200b9d36b851a5085d35b9598235a456604532f306eRussell King	if (len) {
201b9d36b851a5085d35b9598235a456604532f306eRussell King		if (!nowayout) {
202b9d36b851a5085d35b9598235a456604532f306eRussell King			size_t i;
203b9d36b851a5085d35b9598235a456604532f306eRussell King
204b9d36b851a5085d35b9598235a456604532f306eRussell King			/* In case it was set long ago */
205b9d36b851a5085d35b9598235a456604532f306eRussell King			wdt->expect_close = 0;
206b9d36b851a5085d35b9598235a456604532f306eRussell King
207b9d36b851a5085d35b9598235a456604532f306eRussell King			for (i = 0; i != len; i++) {
208b9d36b851a5085d35b9598235a456604532f306eRussell King				char c;
209b9d36b851a5085d35b9598235a456604532f306eRussell King
210b9d36b851a5085d35b9598235a456604532f306eRussell King				if (get_user(c, data + i))
211b9d36b851a5085d35b9598235a456604532f306eRussell King					return -EFAULT;
212b9d36b851a5085d35b9598235a456604532f306eRussell King				if (c == 'V')
213b9d36b851a5085d35b9598235a456604532f306eRussell King					wdt->expect_close = 42;
214b9d36b851a5085d35b9598235a456604532f306eRussell King			}
215b9d36b851a5085d35b9598235a456604532f306eRussell King		}
216b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_keepalive(wdt);
217b9d36b851a5085d35b9598235a456604532f306eRussell King	}
218b9d36b851a5085d35b9598235a456604532f306eRussell King	return len;
219b9d36b851a5085d35b9598235a456604532f306eRussell King}
220b9d36b851a5085d35b9598235a456604532f306eRussell King
22142747d712de56cf2087b702d2ad90af114c53138Wim Van Sebroeckstatic const struct watchdog_info ident = {
222b9d36b851a5085d35b9598235a456604532f306eRussell King	.options		= WDIOF_SETTIMEOUT |
223b9d36b851a5085d35b9598235a456604532f306eRussell King				  WDIOF_KEEPALIVEPING |
224b9d36b851a5085d35b9598235a456604532f306eRussell King				  WDIOF_MAGICCLOSE,
225b9d36b851a5085d35b9598235a456604532f306eRussell King	.identity		= "MPcore Watchdog",
226b9d36b851a5085d35b9598235a456604532f306eRussell King};
227b9d36b851a5085d35b9598235a456604532f306eRussell King
22883ab1a53f219c8139199633f60ab0ef88ef18c54Alan Coxstatic long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
22983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox							unsigned long arg)
230b9d36b851a5085d35b9598235a456604532f306eRussell King{
231b9d36b851a5085d35b9598235a456604532f306eRussell King	struct mpcore_wdt *wdt = file->private_data;
232b9d36b851a5085d35b9598235a456604532f306eRussell King	int ret;
233b9d36b851a5085d35b9598235a456604532f306eRussell King	union {
234b9d36b851a5085d35b9598235a456604532f306eRussell King		struct watchdog_info ident;
235b9d36b851a5085d35b9598235a456604532f306eRussell King		int i;
236b9d36b851a5085d35b9598235a456604532f306eRussell King	} uarg;
237b9d36b851a5085d35b9598235a456604532f306eRussell King
238b9d36b851a5085d35b9598235a456604532f306eRussell King	if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
239795b89d207d8ff5397f9ff1f4d44662aa7c821fcSamuel Tardieu		return -ENOTTY;
240b9d36b851a5085d35b9598235a456604532f306eRussell King
241b9d36b851a5085d35b9598235a456604532f306eRussell King	if (_IOC_DIR(cmd) & _IOC_WRITE) {
242b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
243b9d36b851a5085d35b9598235a456604532f306eRussell King		if (ret)
244b9d36b851a5085d35b9598235a456604532f306eRussell King			return -EFAULT;
245b9d36b851a5085d35b9598235a456604532f306eRussell King	}
246b9d36b851a5085d35b9598235a456604532f306eRussell King
247b9d36b851a5085d35b9598235a456604532f306eRussell King	switch (cmd) {
248b9d36b851a5085d35b9598235a456604532f306eRussell King	case WDIOC_GETSUPPORT:
249b9d36b851a5085d35b9598235a456604532f306eRussell King		uarg.ident = ident;
250b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = 0;
251b9d36b851a5085d35b9598235a456604532f306eRussell King		break;
252b9d36b851a5085d35b9598235a456604532f306eRussell King
2530c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_GETSTATUS:
2540c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck	case WDIOC_GETBOOTSTATUS:
2550c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		uarg.i = 0;
2560c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		ret = 0;
2570c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck		break;
2580c06090c9472db0525cb6fe229c3bea33bbbbb3cWim Van Sebroeck
259b9d36b851a5085d35b9598235a456604532f306eRussell King	case WDIOC_SETOPTIONS:
260b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = -EINVAL;
261b9d36b851a5085d35b9598235a456604532f306eRussell King		if (uarg.i & WDIOS_DISABLECARD) {
262b9d36b851a5085d35b9598235a456604532f306eRussell King			mpcore_wdt_stop(wdt);
263b9d36b851a5085d35b9598235a456604532f306eRussell King			ret = 0;
264b9d36b851a5085d35b9598235a456604532f306eRussell King		}
265b9d36b851a5085d35b9598235a456604532f306eRussell King		if (uarg.i & WDIOS_ENABLECARD) {
266b9d36b851a5085d35b9598235a456604532f306eRussell King			mpcore_wdt_start(wdt);
267b9d36b851a5085d35b9598235a456604532f306eRussell King			ret = 0;
268b9d36b851a5085d35b9598235a456604532f306eRussell King		}
269b9d36b851a5085d35b9598235a456604532f306eRussell King		break;
270b9d36b851a5085d35b9598235a456604532f306eRussell King
271b9d36b851a5085d35b9598235a456604532f306eRussell King	case WDIOC_KEEPALIVE:
272b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_keepalive(wdt);
273b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = 0;
274b9d36b851a5085d35b9598235a456604532f306eRussell King		break;
275b9d36b851a5085d35b9598235a456604532f306eRussell King
276b9d36b851a5085d35b9598235a456604532f306eRussell King	case WDIOC_SETTIMEOUT:
277b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = mpcore_wdt_set_heartbeat(uarg.i);
278b9d36b851a5085d35b9598235a456604532f306eRussell King		if (ret)
279b9d36b851a5085d35b9598235a456604532f306eRussell King			break;
280b9d36b851a5085d35b9598235a456604532f306eRussell King
281b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_keepalive(wdt);
282b9d36b851a5085d35b9598235a456604532f306eRussell King		/* Fall */
283b9d36b851a5085d35b9598235a456604532f306eRussell King	case WDIOC_GETTIMEOUT:
284b9d36b851a5085d35b9598235a456604532f306eRussell King		uarg.i = mpcore_margin;
285b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = 0;
286b9d36b851a5085d35b9598235a456604532f306eRussell King		break;
287b9d36b851a5085d35b9598235a456604532f306eRussell King
288b9d36b851a5085d35b9598235a456604532f306eRussell King	default:
289795b89d207d8ff5397f9ff1f4d44662aa7c821fcSamuel Tardieu		return -ENOTTY;
290b9d36b851a5085d35b9598235a456604532f306eRussell King	}
291b9d36b851a5085d35b9598235a456604532f306eRussell King
292b9d36b851a5085d35b9598235a456604532f306eRussell King	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
293b9d36b851a5085d35b9598235a456604532f306eRussell King		ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
294b9d36b851a5085d35b9598235a456604532f306eRussell King		if (ret)
295b9d36b851a5085d35b9598235a456604532f306eRussell King			ret = -EFAULT;
296b9d36b851a5085d35b9598235a456604532f306eRussell King	}
297b9d36b851a5085d35b9598235a456604532f306eRussell King	return ret;
298b9d36b851a5085d35b9598235a456604532f306eRussell King}
299b9d36b851a5085d35b9598235a456604532f306eRussell King
300b9d36b851a5085d35b9598235a456604532f306eRussell King/*
301b9d36b851a5085d35b9598235a456604532f306eRussell King *	System shutdown handler.  Turn off the watchdog if we're
302b9d36b851a5085d35b9598235a456604532f306eRussell King *	restarting or halting the system.
303b9d36b851a5085d35b9598235a456604532f306eRussell King */
304aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic void mpcore_wdt_shutdown(struct platform_device *pdev)
305b9d36b851a5085d35b9598235a456604532f306eRussell King{
306aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
307b9d36b851a5085d35b9598235a456604532f306eRussell King
308b9d36b851a5085d35b9598235a456604532f306eRussell King	if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
309b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_stop(wdt);
310b9d36b851a5085d35b9598235a456604532f306eRussell King}
311b9d36b851a5085d35b9598235a456604532f306eRussell King
312b9d36b851a5085d35b9598235a456604532f306eRussell King/*
313b9d36b851a5085d35b9598235a456604532f306eRussell King *	Kernel Interfaces
314b9d36b851a5085d35b9598235a456604532f306eRussell King */
31562322d2554d2f9680c8ace7bbf1f97d8fa84ad1aArjan van de Venstatic const struct file_operations mpcore_wdt_fops = {
316b9d36b851a5085d35b9598235a456604532f306eRussell King	.owner		= THIS_MODULE,
317b9d36b851a5085d35b9598235a456604532f306eRussell King	.llseek		= no_llseek,
318b9d36b851a5085d35b9598235a456604532f306eRussell King	.write		= mpcore_wdt_write,
31983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox	.unlocked_ioctl	= mpcore_wdt_ioctl,
320b9d36b851a5085d35b9598235a456604532f306eRussell King	.open		= mpcore_wdt_open,
321b9d36b851a5085d35b9598235a456604532f306eRussell King	.release	= mpcore_wdt_release,
322b9d36b851a5085d35b9598235a456604532f306eRussell King};
323b9d36b851a5085d35b9598235a456604532f306eRussell King
324b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic struct miscdevice mpcore_wdt_miscdev = {
325b9d36b851a5085d35b9598235a456604532f306eRussell King	.minor		= WATCHDOG_MINOR,
326b9d36b851a5085d35b9598235a456604532f306eRussell King	.name		= "watchdog",
327b9d36b851a5085d35b9598235a456604532f306eRussell King	.fops		= &mpcore_wdt_fops,
328b9d36b851a5085d35b9598235a456604532f306eRussell King};
329b9d36b851a5085d35b9598235a456604532f306eRussell King
330aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int __devinit mpcore_wdt_probe(struct platform_device *pdev)
331b9d36b851a5085d35b9598235a456604532f306eRussell King{
332b9d36b851a5085d35b9598235a456604532f306eRussell King	struct mpcore_wdt *wdt;
333b9d36b851a5085d35b9598235a456604532f306eRussell King	struct resource *res;
334b9d36b851a5085d35b9598235a456604532f306eRussell King	int ret;
335b9d36b851a5085d35b9598235a456604532f306eRussell King
336b9d36b851a5085d35b9598235a456604532f306eRussell King	/* We only accept one device, and it must have an id of -1 */
337aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	if (pdev->id != -1)
338b9d36b851a5085d35b9598235a456604532f306eRussell King		return -ENODEV;
339b9d36b851a5085d35b9598235a456604532f306eRussell King
340aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
34175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar	if (!res)
34275f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar		return -ENODEV;
343b9d36b851a5085d35b9598235a456604532f306eRussell King
34475f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar	wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
34575f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar	if (!wdt)
34675f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar		return -ENOMEM;
347b9d36b851a5085d35b9598235a456604532f306eRussell King
348aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	wdt->dev = &pdev->dev;
349aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	wdt->irq = platform_get_irq(pdev, 0);
35060a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar	if (wdt->irq >= 0) {
35160a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar		ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
35260a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar				"mpcore_wdt", wdt);
35360a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar		if (ret) {
35460a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar			dev_printk(KERN_ERR, wdt->dev,
35560a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar					"cannot register IRQ%d for watchdog\n",
35660a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar					wdt->irq);
35760a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar			return ret;
35860a1aa50b2ca142a91455203ca2aa09502eddd20Viresh Kumar		}
359b9d36b851a5085d35b9598235a456604532f306eRussell King	}
360b9d36b851a5085d35b9598235a456604532f306eRussell King
36175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar	wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
36275f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar	if (!wdt->base)
36375f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar		return -ENOMEM;
36475f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar
365aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	mpcore_wdt_miscdev.parent = &pdev->dev;
366b9d36b851a5085d35b9598235a456604532f306eRussell King	ret = misc_register(&mpcore_wdt_miscdev);
367b9d36b851a5085d35b9598235a456604532f306eRussell King	if (ret) {
36898af057092f8f0dabe63c5df08adc2bbfbddb1d2Srinidhi Kasagar		dev_printk(KERN_ERR, wdt->dev,
36983ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox			"cannot register miscdev on minor=%d (err=%d)\n",
37083ab1a53f219c8139199633f60ab0ef88ef18c54Alan Cox							WATCHDOG_MINOR, ret);
37175f5a536c0b605b9b8406325f51f62f67141973eViresh Kumar		return ret;
372b9d36b851a5085d35b9598235a456604532f306eRussell King	}
373b9d36b851a5085d35b9598235a456604532f306eRussell King
374b9d36b851a5085d35b9598235a456604532f306eRussell King	mpcore_wdt_stop(wdt);
375aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	platform_set_drvdata(pdev, wdt);
376aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	mpcore_wdt_pdev = pdev;
377b9d36b851a5085d35b9598235a456604532f306eRussell King
378b9d36b851a5085d35b9598235a456604532f306eRussell King	return 0;
379b9d36b851a5085d35b9598235a456604532f306eRussell King}
380b9d36b851a5085d35b9598235a456604532f306eRussell King
381aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int __devexit mpcore_wdt_remove(struct platform_device *pdev)
382b9d36b851a5085d35b9598235a456604532f306eRussell King{
383aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	platform_set_drvdata(pdev, NULL);
384b9d36b851a5085d35b9598235a456604532f306eRussell King
385b9d36b851a5085d35b9598235a456604532f306eRussell King	misc_deregister(&mpcore_wdt_miscdev);
386b9d36b851a5085d35b9598235a456604532f306eRussell King
387aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	mpcore_wdt_pdev = NULL;
388b9d36b851a5085d35b9598235a456604532f306eRussell King
389b9d36b851a5085d35b9598235a456604532f306eRussell King	return 0;
390b9d36b851a5085d35b9598235a456604532f306eRussell King}
391b9d36b851a5085d35b9598235a456604532f306eRussell King
392641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#ifdef CONFIG_PM
393aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
394641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham{
395aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
396641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	mpcore_wdt_stop(wdt);		/* Turn the WDT off */
397641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	return 0;
398641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham}
399641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham
400aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumarstatic int mpcore_wdt_resume(struct platform_device *pdev)
401641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham{
402aa065770f55f44ed24f0a9b76ec6e2135264d43bViresh Kumar	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
403641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	/* re-activate timer */
404641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	if (test_bit(0, &wdt->timer_alive))
405641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham		mpcore_wdt_start(wdt);
406641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	return 0;
407641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham}
408641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#else
409641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#define mpcore_wdt_suspend	NULL
410641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#define mpcore_wdt_resume	NULL
411641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham#endif
412641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham
413f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay Sievers/* work with hotplug and coldplug */
414f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay SieversMODULE_ALIAS("platform:mpcore_wdt");
415f37d193c7c150c40059c7ce5de34e8b28a9cd4aeKay Sievers
4163ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell Kingstatic struct platform_driver mpcore_wdt_driver = {
417b9d36b851a5085d35b9598235a456604532f306eRussell King	.probe		= mpcore_wdt_probe,
418b9d36b851a5085d35b9598235a456604532f306eRussell King	.remove		= __devexit_p(mpcore_wdt_remove),
419641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	.suspend	= mpcore_wdt_suspend,
420641e4f449512ced3a3b784b33ce191e664a6d2ddPeter Fordham	.resume		= mpcore_wdt_resume,
421b9d36b851a5085d35b9598235a456604532f306eRussell King	.shutdown	= mpcore_wdt_shutdown,
4223ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	.driver		= {
4233ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King		.owner	= THIS_MODULE,
4243ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King		.name	= "mpcore_wdt",
4253ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	},
426b9d36b851a5085d35b9598235a456604532f306eRussell King};
427b9d36b851a5085d35b9598235a456604532f306eRussell King
428b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic int __init mpcore_wdt_init(void)
429b9d36b851a5085d35b9598235a456604532f306eRussell King{
430b9d36b851a5085d35b9598235a456604532f306eRussell King	/*
431b9d36b851a5085d35b9598235a456604532f306eRussell King	 * Check that the margin value is within it's range;
432b9d36b851a5085d35b9598235a456604532f306eRussell King	 * if not reset to the default
433b9d36b851a5085d35b9598235a456604532f306eRussell King	 */
434b9d36b851a5085d35b9598235a456604532f306eRussell King	if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
435b9d36b851a5085d35b9598235a456604532f306eRussell King		mpcore_wdt_set_heartbeat(TIMER_MARGIN);
43627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
437b9d36b851a5085d35b9598235a456604532f306eRussell King			TIMER_MARGIN);
438b9d36b851a5085d35b9598235a456604532f306eRussell King	}
439b9d36b851a5085d35b9598235a456604532f306eRussell King
44027c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
44127c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches		mpcore_noboot, mpcore_margin, nowayout);
442b9d36b851a5085d35b9598235a456604532f306eRussell King
4433ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	return platform_driver_register(&mpcore_wdt_driver);
444b9d36b851a5085d35b9598235a456604532f306eRussell King}
445b9d36b851a5085d35b9598235a456604532f306eRussell King
446b9d36b851a5085d35b9598235a456604532f306eRussell Kingstatic void __exit mpcore_wdt_exit(void)
447b9d36b851a5085d35b9598235a456604532f306eRussell King{
4483ae5eaec1d2d9c0cf53745352e7d4b152810ba24Russell King	platform_driver_unregister(&mpcore_wdt_driver);
449b9d36b851a5085d35b9598235a456604532f306eRussell King}
450b9d36b851a5085d35b9598235a456604532f306eRussell King
451b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_init(mpcore_wdt_init);
452b9d36b851a5085d35b9598235a456604532f306eRussell Kingmodule_exit(mpcore_wdt_exit);
453b9d36b851a5085d35b9598235a456604532f306eRussell King
454b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_AUTHOR("ARM Limited");
455b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_DESCRIPTION("MPcore Watchdog Device Driver");
456b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_LICENSE("GPL");
457b9d36b851a5085d35b9598235a456604532f306eRussell KingMODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
458