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/slab.h>
24 #include <linux/interrupt.h>
25 #include <linux/types.h>
26 #include <linux/watchdog.h>
27 #include <linux/uaccess.h>
30 #define WTCLK (0x01 << 10)
31 #define WTE (0x01 << 7) /*wdt enable*/
32 #define WTIS (0x03 << 4)
33 #define WTIF (0x01 << 3)
34 #define WTRF (0x01 << 2)
35 #define WTRE (0x01 << 1)
36 #define WTR (0x01 << 0)
38 * The watchdog time interval can be calculated via following formula:
39 * WTIS real time interval (formula)
40 * 0x00 ((2^ 14 ) * ((external crystal freq) / 256))seconds
41 * 0x01 ((2^ 16 ) * ((external crystal freq) / 256))seconds
42 * 0x02 ((2^ 18 ) * ((external crystal freq) / 256))seconds
43 * 0x03 ((2^ 20 ) * ((external crystal freq) / 256))seconds
45 * The external crystal freq is 15Mhz in the nuc900 evaluation board.
46 * So 0x00 = +-0.28 seconds, 0x01 = +-1.12 seconds, 0x02 = +-4.48 seconds,
47 * 0x03 = +- 16.92 seconds..
49 #define WDT_HW_TIMEOUT 0x02
50 #define WDT_TIMEOUT (HZ/2)
51 #define WDT_HEARTBEAT 15
53 static int heartbeat
= WDT_HEARTBEAT
;
54 module_param(heartbeat
, int, 0);
55 MODULE_PARM_DESC(heartbeat
, "Watchdog heartbeats in seconds. "
56 "(default = " __MODULE_STRING(WDT_HEARTBEAT
) ")");
58 static bool nowayout
= WATCHDOG_NOWAYOUT
;
59 module_param(nowayout
, bool, 0);
60 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started "
61 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
65 struct clk
*wdt_clock
;
66 struct platform_device
*pdev
;
67 void __iomem
*wdt_base
;
69 struct timer_list timer
;
71 unsigned long next_heartbeat
;
74 static unsigned long nuc900wdt_busy
;
75 static struct nuc900_wdt
*nuc900_wdt
;
77 static inline void nuc900_wdt_keepalive(void)
81 spin_lock(&nuc900_wdt
->wdt_lock
);
83 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
85 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
87 spin_unlock(&nuc900_wdt
->wdt_lock
);
90 static inline void nuc900_wdt_start(void)
94 spin_lock(&nuc900_wdt
->wdt_lock
);
96 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
97 val
|= (WTRE
| WTE
| WTR
| WTCLK
| WTIF
);
99 val
|= (WDT_HW_TIMEOUT
<< 0x04);
100 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
102 spin_unlock(&nuc900_wdt
->wdt_lock
);
104 nuc900_wdt
->next_heartbeat
= jiffies
+ heartbeat
* HZ
;
105 mod_timer(&nuc900_wdt
->timer
, jiffies
+ WDT_TIMEOUT
);
108 static inline void nuc900_wdt_stop(void)
112 del_timer(&nuc900_wdt
->timer
);
114 spin_lock(&nuc900_wdt
->wdt_lock
);
116 val
= __raw_readl(nuc900_wdt
->wdt_base
+ REG_WTCR
);
118 __raw_writel(val
, nuc900_wdt
->wdt_base
+ REG_WTCR
);
120 spin_unlock(&nuc900_wdt
->wdt_lock
);
123 static inline void nuc900_wdt_ping(void)
125 nuc900_wdt
->next_heartbeat
= jiffies
+ heartbeat
* HZ
;
128 static int nuc900_wdt_open(struct inode
*inode
, struct file
*file
)
131 if (test_and_set_bit(0, &nuc900wdt_busy
))
136 return nonseekable_open(inode
, file
);
139 static int nuc900_wdt_close(struct inode
*inode
, struct file
*file
)
141 if (nuc900_wdt
->expect_close
== 42)
144 dev_crit(&nuc900_wdt
->pdev
->dev
,
145 "Unexpected close, not stopping watchdog!\n");
149 nuc900_wdt
->expect_close
= 0;
150 clear_bit(0, &nuc900wdt_busy
);
154 static const struct watchdog_info nuc900_wdt_info
= {
155 .identity
= "nuc900 watchdog",
156 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
|
160 static long nuc900_wdt_ioctl(struct file
*file
,
161 unsigned int cmd
, unsigned long arg
)
163 void __user
*argp
= (void __user
*)arg
;
164 int __user
*p
= argp
;
168 case WDIOC_GETSUPPORT
:
169 return copy_to_user(argp
, &nuc900_wdt_info
,
170 sizeof(nuc900_wdt_info
)) ? -EFAULT
: 0;
171 case WDIOC_GETSTATUS
:
172 case WDIOC_GETBOOTSTATUS
:
173 return put_user(0, p
);
175 case WDIOC_KEEPALIVE
:
179 case WDIOC_SETTIMEOUT
:
180 if (get_user(new_value
, p
))
183 heartbeat
= new_value
;
186 return put_user(new_value
, p
);
187 case WDIOC_GETTIMEOUT
:
188 return put_user(heartbeat
, p
);
194 static ssize_t
nuc900_wdt_write(struct file
*file
, const char __user
*data
,
195 size_t len
, loff_t
*ppos
)
200 /* Scan for magic character */
204 nuc900_wdt
->expect_close
= 0;
206 for (i
= 0; i
< len
; i
++) {
208 if (get_user(c
, data
+ i
))
211 nuc900_wdt
->expect_close
= 42;
221 static void nuc900_wdt_timer_ping(unsigned long data
)
223 if (time_before(jiffies
, nuc900_wdt
->next_heartbeat
)) {
224 nuc900_wdt_keepalive();
225 mod_timer(&nuc900_wdt
->timer
, jiffies
+ WDT_TIMEOUT
);
227 dev_warn(&nuc900_wdt
->pdev
->dev
, "Will reset the machine !\n");
230 static const struct file_operations nuc900wdt_fops
= {
231 .owner
= THIS_MODULE
,
233 .unlocked_ioctl
= nuc900_wdt_ioctl
,
234 .open
= nuc900_wdt_open
,
235 .release
= nuc900_wdt_close
,
236 .write
= nuc900_wdt_write
,
239 static struct miscdevice nuc900wdt_miscdev
= {
240 .minor
= WATCHDOG_MINOR
,
242 .fops
= &nuc900wdt_fops
,
245 static int __devinit
nuc900wdt_probe(struct platform_device
*pdev
)
249 nuc900_wdt
= kzalloc(sizeof(struct nuc900_wdt
), GFP_KERNEL
);
253 nuc900_wdt
->pdev
= pdev
;
255 spin_lock_init(&nuc900_wdt
->wdt_lock
);
257 nuc900_wdt
->res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
258 if (nuc900_wdt
->res
== NULL
) {
259 dev_err(&pdev
->dev
, "no memory resource specified\n");
264 if (!request_mem_region(nuc900_wdt
->res
->start
,
265 resource_size(nuc900_wdt
->res
), pdev
->name
)) {
266 dev_err(&pdev
->dev
, "failed to get memory region\n");
271 nuc900_wdt
->wdt_base
= ioremap(nuc900_wdt
->res
->start
,
272 resource_size(nuc900_wdt
->res
));
273 if (nuc900_wdt
->wdt_base
== NULL
) {
274 dev_err(&pdev
->dev
, "failed to ioremap() region\n");
279 nuc900_wdt
->wdt_clock
= clk_get(&pdev
->dev
, NULL
);
280 if (IS_ERR(nuc900_wdt
->wdt_clock
)) {
281 dev_err(&pdev
->dev
, "failed to find watchdog clock source\n");
282 ret
= PTR_ERR(nuc900_wdt
->wdt_clock
);
286 clk_enable(nuc900_wdt
->wdt_clock
);
288 setup_timer(&nuc900_wdt
->timer
, nuc900_wdt_timer_ping
, 0);
290 ret
= misc_register(&nuc900wdt_miscdev
);
292 dev_err(&pdev
->dev
, "err register miscdev on minor=%d (%d)\n",
293 WATCHDOG_MINOR
, ret
);
300 clk_disable(nuc900_wdt
->wdt_clock
);
301 clk_put(nuc900_wdt
->wdt_clock
);
303 iounmap(nuc900_wdt
->wdt_base
);
305 release_mem_region(nuc900_wdt
->res
->start
,
306 resource_size(nuc900_wdt
->res
));
312 static int __devexit
nuc900wdt_remove(struct platform_device
*pdev
)
314 misc_deregister(&nuc900wdt_miscdev
);
316 clk_disable(nuc900_wdt
->wdt_clock
);
317 clk_put(nuc900_wdt
->wdt_clock
);
319 iounmap(nuc900_wdt
->wdt_base
);
321 release_mem_region(nuc900_wdt
->res
->start
,
322 resource_size(nuc900_wdt
->res
));
329 static struct platform_driver nuc900wdt_driver
= {
330 .probe
= nuc900wdt_probe
,
331 .remove
= __devexit_p(nuc900wdt_remove
),
333 .name
= "nuc900-wdt",
334 .owner
= THIS_MODULE
,
338 module_platform_driver(nuc900wdt_driver
);
340 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
341 MODULE_DESCRIPTION("Watchdog driver for NUC900");
342 MODULE_LICENSE("GPL");
343 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);
344 MODULE_ALIAS("platform:nuc900-wdt");