2 * Copyright 2010-2011 Picochip Ltd., Jamie Iles
3 * http://www.picochip.com
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
10 * This file implements a driver for the Synopsys DesignWare watchdog device
11 * in the many ARM subsystems. The watchdog has 16 different timeout periods
12 * and these are a function of the input clock frequency.
14 * The DesignWare watchdog cannot be stopped once it has been started so we
15 * use a software timer to implement a ping that will keep the watchdog alive.
16 * If we receive an expected close for the watchdog then we keep the timer
17 * running, otherwise the timer is stopped and the watchdog will expire.
20 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22 #include <linux/bitops.h>
23 #include <linux/clk.h>
24 #include <linux/device.h>
25 #include <linux/err.h>
28 #include <linux/kernel.h>
29 #include <linux/miscdevice.h>
30 #include <linux/module.h>
31 #include <linux/moduleparam.h>
33 #include <linux/platform_device.h>
34 #include <linux/spinlock.h>
35 #include <linux/timer.h>
36 #include <linux/uaccess.h>
37 #include <linux/watchdog.h>
39 #define WDOG_CONTROL_REG_OFFSET 0x00
40 #define WDOG_CONTROL_REG_WDT_EN_MASK 0x01
41 #define WDOG_TIMEOUT_RANGE_REG_OFFSET 0x04
42 #define WDOG_CURRENT_COUNT_REG_OFFSET 0x08
43 #define WDOG_COUNTER_RESTART_REG_OFFSET 0x0c
44 #define WDOG_COUNTER_RESTART_KICK_VALUE 0x76
46 /* The maximum TOP (timeout period) value that can be set in the watchdog. */
47 #define DW_WDT_MAX_TOP 15
49 static bool nowayout
= WATCHDOG_NOWAYOUT
;
50 module_param(nowayout
, bool, 0);
51 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started "
52 "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
54 #define WDT_TIMEOUT (HZ / 2)
61 unsigned long next_heartbeat
;
62 struct timer_list timer
;
66 static inline int dw_wdt_is_enabled(void)
68 return readl(dw_wdt
.regs
+ WDOG_CONTROL_REG_OFFSET
) &
69 WDOG_CONTROL_REG_WDT_EN_MASK
;
72 static inline int dw_wdt_top_in_seconds(unsigned top
)
75 * There are 16 possible timeout values in 0..15 where the number of
76 * cycles is 2 ^ (16 + i) and the watchdog counts down.
78 return (1 << (16 + top
)) / clk_get_rate(dw_wdt
.clk
);
81 static int dw_wdt_get_top(void)
83 int top
= readl(dw_wdt
.regs
+ WDOG_TIMEOUT_RANGE_REG_OFFSET
) & 0xF;
85 return dw_wdt_top_in_seconds(top
);
88 static inline void dw_wdt_set_next_heartbeat(void)
90 dw_wdt
.next_heartbeat
= jiffies
+ dw_wdt_get_top() * HZ
;
93 static int dw_wdt_set_top(unsigned top_s
)
95 int i
, top_val
= DW_WDT_MAX_TOP
;
98 * Iterate over the timeout values until we find the closest match. We
101 for (i
= 0; i
<= DW_WDT_MAX_TOP
; ++i
)
102 if (dw_wdt_top_in_seconds(i
) >= top_s
) {
107 /* Set the new value in the watchdog. */
108 writel(top_val
, dw_wdt
.regs
+ WDOG_TIMEOUT_RANGE_REG_OFFSET
);
110 dw_wdt_set_next_heartbeat();
112 return dw_wdt_top_in_seconds(top_val
);
115 static void dw_wdt_keepalive(void)
117 writel(WDOG_COUNTER_RESTART_KICK_VALUE
, dw_wdt
.regs
+
118 WDOG_COUNTER_RESTART_REG_OFFSET
);
121 static void dw_wdt_ping(unsigned long data
)
123 if (time_before(jiffies
, dw_wdt
.next_heartbeat
) ||
124 (!nowayout
&& !dw_wdt
.in_use
)) {
126 mod_timer(&dw_wdt
.timer
, jiffies
+ WDT_TIMEOUT
);
128 pr_crit("keepalive missed, machine will reset\n");
131 static int dw_wdt_open(struct inode
*inode
, struct file
*filp
)
133 if (test_and_set_bit(0, &dw_wdt
.in_use
))
136 /* Make sure we don't get unloaded. */
137 __module_get(THIS_MODULE
);
139 spin_lock(&dw_wdt
.lock
);
140 if (!dw_wdt_is_enabled()) {
142 * The watchdog is not currently enabled. Set the timeout to
143 * the maximum and then start it.
145 dw_wdt_set_top(DW_WDT_MAX_TOP
);
146 writel(WDOG_CONTROL_REG_WDT_EN_MASK
,
147 dw_wdt
.regs
+ WDOG_CONTROL_REG_OFFSET
);
150 dw_wdt_set_next_heartbeat();
152 spin_unlock(&dw_wdt
.lock
);
154 return nonseekable_open(inode
, filp
);
157 ssize_t
dw_wdt_write(struct file
*filp
, const char __user
*buf
, size_t len
,
166 dw_wdt
.expect_close
= 0;
168 for (i
= 0; i
< len
; ++i
) {
171 if (get_user(c
, buf
+ i
))
175 dw_wdt
.expect_close
= 1;
181 dw_wdt_set_next_heartbeat();
182 mod_timer(&dw_wdt
.timer
, jiffies
+ WDT_TIMEOUT
);
187 static u32
dw_wdt_time_left(void)
189 return readl(dw_wdt
.regs
+ WDOG_CURRENT_COUNT_REG_OFFSET
) /
190 clk_get_rate(dw_wdt
.clk
);
193 static const struct watchdog_info dw_wdt_ident
= {
194 .options
= WDIOF_KEEPALIVEPING
| WDIOF_SETTIMEOUT
|
196 .identity
= "Synopsys DesignWare Watchdog",
199 static long dw_wdt_ioctl(struct file
*filp
, unsigned int cmd
, unsigned long arg
)
205 case WDIOC_GETSUPPORT
:
206 return copy_to_user((struct watchdog_info
*)arg
, &dw_wdt_ident
,
207 sizeof(dw_wdt_ident
)) ? -EFAULT
: 0;
209 case WDIOC_GETSTATUS
:
210 case WDIOC_GETBOOTSTATUS
:
211 return put_user(0, (int *)arg
);
213 case WDIOC_KEEPALIVE
:
214 dw_wdt_set_next_heartbeat();
217 case WDIOC_SETTIMEOUT
:
218 if (get_user(val
, (int __user
*)arg
))
220 timeout
= dw_wdt_set_top(val
);
221 return put_user(timeout
, (int __user
*)arg
);
223 case WDIOC_GETTIMEOUT
:
224 return put_user(dw_wdt_get_top(), (int __user
*)arg
);
226 case WDIOC_GETTIMELEFT
:
227 /* Get the time left until expiry. */
228 if (get_user(val
, (int __user
*)arg
))
230 return put_user(dw_wdt_time_left(), (int __user
*)arg
);
237 static int dw_wdt_release(struct inode
*inode
, struct file
*filp
)
239 clear_bit(0, &dw_wdt
.in_use
);
241 if (!dw_wdt
.expect_close
) {
242 del_timer(&dw_wdt
.timer
);
245 pr_crit("unexpected close, system will reboot soon\n");
247 pr_crit("watchdog cannot be disabled, system will reboot soon\n");
250 dw_wdt
.expect_close
= 0;
256 static int dw_wdt_suspend(struct device
*dev
)
258 clk_disable(dw_wdt
.clk
);
263 static int dw_wdt_resume(struct device
*dev
)
265 int err
= clk_enable(dw_wdt
.clk
);
275 static const struct dev_pm_ops dw_wdt_pm_ops
= {
276 .suspend
= dw_wdt_suspend
,
277 .resume
= dw_wdt_resume
,
279 #endif /* CONFIG_PM */
281 static const struct file_operations wdt_fops
= {
282 .owner
= THIS_MODULE
,
285 .write
= dw_wdt_write
,
286 .unlocked_ioctl
= dw_wdt_ioctl
,
287 .release
= dw_wdt_release
290 static struct miscdevice dw_wdt_miscdev
= {
293 .minor
= WATCHDOG_MINOR
,
296 static int dw_wdt_drv_probe(struct platform_device
*pdev
)
299 struct resource
*mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
304 dw_wdt
.regs
= devm_request_and_ioremap(&pdev
->dev
, mem
);
308 dw_wdt
.clk
= clk_get(&pdev
->dev
, NULL
);
309 if (IS_ERR(dw_wdt
.clk
))
310 return PTR_ERR(dw_wdt
.clk
);
312 ret
= clk_enable(dw_wdt
.clk
);
316 spin_lock_init(&dw_wdt
.lock
);
318 ret
= misc_register(&dw_wdt_miscdev
);
320 goto out_disable_clk
;
322 dw_wdt_set_next_heartbeat();
323 setup_timer(&dw_wdt
.timer
, dw_wdt_ping
, 0);
324 mod_timer(&dw_wdt
.timer
, jiffies
+ WDT_TIMEOUT
);
329 clk_disable(dw_wdt
.clk
);
336 static int dw_wdt_drv_remove(struct platform_device
*pdev
)
338 misc_deregister(&dw_wdt_miscdev
);
340 clk_disable(dw_wdt
.clk
);
346 static struct platform_driver dw_wdt_driver
= {
347 .probe
= dw_wdt_drv_probe
,
348 .remove
= dw_wdt_drv_remove
,
351 .owner
= THIS_MODULE
,
353 .pm
= &dw_wdt_pm_ops
,
354 #endif /* CONFIG_PM */
358 module_platform_driver(dw_wdt_driver
);
360 MODULE_AUTHOR("Jamie Iles");
361 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");
362 MODULE_LICENSE("GPL");
363 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR
);