2 * Copyright (c) 2009 Nuvoton technology corporation.
4 * Wan ZongShun <mcuos.com@gmail.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
12 #include <linux/bitops.h>
13 #include <linux/errno.h>
15 #include <linux/init.h>
17 #include <linux/clk.h>
18 #include <linux/kernel.h>
19 #include <linux/miscdevice.h>
20 #include <linux/module.h>
21 #include <linux/moduleparam.h>
22 #include <linux/platform_device.h>
23 #include <linux/interrupt.h>
24 #include <linux/types.h>
25 #include <linux/watchdog.h>
26 #include <linux/uaccess.h>
29 #define WTCLK (0x01 << 10)
30 #define WTE (0x01 << 7) /*wdt enable*/
31 #define WTIS (0x03 << 4)
32 #define WTIF (0x01 << 3)
33 #define WTRF (0x01 << 2)
34 #define WTRE (0x01 << 1)
35 #define WTR (0x01 << 0)
37 * The watchdog time interval can be calculated via following formula:
38 * WTIS real time interval (formula)
39 * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
40 * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
41 * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
42 * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
44 * The external crystal freq is 15Mhz in the nuc900 evaluation board.
45 * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
46 * 0x03 = +- 16.92 seconds..
48 #define WDT_HW_TIMEOUT 0x02
49 #define WDT_TIMEOUT (HZ/2)
50 #define WDT_HEARTBEAT 15
52 static int heartbeat
= WDT_HEARTBEAT
;
53 module_param(heartbeat
, int, 0);
54 MODULE_PARM_DESC(heartbeat
, "Watchdog heartbeats in seconds. "
55 "(default = " __MODULE_STRING(WDT_HEARTBEAT
) ")");
57 static int nowayout
= WATCHDOG_NOWAYOUT
;
58 module_param(nowayout
, int, 0);
59 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started "
60 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
64 struct clk
*wdt_clock
;
65 struct platform_device
*pdev
;
66 void __iomem
*wdt_base
;
68 struct timer_list timer
;
70 unsigned long next_heartbeat
;
73 static unsigned long nuc900wdt_busy
;
74 struct nuc900_wdt
*nuc900_wdt
;
76 static inline void nuc900_wdt_keepalive(void)
80 spin_lock(&nuc900_wdt
->wdt_lock
);
82 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
84 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
86 spin_unlock(&nuc900_wdt
->wdt_lock
);
89 static inline void nuc900_wdt_start(void)
93 spin_lock(&nuc900_wdt
->wdt_lock
);
95 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
96 val
|= (WTRE
| WTE
| WTR
| WTCLK
| WTIF
);
98 val
|= (WDT_HW_TIMEOUT
<< 0x04);
99 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
101 spin_unlock(&nuc900_wdt
->wdt_lock
);
103 nuc900_wdt
->next_heartbeat
= jiffies
+ heartbeat
* HZ
;
104 mod_timer(&nuc900_wdt
->timer
, jiffies
+ WDT_TIMEOUT
);
107 static inline void nuc900_wdt_stop(void)
111 del_timer(&nuc900_wdt
->timer
);
113 spin_lock(&nuc900_wdt
->wdt_lock
);
115 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
117 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
119 spin_unlock(&nuc900_wdt
->wdt_lock
);
122 static inline void nuc900_wdt_ping(void)
124 nuc900_wdt
->next_heartbeat
= jiffies
+ heartbeat
* HZ
;
127 static int nuc900_wdt_open(struct inode
*inode
, struct file
*file
)
130 if (test_and_set_bit(0, &nuc900wdt_busy
))
135 return nonseekable_open(inode
, file
);
138 static int nuc900_wdt_close(struct inode
*inode
, struct file
*file
)
140 if (nuc900_wdt
->expect_close
== 42)
143 dev_crit(&nuc900_wdt
->pdev
->dev
,
144 "Unexpected close, not stopping watchdog!\n");
148 nuc900_wdt
->expect_close
= 0;
149 clear_bit(0, &nuc900wdt_busy
);
153 static const struct watchdog_info nuc900_wdt_info
= {
154 .identity
= "nuc900 watchdog",
155 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
|
159 static long nuc900_wdt_ioctl(struct file
*file
,
160 unsigned int cmd
, unsigned long arg
)
162 void __user
*argp
= (void __user
*)arg
;
163 int __user
*p
= argp
;
167 case WDIOC_GETSUPPORT
:
168 return copy_to_user(argp
, &nuc900_wdt_info
,
169 sizeof(nuc900_wdt_info
)) ? -EFAULT
: 0;
170 case WDIOC_GETSTATUS
:
171 case WDIOC_GETBOOTSTATUS
:
172 return put_user(0, p
);
174 case WDIOC_KEEPALIVE
:
178 case WDIOC_SETTIMEOUT
:
179 if (get_user(new_value
, p
))
182 heartbeat
= new_value
;
185 return put_user(new_value
, p
);
186 case WDIOC_GETTIMEOUT
:
187 return put_user(heartbeat
, p
);
193 static ssize_t
nuc900_wdt_write(struct file
*file
, const char __user
*data
,
194 size_t len
, loff_t
*ppos
)
199 /* Scan for magic character */
203 nuc900_wdt
->expect_close
= 0;
205 for (i
= 0; i
< len
; i
++) {
207 if (get_user(c
, data
+ i
))
210 nuc900_wdt
->expect_close
= 42;
220 static void nuc900_wdt_timer_ping(unsigned long data
)
222 if (time_before(jiffies
, nuc900_wdt
->next_heartbeat
)) {
223 nuc900_wdt_keepalive();
224 mod_timer(&nuc900_wdt
->timer
, jiffies
+ WDT_TIMEOUT
);
226 dev_warn(&nuc900_wdt
->pdev
->dev
, "Will reset the machine !\n");
229 static const struct file_operations nuc900wdt_fops
= {
230 .owner
= THIS_MODULE
,
232 .unlocked_ioctl
= nuc900_wdt_ioctl
,
233 .open
= nuc900_wdt_open
,
234 .release
= nuc900_wdt_close
,
235 .write
= nuc900_wdt_write
,
238 static struct miscdevice nuc900wdt_miscdev
= {
239 .minor
= WATCHDOG_MINOR
,
241 .fops
= &nuc900wdt_fops
,
244 static int __devinit
nuc900wdt_probe(struct platform_device
*pdev
)
248 nuc900_wdt
= kzalloc(sizeof(struct nuc900_wdt
), GFP_KERNEL
);
252 nuc900_wdt
->pdev
= pdev
;
254 spin_lock_init(&nuc900_wdt
->wdt_lock
);
256 nuc900_wdt
->res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
257 if (nuc900_wdt
->res
== NULL
) {
258 dev_err(&pdev
->dev
, "no memory resource specified\n");
263 if (!request_mem_region(nuc900_wdt
->res
->start
,
264 resource_size(nuc900_wdt
->res
), pdev
->name
)) {
265 dev_err(&pdev
->dev
, "failed to get memory region\n");
270 nuc900_wdt
->wdt_base
= ioremap(nuc900_wdt
->res
->start
,
271 resource_size(nuc900_wdt
->res
));
272 if (nuc900_wdt
->wdt_base
== NULL
) {
273 dev_err(&pdev
->dev
, "failed to ioremap() region\n");
278 nuc900_wdt
->wdt_clock
= clk_get(&pdev
->dev
, NULL
);
279 if (IS_ERR(nuc900_wdt
->wdt_clock
)) {
280 dev_err(&pdev
->dev
, "failed to find watchdog clock source\n");
281 ret
= PTR_ERR(nuc900_wdt
->wdt_clock
);
285 clk_enable(nuc900_wdt
->wdt_clock
);
287 setup_timer(&nuc900_wdt
->timer
, nuc900_wdt_timer_ping
, 0);
289 if (misc_register(&nuc900wdt_miscdev
)) {
290 dev_err(&pdev
->dev
, "err register miscdev on minor=%d (%d)\n",
291 WATCHDOG_MINOR
, ret
);
298 clk_disable(nuc900_wdt
->wdt_clock
);
299 clk_put(nuc900_wdt
->wdt_clock
);
301 iounmap(nuc900_wdt
->wdt_base
);
303 release_mem_region(nuc900_wdt
->res
->start
,
304 resource_size(nuc900_wdt
->res
));
310 static int __devexit
nuc900wdt_remove(struct platform_device
*pdev
)
312 misc_deregister(&nuc900wdt_miscdev
);
314 clk_disable(nuc900_wdt
->wdt_clock
);
315 clk_put(nuc900_wdt
->wdt_clock
);
317 iounmap(nuc900_wdt
->wdt_base
);
319 release_mem_region(nuc900_wdt
->res
->start
,
320 resource_size(nuc900_wdt
->res
));
327 static struct platform_driver nuc900wdt_driver
= {
328 .probe
= nuc900wdt_probe
,
329 .remove
= __devexit_p(nuc900wdt_remove
),
331 .name
= "nuc900-wdt",
332 .owner
= THIS_MODULE
,
336 static int __init
nuc900_wdt_init(void)
338 return platform_driver_register(&nuc900wdt_driver
);
341 static void __exit
nuc900_wdt_exit(void)
343 platform_driver_unregister(&nuc900wdt_driver
);
346 module_init(nuc900_wdt_init
);
347 module_exit(nuc900_wdt_exit
);
349 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
350 MODULE_DESCRIPTION("Watchdog driver for NUC900");
351 MODULE_LICENSE("GPL");
352 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);
353 MODULE_ALIAS("platform:nuc900-wdt");