aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/watchdog/rm9k_wdt.c
diff options
context:
space:
mode:
authorThomas Koeller <thomas.koeller@baslerweb.com>2006-08-14 15:55:29 -0400
committerWim Van Sebroeck <wim@iguana.be>2006-11-17 14:45:24 -0500
commit825d3748c1b5f9272e4f9769f1c2da85174ece28 (patch)
tree42f1dadaeb33326f58a5af5c21347a20df016fbd /drivers/char/watchdog/rm9k_wdt.c
parente033351d5359f348d2913eb83fbb37023d8f21af (diff)
[WATCHDOG] MIPS RM9000 on-chip watchdog device
This is a driver for the on-chip watchdog device found on some MIPS RM9000 processors. Signed-off-by: Thomas Koeller <thomas.koeller@baslerweb.com> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/char/watchdog/rm9k_wdt.c')
-rw-r--r--drivers/char/watchdog/rm9k_wdt.c431
1 files changed, 431 insertions, 0 deletions
diff --git a/drivers/char/watchdog/rm9k_wdt.c b/drivers/char/watchdog/rm9k_wdt.c
new file mode 100644
index 000000000000..8cd12ab9bff4
--- /dev/null
+++ b/drivers/char/watchdog/rm9k_wdt.c
@@ -0,0 +1,431 @@
1/*
2 * Watchdog implementation for GPI h/w found on PMC-Sierra RM9xxx
3 * chips.
4 *
5 * Copyright (C) 2004 by Basler Vision Technologies AG
6 * Author: Thomas Koeller <thomas.koeller@baslerweb.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23#include <linux/platform_device.h>
24#include <linux/module.h>
25#include <linux/moduleparam.h>
26#include <linux/interrupt.h>
27#include <linux/fs.h>
28#include <linux/reboot.h>
29#include <linux/miscdevice.h>
30#include <linux/watchdog.h>
31#include <asm/io.h>
32#include <asm/atomic.h>
33#include <asm/processor.h>
34#include <asm/uaccess.h>
35#include <asm/system.h>
36#include <asm/rm9k-ocd.h>
37
38#include <rm9k_wdt.h>
39
40
41#define CLOCK 125000000
42#define MAX_TIMEOUT_SECONDS 32
43#define CPCCR 0x0080
44#define CPGIG1SR 0x0044
45#define CPGIG1ER 0x0054
46
47
48
49/* Function prototypes */
50static int __init wdt_gpi_probe(struct device *);
51static int __exit wdt_gpi_remove(struct device *);
52static void wdt_gpi_set_timeout(unsigned int);
53static int wdt_gpi_open(struct inode *, struct file *);
54static int wdt_gpi_release(struct inode *, struct file *);
55static ssize_t wdt_gpi_write(struct file *, const char __user *, size_t, loff_t *);
56static long wdt_gpi_ioctl(struct file *, unsigned int, unsigned long);
57static const struct resource *wdt_gpi_get_resource(struct platform_device *, const char *, unsigned int);
58static int wdt_gpi_notify(struct notifier_block *, unsigned long, void *);
59static irqreturn_t wdt_gpi_irqhdl(int, void *, struct pt_regs *);
60
61
62
63
64static const char wdt_gpi_name[] = "wdt_gpi";
65static atomic_t opencnt;
66static int expect_close;
67static int locked = 0;
68
69
70
71/* These are set from device resources */
72static void __iomem * wd_regs;
73static unsigned int wd_irq, wd_ctr;
74
75
76
77/* Module arguments */
78static int timeout = MAX_TIMEOUT_SECONDS;
79module_param(timeout, int, 0444);
80static unsigned long resetaddr = 0xbffdc200;
81module_param(resetaddr, ulong, 0444);
82static unsigned long flagaddr = 0xbffdc104;
83module_param(flagaddr, ulong, 0444);
84static int powercycle = 0;
85module_param(powercycle, bool, 0444);
86
87static int nowayout = WATCHDOG_NOWAYOUT;
88module_param(nowayout, bool, 0444);
89
90
91
92static struct file_operations fops = {
93 .owner = THIS_MODULE,
94 .open = wdt_gpi_open,
95 .release = wdt_gpi_release,
96 .write = wdt_gpi_write,
97 .unlocked_ioctl = wdt_gpi_ioctl,
98};
99
100static struct miscdevice miscdev = {
101 .minor = WATCHDOG_MINOR,
102 .name = wdt_gpi_name,
103 .fops = &fops,
104};
105
106static struct device_driver wdt_gpi_driver = {
107 .name = (char *) wdt_gpi_name,
108 .bus = &platform_bus_type,
109 .owner = THIS_MODULE,
110 .probe = wdt_gpi_probe,
111 .remove = __exit_p(wdt_gpi_remove),
112 .shutdown = NULL,
113 .suspend = NULL,
114 .resume = NULL,
115};
116
117static struct notifier_block wdt_gpi_shutdown = {
118 .notifier_call = wdt_gpi_notify,
119};
120
121
122
123static const struct resource *
124wdt_gpi_get_resource(struct platform_device *pdv, const char *name,
125 unsigned int type)
126{
127 char buf[80];
128 if (snprintf(buf, sizeof buf, "%s_0", name) >= sizeof buf)
129 return NULL;
130 return platform_get_resource_byname(pdv, type, buf);
131}
132
133
134
135/* No hotplugging on the platform bus - use __init */
136static int __init wdt_gpi_probe(struct device *dev)
137{
138 int res;
139 struct platform_device * const pdv = to_platform_device(dev);
140 const struct resource
141 * const rr = wdt_gpi_get_resource(pdv, WDT_RESOURCE_REGS,
142 IORESOURCE_MEM),
143 * const ri = wdt_gpi_get_resource(pdv, WDT_RESOURCE_IRQ,
144 IORESOURCE_IRQ),
145 * const rc = wdt_gpi_get_resource(pdv, WDT_RESOURCE_COUNTER,
146 0);
147
148 if (unlikely(!rr || !ri || !rc))
149 return -ENXIO;
150
151 wd_regs = ioremap_nocache(rr->start, rr->end + 1 - rr->start);
152 if (unlikely(!wd_regs))
153 return -ENOMEM;
154 wd_irq = ri->start;
155 wd_ctr = rc->start;
156 res = misc_register(&miscdev);
157 if (res)
158 iounmap(wd_regs);
159 else
160 register_reboot_notifier(&wdt_gpi_shutdown);
161 return res;
162}
163
164
165
166static int __exit wdt_gpi_remove(struct device *dev)
167{
168 int res;
169
170 unregister_reboot_notifier(&wdt_gpi_shutdown);
171 res = misc_deregister(&miscdev);
172 iounmap(wd_regs);
173 wd_regs = NULL;
174 return res;
175}
176
177
178static void wdt_gpi_set_timeout(unsigned int to)
179{
180 u32 reg;
181 const u32 wdval = (to * CLOCK) & ~0x0000000f;
182
183 lock_titan_regs();
184 reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4));
185 titan_writel(reg, CPCCR);
186 wmb();
187 __raw_writel(wdval, wd_regs + 0x0000);
188 wmb();
189 titan_writel(reg | (0x2 << (wd_ctr * 4)), CPCCR);
190 wmb();
191 titan_writel(reg | (0x5 << (wd_ctr * 4)), CPCCR);
192 iob();
193 unlock_titan_regs();
194}
195
196
197
198static int wdt_gpi_open(struct inode *i, struct file *f)
199{
200 int res;
201 u32 reg;
202
203 if (unlikely(0 > atomic_dec_if_positive(&opencnt)))
204 return -EBUSY;
205
206 expect_close = 0;
207 if (locked) {
208 module_put(THIS_MODULE);
209 free_irq(wd_irq, &miscdev);
210 locked = 0;
211 }
212
213 res = request_irq(wd_irq, wdt_gpi_irqhdl, SA_SHIRQ | SA_INTERRUPT,
214 wdt_gpi_name, &miscdev);
215 if (unlikely(res))
216 return res;
217
218 wdt_gpi_set_timeout(timeout);
219
220 lock_titan_regs();
221 reg = titan_readl(CPGIG1ER);
222 titan_writel(reg | (0x100 << wd_ctr), CPGIG1ER);
223 iob();
224 unlock_titan_regs();
225
226 printk(KERN_INFO "%s: watchdog started, timeout = %u seconds\n",
227 wdt_gpi_name, timeout);
228 return 0;
229}
230
231
232
233static int wdt_gpi_release(struct inode *i, struct file *f)
234{
235 if (nowayout) {
236 printk(KERN_NOTICE "%s: no way out - watchdog left running\n",
237 wdt_gpi_name);
238 __module_get(THIS_MODULE);
239 locked = 1;
240 } else {
241 if (expect_close) {
242 u32 reg;
243
244 lock_titan_regs();
245 reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4));
246 titan_writel(reg, CPCCR);
247 reg = titan_readl(CPGIG1ER);
248 titan_writel(reg & ~(0x100 << wd_ctr), CPGIG1ER);
249 iob();
250 unlock_titan_regs();
251 free_irq(wd_irq, &miscdev);
252 printk(KERN_INFO "%s: watchdog stopped\n", wdt_gpi_name);
253 } else {
254 printk(KERN_NOTICE "%s: unexpected close() -"
255 " watchdog left running\n",
256 wdt_gpi_name);
257 wdt_gpi_set_timeout(timeout);
258 __module_get(THIS_MODULE);
259 locked = 1;
260 }
261 }
262
263 atomic_inc(&opencnt);
264 return 0;
265}
266
267
268
269static ssize_t
270wdt_gpi_write(struct file *f, const char __user *d, size_t s, loff_t *o)
271{
272 char val;
273
274 wdt_gpi_set_timeout(timeout);
275 expect_close = (s > 0) && !get_user(val, d) && (val == 'V');
276 return s ? 1 : 0;
277}
278
279
280
281static long
282wdt_gpi_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
283{
284 long res = -ENOTTY;
285 const long size = _IOC_SIZE(cmd);
286 int stat;
287 static struct watchdog_info wdinfo = {
288 .identity = "RM9xxx/GPI watchdog",
289 .firmware_version = 0,
290 .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
291 };
292
293 if (unlikely(_IOC_TYPE(cmd) != WATCHDOG_IOCTL_BASE))
294 return -ENOTTY;
295
296 if ((_IOC_DIR(cmd) & _IOC_READ)
297 && !access_ok(VERIFY_WRITE, arg, size))
298 return -EFAULT;
299
300 if ((_IOC_DIR(cmd) & _IOC_WRITE)
301 && !access_ok(VERIFY_READ, arg, size))
302 return -EFAULT;
303
304 expect_close = 0;
305
306 switch (cmd) {
307 case WDIOC_GETSUPPORT:
308 wdinfo.options = nowayout ?
309 WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING :
310 WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE;
311 res = __copy_to_user((void __user *)arg, &wdinfo, size) ?
312 -EFAULT : size;
313 break;
314
315 case WDIOC_GETSTATUS:
316 break;
317
318 case WDIOC_GETBOOTSTATUS:
319 stat = (*(volatile char *) flagaddr & 0x01)
320 ? WDIOF_CARDRESET : 0;
321 res = __copy_to_user((void __user *)arg, &stat, size) ?
322 -EFAULT : size;
323 break;
324
325 case WDIOC_SETOPTIONS:
326 break;
327
328 case WDIOC_KEEPALIVE:
329 wdt_gpi_set_timeout(timeout);
330 res = size;
331 break;
332
333 case WDIOC_SETTIMEOUT:
334 {
335 int val;
336 if (unlikely(__copy_from_user(&val, (const void __user *) arg,
337 size))) {
338 res = -EFAULT;
339 break;
340 }
341
342 if (val > 32)
343 val = 32;
344 timeout = val;
345 wdt_gpi_set_timeout(val);
346 res = size;
347 printk("%s: timeout set to %u seconds\n",
348 wdt_gpi_name, timeout);
349 }
350 break;
351
352 case WDIOC_GETTIMEOUT:
353 res = __copy_to_user((void __user *) arg, &timeout, size) ?
354 -EFAULT : size;
355 break;
356 }
357
358 return res;
359}
360
361
362
363
364static irqreturn_t wdt_gpi_irqhdl(int irq, void *ctxt, struct pt_regs *regs)
365{
366 if (!unlikely(__raw_readl(wd_regs + 0x0008) & 0x1))
367 return IRQ_NONE;
368 __raw_writel(0x1, wd_regs + 0x0008);
369
370
371 printk(KERN_WARNING "%s: watchdog expired - resetting system\n",
372 wdt_gpi_name);
373
374 *(volatile char *) flagaddr |= 0x01;
375 *(volatile char *) resetaddr = powercycle ? 0x01 : 0x2;
376 iob();
377 while (1)
378 cpu_relax();
379}
380
381
382
383static int
384wdt_gpi_notify(struct notifier_block *this, unsigned long code, void *unused)
385{
386 if(code == SYS_DOWN || code == SYS_HALT) {
387 u32 reg;
388
389 lock_titan_regs();
390 reg = titan_readl(CPCCR) & ~(0xf << (wd_ctr * 4));
391 titan_writel(reg, CPCCR);
392 reg = titan_readl(CPGIG1ER);
393 titan_writel(reg & ~(0x100 << wd_ctr), CPGIG1ER);
394 iob();
395 unlock_titan_regs();
396 }
397 return NOTIFY_DONE;
398}
399
400
401
402static int __init wdt_gpi_init_module(void)
403{
404 atomic_set(&opencnt, 1);
405 if (timeout > MAX_TIMEOUT_SECONDS)
406 timeout = MAX_TIMEOUT_SECONDS;
407 return driver_register(&wdt_gpi_driver);
408}
409
410
411
412static void __exit wdt_gpi_cleanup_module(void)
413{
414 driver_unregister(&wdt_gpi_driver);
415}
416
417module_init(wdt_gpi_init_module);
418module_exit(wdt_gpi_cleanup_module);
419
420
421
422MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
423MODULE_DESCRIPTION("Basler eXcite watchdog driver for gpi devices");
424MODULE_VERSION("0.1");
425MODULE_LICENSE("GPL");
426MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
427MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds");
428MODULE_PARM_DESC(resetaddr, "Address to write to to force a reset");
429MODULE_PARM_DESC(flagaddr, "Address to write to boot flags to");
430MODULE_PARM_DESC(nowayout, "Watchdog cannot be disabled once started");
431MODULE_PARM_DESC(powercycle, "Cycle power if watchdog expires");