aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/mpc83xx_wdt.c
diff options
context:
space:
mode:
authorAnton Vorontsov <avorontsov@ru.mvista.com>2008-07-04 02:51:34 -0400
committerWim Van Sebroeck <wim@iguana.be>2008-08-06 09:05:12 -0400
commit500c919e3d699644cc9d6c1e93022481baafd8e1 (patch)
tree4bf64f774303f6adbb8368093cca12170fc3f5ca /drivers/watchdog/mpc83xx_wdt.c
parentef8ab12ec2d663f9b146c920a4dd589a7e767f2d (diff)
[WATCHDOG] mpc83xx_wdt: add support for MPC86xx CPUs
On MPC86xx the watchdog could be enabled only at power-on-reset, and could not be disabled afterwards. We must ping the watchdog from the kernel until the userspace handles it. MPC83xx CPUs are only differ in a way that watchdog could be disabled once, but after it was enabled via software it becomes just the same as MPC86xx. Thus, to support MPC86xx I added the kernel timer which pings the watchdog until the userspace opens it. Since we implemented the timer, now we're able to implement proper handling for the CONFIG_WATCHDOG_NOWAYOUT case, for MPC83xx and MPC86xx. Also move the probe code into subsys_initcall, because we want start pinging the watchdog ASAP, and misc devices are available in subsys_initcall. Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com> Cc: Kumar Gala <galak@kernel.crashing.org> Signed-off-by: Wim Van Sebroeck <wim@iguana.be> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Diffstat (limited to 'drivers/watchdog/mpc83xx_wdt.c')
-rw-r--r--drivers/watchdog/mpc83xx_wdt.c80
1 files changed, 72 insertions, 8 deletions
diff --git a/drivers/watchdog/mpc83xx_wdt.c b/drivers/watchdog/mpc83xx_wdt.c
index 5f1b7bff8f12..fa82ec99ba81 100644
--- a/drivers/watchdog/mpc83xx_wdt.c
+++ b/drivers/watchdog/mpc83xx_wdt.c
@@ -1,10 +1,12 @@
1/* 1/*
2 * mpc83xx_wdt.c - MPC83xx watchdog userspace interface 2 * mpc83xx_wdt.c - MPC83xx/MPC86xx watchdog userspace interface
3 * 3 *
4 * Authors: Dave Updegraff <dave@cray.org> 4 * Authors: Dave Updegraff <dave@cray.org>
5 * Kumar Gala <galak@kernel.crashing.org> 5 * Kumar Gala <galak@kernel.crashing.org>
6 * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org> 6 * Attribution: from 83xx_wst: Florian Schirmer <jolt@tuxbox.org>
7 * ..and from sc520_wdt 7 * ..and from sc520_wdt
8 * Copyright (c) 2008 MontaVista Software, Inc.
9 * Anton Vorontsov <avorontsov@ru.mvista.com>
8 * 10 *
9 * Note: it appears that you can only actually ENABLE or DISABLE the thing 11 * Note: it appears that you can only actually ENABLE or DISABLE the thing
10 * once after POR. Once enabled, you cannot disable, and vice versa. 12 * once after POR. Once enabled, you cannot disable, and vice versa.
@@ -18,6 +20,7 @@
18#include <linux/fs.h> 20#include <linux/fs.h>
19#include <linux/init.h> 21#include <linux/init.h>
20#include <linux/kernel.h> 22#include <linux/kernel.h>
23#include <linux/timer.h>
21#include <linux/miscdevice.h> 24#include <linux/miscdevice.h>
22#include <linux/of_platform.h> 25#include <linux/of_platform.h>
23#include <linux/module.h> 26#include <linux/module.h>
@@ -39,6 +42,11 @@ struct mpc83xx_wdt {
39 u8 res2[0xF0]; 42 u8 res2[0xF0];
40}; 43};
41 44
45struct mpc83xx_wdt_type {
46 int prescaler;
47 bool hw_enabled;
48};
49
42static struct mpc83xx_wdt __iomem *wd_base; 50static struct mpc83xx_wdt __iomem *wd_base;
43 51
44static u16 timeout = 0xffff; 52static u16 timeout = 0xffff;
@@ -51,6 +59,11 @@ module_param(reset, bool, 0);
51MODULE_PARM_DESC(reset, 59MODULE_PARM_DESC(reset,
52 "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset"); 60 "Watchdog Interrupt/Reset Mode. 0 = interrupt, 1 = reset");
53 61
62static int nowayout = WATCHDOG_NOWAYOUT;
63module_param(nowayout, int, 0);
64MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
65 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
66
54/* 67/*
55 * We always prescale, but if someone really doesn't want to they can set this 68 * We always prescale, but if someone really doesn't want to they can set this
56 * to 0 69 * to 0
@@ -70,6 +83,22 @@ static void mpc83xx_wdt_keepalive(void)
70 spin_unlock(&wdt_spinlock); 83 spin_unlock(&wdt_spinlock);
71} 84}
72 85
86static void mpc83xx_wdt_timer_ping(unsigned long arg);
87static DEFINE_TIMER(wdt_timer, mpc83xx_wdt_timer_ping, 0, 0);
88
89static void mpc83xx_wdt_timer_ping(unsigned long arg)
90{
91 mpc83xx_wdt_keepalive();
92 /* We're pinging it twice faster than needed, just to be sure. */
93 mod_timer(&wdt_timer, jiffies + HZ * timeout_sec / 2);
94}
95
96static void mpc83xx_wdt_pr_warn(const char *msg)
97{
98 pr_crit("mpc83xx_wdt: %s, expect the %s soon!\n", msg,
99 reset ? "reset" : "machine check exception");
100}
101
73static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf, 102static ssize_t mpc83xx_wdt_write(struct file *file, const char __user *buf,
74 size_t count, loff_t *ppos) 103 size_t count, loff_t *ppos)
75{ 104{
@@ -85,7 +114,8 @@ static int mpc83xx_wdt_open(struct inode *inode, struct file *file)
85 return -EBUSY; 114 return -EBUSY;
86 115
87 /* Once we start the watchdog we can't stop it */ 116 /* Once we start the watchdog we can't stop it */
88 __module_get(THIS_MODULE); 117 if (nowayout)
118 __module_get(THIS_MODULE);
89 119
90 /* Good, fire up the show */ 120 /* Good, fire up the show */
91 if (prescale) 121 if (prescale)
@@ -97,13 +127,17 @@ static int mpc83xx_wdt_open(struct inode *inode, struct file *file)
97 127
98 out_be32(&wd_base->swcrr, tmp); 128 out_be32(&wd_base->swcrr, tmp);
99 129
130 del_timer_sync(&wdt_timer);
131
100 return nonseekable_open(inode, file); 132 return nonseekable_open(inode, file);
101} 133}
102 134
103static int mpc83xx_wdt_release(struct inode *inode, struct file *file) 135static int mpc83xx_wdt_release(struct inode *inode, struct file *file)
104{ 136{
105 printk(KERN_CRIT "Unexpected close, not stopping watchdog!\n"); 137 if (!nowayout)
106 mpc83xx_wdt_keepalive(); 138 mpc83xx_wdt_timer_ping(0);
139 else
140 mpc83xx_wdt_pr_warn("watchdog closed");
107 clear_bit(0, &wdt_is_open); 141 clear_bit(0, &wdt_is_open);
108 return 0; 142 return 0;
109} 143}
@@ -154,15 +188,25 @@ static int __devinit mpc83xx_wdt_probe(struct of_device *ofdev,
154 const struct of_device_id *match) 188 const struct of_device_id *match)
155{ 189{
156 int ret; 190 int ret;
191 struct device_node *np = ofdev->node;
192 struct mpc83xx_wdt_type *wdt_type = match->data;
157 u32 freq = fsl_get_sys_freq(); 193 u32 freq = fsl_get_sys_freq();
194 bool enabled;
158 195
159 if (!freq || freq == -1) 196 if (!freq || freq == -1)
160 return -EINVAL; 197 return -EINVAL;
161 198
162 wd_base = of_iomap(ofdev->node, 0); 199 wd_base = of_iomap(np, 0);
163 if (!wd_base) 200 if (!wd_base)
164 return -ENOMEM; 201 return -ENOMEM;
165 202
203 enabled = in_be32(&wd_base->swcrr) & SWCRR_SWEN;
204 if (!enabled && wdt_type->hw_enabled) {
205 pr_info("mpc83xx_wdt: could not be enabled in software\n");
206 ret = -ENOSYS;
207 goto err_unmap;
208 }
209
166 ret = misc_register(&mpc83xx_wdt_miscdev); 210 ret = misc_register(&mpc83xx_wdt_miscdev);
167 if (ret) { 211 if (ret) {
168 pr_err("cannot register miscdev on minor=%d (err=%d)\n", 212 pr_err("cannot register miscdev on minor=%d (err=%d)\n",
@@ -172,13 +216,21 @@ static int __devinit mpc83xx_wdt_probe(struct of_device *ofdev,
172 216
173 /* Calculate the timeout in seconds */ 217 /* Calculate the timeout in seconds */
174 if (prescale) 218 if (prescale)
175 timeout_sec = (timeout * 0x10000) / freq; 219 timeout_sec = (timeout * wdt_type->prescaler) / freq;
176 else 220 else
177 timeout_sec = timeout / freq; 221 timeout_sec = timeout / freq;
178 222
179 pr_info("WDT driver for MPC83xx initialized. mode:%s timeout=%d " 223 pr_info("WDT driver for MPC83xx initialized. mode:%s timeout=%d "
180 "(%d seconds)\n", reset ? "reset" : "interrupt", timeout, 224 "(%d seconds)\n", reset ? "reset" : "interrupt", timeout,
181 timeout_sec); 225 timeout_sec);
226
227 /*
228 * If the watchdog was previously enabled or we're running on
229 * MPC86xx, we should ping the wdt from the kernel until the
230 * userspace handles it.
231 */
232 if (enabled)
233 mpc83xx_wdt_timer_ping(0);
182 return 0; 234 return 0;
183err_unmap: 235err_unmap:
184 iounmap(wd_base); 236 iounmap(wd_base);
@@ -187,6 +239,8 @@ err_unmap:
187 239
188static int __devexit mpc83xx_wdt_remove(struct of_device *ofdev) 240static int __devexit mpc83xx_wdt_remove(struct of_device *ofdev)
189{ 241{
242 mpc83xx_wdt_pr_warn("watchdog removed");
243 del_timer_sync(&wdt_timer);
190 misc_deregister(&mpc83xx_wdt_miscdev); 244 misc_deregister(&mpc83xx_wdt_miscdev);
191 iounmap(wd_base); 245 iounmap(wd_base);
192 246
@@ -196,6 +250,16 @@ static int __devexit mpc83xx_wdt_remove(struct of_device *ofdev)
196static const struct of_device_id mpc83xx_wdt_match[] = { 250static const struct of_device_id mpc83xx_wdt_match[] = {
197 { 251 {
198 .compatible = "mpc83xx_wdt", 252 .compatible = "mpc83xx_wdt",
253 .data = &(struct mpc83xx_wdt_type) {
254 .prescaler = 0x10000,
255 },
256 },
257 {
258 .compatible = "fsl,mpc8610-wdt",
259 .data = &(struct mpc83xx_wdt_type) {
260 .prescaler = 0x10000,
261 .hw_enabled = true,
262 },
199 }, 263 },
200 {}, 264 {},
201}; 265};
@@ -221,10 +285,10 @@ static void __exit mpc83xx_wdt_exit(void)
221 of_unregister_platform_driver(&mpc83xx_wdt_driver); 285 of_unregister_platform_driver(&mpc83xx_wdt_driver);
222} 286}
223 287
224module_init(mpc83xx_wdt_init); 288subsys_initcall(mpc83xx_wdt_init);
225module_exit(mpc83xx_wdt_exit); 289module_exit(mpc83xx_wdt_exit);
226 290
227MODULE_AUTHOR("Dave Updegraff, Kumar Gala"); 291MODULE_AUTHOR("Dave Updegraff, Kumar Gala");
228MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx uProcessor"); 292MODULE_DESCRIPTION("Driver for watchdog timer in MPC83xx/MPC86xx uProcessors");
229MODULE_LICENSE("GPL"); 293MODULE_LICENSE("GPL");
230MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); 294MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);