2 * Watchdog implementation for GPI h/w found on PMC-Sierra RM9xxx
5 * Copyright (C) 2004 by Basler Vision Technologies AG
6 * Author: Thomas Koeller <thomas.koeller@baslerweb.com>
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.
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.
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
23 #include <linux/platform_device.h>
24 #include <linux/module.h>
25 #include <linux/moduleparam.h>
26 #include <linux/interrupt.h>
28 #include <linux/reboot.h>
29 #include <linux/miscdevice.h>
30 #include <linux/watchdog.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>
41 #define CLOCK 125000000
42 #define MAX_TIMEOUT_SECONDS 32
44 #define CPGIG1SR 0x0044
45 #define CPGIG1ER 0x0054
48 /* Function prototypes */
49 static irqreturn_t
wdt_gpi_irqhdl(int, void *, struct pt_regs
*);
50 static void wdt_gpi_set_timeout(unsigned int);
51 static int wdt_gpi_open(struct inode
*, struct file
*);
52 static int wdt_gpi_release(struct inode
*, struct file
*);
53 static ssize_t
wdt_gpi_write(struct file
*, const char __user
*, size_t, loff_t
*);
54 static long wdt_gpi_ioctl(struct file
*, unsigned int, unsigned long);
55 static const struct resource
*wdt_gpi_get_resource(struct platform_device
*, const char *, unsigned int);
56 static int wdt_gpi_notify(struct notifier_block
*, unsigned long, void *);
57 static int __init
wdt_gpi_probe(struct device
*);
58 static int __exit
wdt_gpi_remove(struct device
*);
61 static const char wdt_gpi_name
[] = "wdt_gpi";
62 static atomic_t opencnt
;
63 static int expect_close
;
64 static int locked
= 0;
67 /* These are set from device resources */
68 static void __iomem
* wd_regs
;
69 static unsigned int wd_irq
, wd_ctr
;
72 /* Module arguments */
73 static int timeout
= MAX_TIMEOUT_SECONDS
;
74 module_param(timeout
, int, 0444);
75 MODULE_PARM_DESC(timeout
, "Watchdog timeout in seconds");
77 static unsigned long resetaddr
= 0xbffdc200;
78 module_param(resetaddr
, ulong
, 0444);
79 MODULE_PARM_DESC(resetaddr
, "Address to write to to force a reset");
81 static unsigned long flagaddr
= 0xbffdc104;
82 module_param(flagaddr
, ulong
, 0444);
83 MODULE_PARM_DESC(flagaddr
, "Address to write to boot flags to");
85 static int powercycle
= 0;
86 module_param(powercycle
, bool, 0444);
87 MODULE_PARM_DESC(powercycle
, "Cycle power if watchdog expires");
89 static int nowayout
= WATCHDOG_NOWAYOUT
;
90 module_param(nowayout
, bool, 0444);
91 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be disabled once started");
94 /* Interrupt handler */
95 static irqreturn_t
wdt_gpi_irqhdl(int irq
, void *ctxt
, struct pt_regs
*regs
)
97 if (!unlikely(__raw_readl(wd_regs
+ 0x0008) & 0x1))
99 __raw_writel(0x1, wd_regs
+ 0x0008);
102 printk(KERN_WARNING
"%s: watchdog expired - resetting system\n",
105 *(volatile char *) flagaddr
|= 0x01;
106 *(volatile char *) resetaddr
= powercycle
? 0x01 : 0x2;
113 /* Watchdog functions */
114 static void wdt_gpi_start(void)
119 reg
= titan_readl(CPGIG1ER
);
120 titan_writel(reg
| (0x100 << wd_ctr
), CPGIG1ER
);
125 static void wdt_gpi_stop(void)
130 reg
= titan_readl(CPCCR
) & ~(0xf << (wd_ctr
* 4));
131 titan_writel(reg
, CPCCR
);
132 reg
= titan_readl(CPGIG1ER
);
133 titan_writel(reg
& ~(0x100 << wd_ctr
), CPGIG1ER
);
138 static void wdt_gpi_set_timeout(unsigned int to
)
141 const u32 wdval
= (to
* CLOCK
) & ~0x0000000f;
144 reg
= titan_readl(CPCCR
) & ~(0xf << (wd_ctr
* 4));
145 titan_writel(reg
, CPCCR
);
147 __raw_writel(wdval
, wd_regs
+ 0x0000);
149 titan_writel(reg
| (0x2 << (wd_ctr
* 4)), CPCCR
);
151 titan_writel(reg
| (0x5 << (wd_ctr
* 4)), CPCCR
);
157 /* /dev/watchdog operations */
158 static int wdt_gpi_open(struct inode
*i
, struct file
*f
)
162 if (unlikely(0 > atomic_dec_if_positive(&opencnt
)))
167 module_put(THIS_MODULE
);
168 free_irq(wd_irq
, &miscdev
);
172 res
= request_irq(wd_irq
, wdt_gpi_irqhdl
, SA_SHIRQ
| SA_INTERRUPT
,
173 wdt_gpi_name
, &miscdev
);
177 wdt_gpi_set_timeout(timeout
);
180 printk(KERN_INFO
"%s: watchdog started, timeout = %u seconds\n",
181 wdt_gpi_name
, timeout
);
185 static int wdt_gpi_release(struct inode
*i
, struct file
*f
)
188 printk(KERN_NOTICE
"%s: no way out - watchdog left running\n",
190 __module_get(THIS_MODULE
);
195 free_irq(wd_irq
, &miscdev
);
196 printk(KERN_INFO
"%s: watchdog stopped\n", wdt_gpi_name
);
198 printk(KERN_NOTICE
"%s: unexpected close() -"
199 " watchdog left running\n",
201 wdt_gpi_set_timeout(timeout
);
202 __module_get(THIS_MODULE
);
207 atomic_inc(&opencnt
);
212 wdt_gpi_write(struct file
*f
, const char __user
*d
, size_t s
, loff_t
*o
)
216 wdt_gpi_set_timeout(timeout
);
217 expect_close
= (s
> 0) && !get_user(val
, d
) && (val
== 'V');
222 wdt_gpi_ioctl(struct file
*f
, unsigned int cmd
, unsigned long arg
)
225 const long size
= _IOC_SIZE(cmd
);
227 static struct watchdog_info wdinfo
= {
228 .identity
= "RM9xxx/GPI watchdog",
229 .firmware_version
= 0,
230 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
233 if (unlikely(_IOC_TYPE(cmd
) != WATCHDOG_IOCTL_BASE
))
236 if ((_IOC_DIR(cmd
) & _IOC_READ
)
237 && !access_ok(VERIFY_WRITE
, arg
, size
))
240 if ((_IOC_DIR(cmd
) & _IOC_WRITE
)
241 && !access_ok(VERIFY_READ
, arg
, size
))
247 case WDIOC_GETSUPPORT
:
248 wdinfo
.options
= nowayout
?
249 WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
:
250 WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
;
251 res
= __copy_to_user((void __user
*)arg
, &wdinfo
, size
) ?
255 case WDIOC_GETSTATUS
:
258 case WDIOC_GETBOOTSTATUS
:
259 stat
= (*(volatile char *) flagaddr
& 0x01)
260 ? WDIOF_CARDRESET
: 0;
261 res
= __copy_to_user((void __user
*)arg
, &stat
, size
) ?
265 case WDIOC_SETOPTIONS
:
268 case WDIOC_KEEPALIVE
:
269 wdt_gpi_set_timeout(timeout
);
273 case WDIOC_SETTIMEOUT
:
276 if (unlikely(__copy_from_user(&val
, (const void __user
*) arg
,
285 wdt_gpi_set_timeout(val
);
287 printk("%s: timeout set to %u seconds\n",
288 wdt_gpi_name
, timeout
);
292 case WDIOC_GETTIMEOUT
:
293 res
= __copy_to_user((void __user
*) arg
, &timeout
, size
) ?
302 /* Shutdown notifier*/
304 wdt_gpi_notify(struct notifier_block
*this, unsigned long code
, void *unused
)
306 if (code
== SYS_DOWN
|| code
== SYS_HALT
)
313 /* Kernel interfaces */
314 static struct file_operations fops
= {
315 .owner
= THIS_MODULE
,
316 .open
= wdt_gpi_open
,
317 .release
= wdt_gpi_release
,
318 .write
= wdt_gpi_write
,
319 .unlocked_ioctl
= wdt_gpi_ioctl
,
322 static struct miscdevice miscdev
= {
323 .minor
= WATCHDOG_MINOR
,
324 .name
= wdt_gpi_name
,
328 static struct notifier_block wdt_gpi_shutdown
= {
329 .notifier_call
= wdt_gpi_notify
,
333 /* Init & exit procedures */
334 static const struct resource
*
335 wdt_gpi_get_resource(struct platform_device
*pdv
, const char *name
,
339 if (snprintf(buf
, sizeof buf
, "%s_0", name
) >= sizeof buf
)
341 return platform_get_resource_byname(pdv
, type
, buf
);
344 /* No hotplugging on the platform bus - use __init */
345 static int __init
wdt_gpi_probe(struct device
*dev
)
348 struct platform_device
* const pdv
= to_platform_device(dev
);
349 const struct resource
350 * const rr
= wdt_gpi_get_resource(pdv
, WDT_RESOURCE_REGS
,
352 * const ri
= wdt_gpi_get_resource(pdv
, WDT_RESOURCE_IRQ
,
354 * const rc
= wdt_gpi_get_resource(pdv
, WDT_RESOURCE_COUNTER
,
357 if (unlikely(!rr
|| !ri
|| !rc
))
360 wd_regs
= ioremap_nocache(rr
->start
, rr
->end
+ 1 - rr
->start
);
361 if (unlikely(!wd_regs
))
365 res
= misc_register(&miscdev
);
369 register_reboot_notifier(&wdt_gpi_shutdown
);
373 static int __exit
wdt_gpi_remove(struct device
*dev
)
377 unregister_reboot_notifier(&wdt_gpi_shutdown
);
378 res
= misc_deregister(&miscdev
);
385 /* Device driver init & exit */
386 static struct device_driver wdt_gpi_driver
= {
387 .name
= (char *) wdt_gpi_name
,
388 .bus
= &platform_bus_type
,
389 .owner
= THIS_MODULE
,
390 .probe
= wdt_gpi_probe
,
391 .remove
= __exit_p(wdt_gpi_remove
),
397 static int __init
wdt_gpi_init_module(void)
399 atomic_set(&opencnt
, 1);
400 if (timeout
> MAX_TIMEOUT_SECONDS
)
401 timeout
= MAX_TIMEOUT_SECONDS
;
402 return driver_register(&wdt_gpi_driver
);
405 static void __exit
wdt_gpi_cleanup_module(void)
407 driver_unregister(&wdt_gpi_driver
);
410 module_init(wdt_gpi_init_module
);
411 module_exit(wdt_gpi_cleanup_module
);
413 MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>");
414 MODULE_DESCRIPTION("Basler eXcite watchdog driver for gpi devices");
415 MODULE_VERSION("0.1");
416 MODULE_LICENSE("GPL");
417 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);