• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6/drivers/watchdog/
1/*
2 * mpc8xxx_wdt.c - MPC8xx/MPC83xx/MPC86xx watchdog userspace interface
3 *
4 * Authors: Dave Updegraff <dave@cray.org>
5 * 	    Kumar Gala <galak@kernel.crashing.org>
6 * 		Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
7 * 				..and from sc520_wdt
8 * Copyright (c) 2008  MontaVista Software, Inc.
9 *                     Anton Vorontsov <avorontsov@ru.mvista.com>
10 *
11 * Note: it appears that you can only actually ENABLE or DISABLE the thing
12 * once after POR. Once enabled, you cannot disable, and vice versa.
13 *
14 * This program is free software; you can redistribute  it and/or modify it
15 * under  the terms of  the GNU General  Public License as published by the
16 * Free Software Foundation;  either version 2 of the  License, or (at your
17 * option) any later version.
18 */
19
20#include <linux/fs.h>
21#include <linux/init.h>
22#include <linux/kernel.h>
23#include <linux/timer.h>
24#include <linux/miscdevice.h>
25#include <linux/of_platform.h>
26#include <linux/module.h>
27#include <linux/watchdog.h>
28#include <linux/io.h>
29#include <linux/uaccess.h>
30#include <sysdev/fsl_soc.h>
31
32struct mpc8xxx_wdt {
33	__be32 res0;
34	__be32 swcrr; /* System watchdog control register */
35#define SWCRR_SWTC 0xFFFF0000 /* Software Watchdog Time Count. */
36#define SWCRR_SWEN 0x00000004 /* Watchdog Enable bit. */
37#define SWCRR_SWRI 0x00000002 /* Software Watchdog Reset/Interrupt Select bit.*/
38#define SWCRR_SWPR 0x00000001 /* Software Watchdog Counter Prescale bit. */
39	__be32 swcnr; /* System watchdog count register */
40	u8 res1[2];
41	__be16 swsrr; /* System watchdog service register */
42	u8 res2[0xF0];
43};
44
45struct mpc8xxx_wdt_type {
46	int prescaler;
47	bool hw_enabled;
48};
49
50static struct mpc8xxx_wdt __iomem *wd_base;
51static int mpc8xxx_wdt_init_late(void);
52
53static u16 timeout = 0xffff;
54module_param(timeout, ushort, 0);
55MODULE_PARM_DESC(timeout,
56	"Watchdog timeout in ticks. (0<timeout<65536, default=65535)");
57
58static int reset = 1;
59module_param(reset, bool, 0);
60MODULE_PARM_DESC(reset,
61	"Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
62
63static int nowayout = WATCHDOG_NOWAYOUT;
64module_param(nowayout, int, 0);
65MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
66		 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
67
68/*
69 * We always prescale, but if someone really doesn't want to they can set this
70 * to 0
71 */
72static int prescale = 1;
73static unsigned int timeout_sec;
74
75static unsigned long wdt_is_open;
76static DEFINE_SPINLOCK(wdt_spinlock);
77
78static void mpc8xxx_wdt_keepalive(void)
79{
80	/* Ping the WDT */
81	spin_lock(&wdt_spinlock);
82	out_be16(&wd_base->swsrr, 0x556c);
83	out_be16(&wd_base->swsrr, 0xaa39);
84	spin_unlock(&wdt_spinlock);
85}
86
87static void mpc8xxx_wdt_timer_ping(unsigned long arg);
88static DEFINE_TIMER(wdt_timer, mpc8xxx_wdt_timer_ping, 0, 0);
89
90static void mpc8xxx_wdt_timer_ping(unsigned long arg)
91{
92	mpc8xxx_wdt_keepalive();
93	/* We're pinging it twice faster than needed, just to be sure. */
94	mod_timer(&wdt_timer, jiffies + HZ * timeout_sec / 2);
95}
96
97static void mpc8xxx_wdt_pr_warn(const char *msg)
98{
99	pr_crit("mpc8xxx_wdt: %s, expect the %s soon!\n", msg,
100		reset ? "reset" : "machine check exception");
101}
102
103static ssize_t mpc8xxx_wdt_write(struct file *file, const char __user *buf,
104				 size_t count, loff_t *ppos)
105{
106	if (count)
107		mpc8xxx_wdt_keepalive();
108	return count;
109}
110
111static int mpc8xxx_wdt_open(struct inode *inode, struct file *file)
112{
113	u32 tmp = SWCRR_SWEN;
114	if (test_and_set_bit(0, &wdt_is_open))
115		return -EBUSY;
116
117	/* Once we start the watchdog we can't stop it */
118	if (nowayout)
119		__module_get(THIS_MODULE);
120
121	/* Good, fire up the show */
122	if (prescale)
123		tmp |= SWCRR_SWPR;
124	if (reset)
125		tmp |= SWCRR_SWRI;
126
127	tmp |= timeout << 16;
128
129	out_be32(&wd_base->swcrr, tmp);
130
131	del_timer_sync(&wdt_timer);
132
133	return nonseekable_open(inode, file);
134}
135
136static int mpc8xxx_wdt_release(struct inode *inode, struct file *file)
137{
138	if (!nowayout)
139		mpc8xxx_wdt_timer_ping(0);
140	else
141		mpc8xxx_wdt_pr_warn("watchdog closed");
142	clear_bit(0, &wdt_is_open);
143	return 0;
144}
145
146static long mpc8xxx_wdt_ioctl(struct file *file, unsigned int cmd,
147							unsigned long arg)
148{
149	void __user *argp = (void __user *)arg;
150	int __user *p = argp;
151	static const struct watchdog_info ident = {
152		.options = WDIOF_KEEPALIVEPING,
153		.firmware_version = 1,
154		.identity = "MPC8xxx",
155	};
156
157	switch (cmd) {
158	case WDIOC_GETSUPPORT:
159		return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
160	case WDIOC_GETSTATUS:
161	case WDIOC_GETBOOTSTATUS:
162		return put_user(0, p);
163	case WDIOC_KEEPALIVE:
164		mpc8xxx_wdt_keepalive();
165		return 0;
166	case WDIOC_GETTIMEOUT:
167		return put_user(timeout_sec, p);
168	default:
169		return -ENOTTY;
170	}
171}
172
173static const struct file_operations mpc8xxx_wdt_fops = {
174	.owner		= THIS_MODULE,
175	.llseek		= no_llseek,
176	.write		= mpc8xxx_wdt_write,
177	.unlocked_ioctl	= mpc8xxx_wdt_ioctl,
178	.open		= mpc8xxx_wdt_open,
179	.release	= mpc8xxx_wdt_release,
180};
181
182static struct miscdevice mpc8xxx_wdt_miscdev = {
183	.minor	= WATCHDOG_MINOR,
184	.name	= "watchdog",
185	.fops	= &mpc8xxx_wdt_fops,
186};
187
188static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev,
189				       const struct of_device_id *match)
190{
191	int ret;
192	struct device_node *np = ofdev->dev.of_node;
193	struct mpc8xxx_wdt_type *wdt_type = match->data;
194	u32 freq = fsl_get_sys_freq();
195	bool enabled;
196
197	if (!freq || freq == -1)
198		return -EINVAL;
199
200	wd_base = of_iomap(np, 0);
201	if (!wd_base)
202		return -ENOMEM;
203
204	enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
205	if (!enabled && wdt_type->hw_enabled) {
206		pr_info("mpc8xxx_wdt: could not be enabled in software\n");
207		ret = -ENOSYS;
208		goto err_unmap;
209	}
210
211	/* Calculate the timeout in seconds */
212	if (prescale)
213		timeout_sec = (timeout * wdt_type->prescaler) / freq;
214	else
215		timeout_sec = timeout / freq;
216
217#ifdef MODULE
218	ret = mpc8xxx_wdt_init_late();
219	if (ret)
220		goto err_unmap;
221#endif
222
223	pr_info("WDT driver for MPC8xxx initialized. mode:%s timeout=%d "
224		"(%d seconds)\n", reset ? "reset" : "interrupt", timeout,
225		timeout_sec);
226
227	/*
228	 * If the watchdog was previously enabled or we're running on
229	 * MPC8xxx, we should ping the wdt from the kernel until the
230	 * userspace handles it.
231	 */
232	if (enabled)
233		mpc8xxx_wdt_timer_ping(0);
234	return 0;
235err_unmap:
236	iounmap(wd_base);
237	wd_base = NULL;
238	return ret;
239}
240
241static int __devexit mpc8xxx_wdt_remove(struct platform_device *ofdev)
242{
243	mpc8xxx_wdt_pr_warn("watchdog removed");
244	del_timer_sync(&wdt_timer);
245	misc_deregister(&mpc8xxx_wdt_miscdev);
246	iounmap(wd_base);
247
248	return 0;
249}
250
251static const struct of_device_id mpc8xxx_wdt_match[] = {
252	{
253		.compatible = "mpc83xx_wdt",
254		.data = &(struct mpc8xxx_wdt_type) {
255			.prescaler = 0x10000,
256		},
257	},
258	{
259		.compatible = "fsl,mpc8610-wdt",
260		.data = &(struct mpc8xxx_wdt_type) {
261			.prescaler = 0x10000,
262			.hw_enabled = true,
263		},
264	},
265	{
266		.compatible = "fsl,mpc823-wdt",
267		.data = &(struct mpc8xxx_wdt_type) {
268			.prescaler = 0x800,
269		},
270	},
271	{},
272};
273MODULE_DEVICE_TABLE(of, mpc8xxx_wdt_match);
274
275static struct of_platform_driver mpc8xxx_wdt_driver = {
276	.probe		= mpc8xxx_wdt_probe,
277	.remove		= __devexit_p(mpc8xxx_wdt_remove),
278	.driver = {
279		.name = "mpc8xxx_wdt",
280		.owner = THIS_MODULE,
281		.of_match_table = mpc8xxx_wdt_match,
282	},
283};
284
285/*
286 * We do wdt initialization in two steps: arch_initcall probes the wdt
287 * very early to start pinging the watchdog (misc devices are not yet
288 * available), and later module_init() just registers the misc device.
289 */
290static int mpc8xxx_wdt_init_late(void)
291{
292	int ret;
293
294	if (!wd_base)
295		return -ENODEV;
296
297	ret = misc_register(&mpc8xxx_wdt_miscdev);
298	if (ret) {
299		pr_err("cannot register miscdev on minor=%d (err=%d)\n",
300			WATCHDOG_MINOR, ret);
301		return ret;
302	}
303	return 0;
304}
305#ifndef MODULE
306module_init(mpc8xxx_wdt_init_late);
307#endif
308
309static int __init mpc8xxx_wdt_init(void)
310{
311	return of_register_platform_driver(&mpc8xxx_wdt_driver);
312}
313arch_initcall(mpc8xxx_wdt_init);
314
315static void __exit mpc8xxx_wdt_exit(void)
316{
317	of_unregister_platform_driver(&mpc8xxx_wdt_driver);
318}
319module_exit(mpc8xxx_wdt_exit);
320
321MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
322MODULE_DESCRIPTION("Driver for watchdog timer in MPC8xx/MPC83xx/MPC86xx "
323		   "uProcessors");
324MODULE_LICENSE("GPL");
325MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
326