aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/watchdog
diff options
context:
space:
mode:
authorWolfram Sang <w.sang@pengutronix.de>2010-04-29 04:03:17 -0400
committerWim Van Sebroeck <wim@iguana.be>2010-05-25 08:43:46 -0400
commitbb2fd8a844d3a9209599b5fb694b30ac46a56ef0 (patch)
tree865ffef13864e8f88974f0d79b7feb447db7b7b6 /drivers/watchdog
parent100fb76f0acfebcb7c72875890b9ef33ff04dc2b (diff)
watchdog: Driver for the watchdog timer on Freescale IMX2 (and later) processors.
This is the driver for the hardware watchdog on the Freescale IMX2 and later processors. Signed-off-by: Wolfram Sang <w.sang@pengutronix.de> Cc: Vladimir Zapolskiy <vzapolskiy@gmail.com> Cc: Sascha Hauer <s.hauer@pengutronix.de> Tested-by: Juergen Beisert <jbe@pengutronix.de> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Diffstat (limited to 'drivers/watchdog')
-rw-r--r--drivers/watchdog/Kconfig12
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/imx2_wdt.c358
3 files changed, 371 insertions, 0 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index c57ecffe0085..afcfacc9bbe2 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -312,6 +312,18 @@ config MAX63XX_WATCHDOG
312 help 312 help
313 Support for memory mapped max63{69,70,71,72,73,74} watchdog timer. 313 Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
314 314
315config IMX2_WDT
316 tristate "IMX2+ Watchdog"
317 depends on ARCH_MX2 || ARCH_MX25 || ARCH_MX3 || ARCH_MX5
318 help
319 This is the driver for the hardware watchdog
320 on the Freescale IMX2 and later processors.
321 If you have one of these processors and wish to have
322 watchdog support enabled, say Y, otherwise say N.
323
324 To compile this driver as a module, choose M here: the
325 module will be called imx2_wdt.
326
315# AVR32 Architecture 327# AVR32 Architecture
316 328
317config AT32AP700X_WDT 329config AT32AP700X_WDT
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5e3cb95bb0e9..72f3e2073f8e 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
47obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o 47obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
48obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o 48obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
49obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o 49obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
50obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
50 51
51# AVR32 Architecture 52# AVR32 Architecture
52obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o 53obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o
diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
new file mode 100644
index 000000000000..ea25885781bb
--- /dev/null
+++ b/drivers/watchdog/imx2_wdt.c
@@ -0,0 +1,358 @@
1/*
2 * Watchdog driver for IMX2 and later processors
3 *
4 * Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <w.sang@pengutronix.de>
5 *
6 * some parts adapted by similar drivers from Darius Augulis and Vladimir
7 * Zapolskiy, additional improvements by Wim Van Sebroeck.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License version 2 as published by
11 * the Free Software Foundation.
12 *
13 * NOTE: MX1 has a slightly different Watchdog than MX2 and later:
14 *
15 * MX1: MX2+:
16 * ---- -----
17 * Registers: 32-bit 16-bit
18 * Stopable timer: Yes No
19 * Need to enable clk: No Yes
20 * Halt on suspend: Manual Can be automatic
21 */
22
23#include <linux/init.h>
24#include <linux/kernel.h>
25#include <linux/miscdevice.h>
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/platform_device.h>
29#include <linux/watchdog.h>
30#include <linux/clk.h>
31#include <linux/fs.h>
32#include <linux/io.h>
33#include <linux/uaccess.h>
34#include <linux/timer.h>
35#include <linux/jiffies.h>
36#include <mach/hardware.h>
37
38#define DRIVER_NAME "imx2-wdt"
39
40#define IMX2_WDT_WCR 0x00 /* Control Register */
41#define IMX2_WDT_WCR_WT (0xFF << 8) /* -> Watchdog Timeout Field */
42#define IMX2_WDT_WCR_WRE (1 << 3) /* -> WDOG Reset Enable */
43#define IMX2_WDT_WCR_WDE (1 << 2) /* -> Watchdog Enable */
44
45#define IMX2_WDT_WSR 0x02 /* Service Register */
46#define IMX2_WDT_SEQ1 0x5555 /* -> service sequence 1 */
47#define IMX2_WDT_SEQ2 0xAAAA /* -> service sequence 2 */
48
49#define IMX2_WDT_MAX_TIME 128
50#define IMX2_WDT_DEFAULT_TIME 60 /* in seconds */
51
52#define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
53
54#define IMX2_WDT_STATUS_OPEN 0
55#define IMX2_WDT_STATUS_STARTED 1
56#define IMX2_WDT_EXPECT_CLOSE 2
57
58static struct {
59 struct clk *clk;
60 void __iomem *base;
61 unsigned timeout;
62 unsigned long status;
63 struct timer_list timer; /* Pings the watchdog when closed */
64} imx2_wdt;
65
66static struct miscdevice imx2_wdt_miscdev;
67
68static int nowayout = WATCHDOG_NOWAYOUT;
69module_param(nowayout, int, 0);
70MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
71 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
72
73
74static unsigned timeout = IMX2_WDT_DEFAULT_TIME;
75module_param(timeout, uint, 0);
76MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
77 __MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")");
78
79static const struct watchdog_info imx2_wdt_info = {
80 .identity = "imx2+ watchdog",
81 .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
82};
83
84static inline void imx2_wdt_setup(void)
85{
86 u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
87
88 /* Strip the old watchdog Time-Out value */
89 val &= ~IMX2_WDT_WCR_WT;
90 /* Generate reset if WDOG times out */
91 val &= ~IMX2_WDT_WCR_WRE;
92 /* Keep Watchdog Disabled */
93 val &= ~IMX2_WDT_WCR_WDE;
94 /* Set the watchdog's Time-Out value */
95 val |= WDOG_SEC_TO_COUNT(imx2_wdt.timeout);
96
97 __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
98
99 /* enable the watchdog */
100 val |= IMX2_WDT_WCR_WDE;
101 __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
102}
103
104static inline void imx2_wdt_ping(void)
105{
106 __raw_writew(IMX2_WDT_SEQ1, imx2_wdt.base + IMX2_WDT_WSR);
107 __raw_writew(IMX2_WDT_SEQ2, imx2_wdt.base + IMX2_WDT_WSR);
108}
109
110static void imx2_wdt_timer_ping(unsigned long arg)
111{
112 /* ping it every imx2_wdt.timeout / 2 seconds to prevent reboot */
113 imx2_wdt_ping();
114 mod_timer(&imx2_wdt.timer, jiffies + imx2_wdt.timeout * HZ / 2);
115}
116
117static void imx2_wdt_start(void)
118{
119 if (!test_and_set_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
120 /* at our first start we enable clock and do initialisations */
121 clk_enable(imx2_wdt.clk);
122
123 imx2_wdt_setup();
124 } else /* delete the timer that pings the watchdog after close */
125 del_timer_sync(&imx2_wdt.timer);
126
127 /* Watchdog is enabled - time to reload the timeout value */
128 imx2_wdt_ping();
129}
130
131static void imx2_wdt_stop(void)
132{
133 /* we don't need a clk_disable, it cannot be disabled once started.
134 * We use a timer to ping the watchdog while /dev/watchdog is closed */
135 imx2_wdt_timer_ping(0);
136}
137
138static void imx2_wdt_set_timeout(int new_timeout)
139{
140 u16 val = __raw_readw(imx2_wdt.base + IMX2_WDT_WCR);
141
142 /* set the new timeout value in the WSR */
143 val &= ~IMX2_WDT_WCR_WT;
144 val |= WDOG_SEC_TO_COUNT(new_timeout);
145 __raw_writew(val, imx2_wdt.base + IMX2_WDT_WCR);
146}
147
148static int imx2_wdt_open(struct inode *inode, struct file *file)
149{
150 if (test_and_set_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status))
151 return -EBUSY;
152
153 imx2_wdt_start();
154 return nonseekable_open(inode, file);
155}
156
157static int imx2_wdt_close(struct inode *inode, struct file *file)
158{
159 if (test_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status) && !nowayout)
160 imx2_wdt_stop();
161 else {
162 dev_crit(imx2_wdt_miscdev.parent,
163 "Unexpected close: Expect reboot!\n");
164 imx2_wdt_ping();
165 }
166
167 clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
168 clear_bit(IMX2_WDT_STATUS_OPEN, &imx2_wdt.status);
169 return 0;
170}
171
172static long imx2_wdt_ioctl(struct file *file, unsigned int cmd,
173 unsigned long arg)
174{
175 void __user *argp = (void __user *)arg;
176 int __user *p = argp;
177 int new_value;
178
179 switch (cmd) {
180 case WDIOC_GETSUPPORT:
181 return copy_to_user(argp, &imx2_wdt_info,
182 sizeof(struct watchdog_info)) ? -EFAULT : 0;
183
184 case WDIOC_GETSTATUS:
185 case WDIOC_GETBOOTSTATUS:
186 return put_user(0, p);
187
188 case WDIOC_KEEPALIVE:
189 imx2_wdt_ping();
190 return 0;
191
192 case WDIOC_SETTIMEOUT:
193 if (get_user(new_value, p))
194 return -EFAULT;
195 if ((new_value < 1) || (new_value > IMX2_WDT_MAX_TIME))
196 return -EINVAL;
197 imx2_wdt_set_timeout(new_value);
198 imx2_wdt.timeout = new_value;
199 imx2_wdt_ping();
200
201 /* Fallthrough to return current value */
202 case WDIOC_GETTIMEOUT:
203 return put_user(imx2_wdt.timeout, p);
204
205 default:
206 return -ENOTTY;
207 }
208}
209
210static ssize_t imx2_wdt_write(struct file *file, const char __user *data,
211 size_t len, loff_t *ppos)
212{
213 size_t i;
214 char c;
215
216 if (len == 0) /* Can we see this even ? */
217 return 0;
218
219 clear_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
220 /* scan to see whether or not we got the magic character */
221 for (i = 0; i != len; i++) {
222 if (get_user(c, data + i))
223 return -EFAULT;
224 if (c == 'V')
225 set_bit(IMX2_WDT_EXPECT_CLOSE, &imx2_wdt.status);
226 }
227
228 imx2_wdt_ping();
229 return len;
230}
231
232static const struct file_operations imx2_wdt_fops = {
233 .owner = THIS_MODULE,
234 .llseek = no_llseek,
235 .unlocked_ioctl = imx2_wdt_ioctl,
236 .open = imx2_wdt_open,
237 .release = imx2_wdt_close,
238 .write = imx2_wdt_write,
239};
240
241static struct miscdevice imx2_wdt_miscdev = {
242 .minor = WATCHDOG_MINOR,
243 .name = "watchdog",
244 .fops = &imx2_wdt_fops,
245};
246
247static int __init imx2_wdt_probe(struct platform_device *pdev)
248{
249 int ret;
250 int res_size;
251 struct resource *res;
252
253 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
254 if (!res) {
255 dev_err(&pdev->dev, "can't get device resources\n");
256 return -ENODEV;
257 }
258
259 res_size = resource_size(res);
260 if (!devm_request_mem_region(&pdev->dev, res->start, res_size,
261 res->name)) {
262 dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n",
263 res_size, res->start);
264 return -ENOMEM;
265 }
266
267 imx2_wdt.base = devm_ioremap_nocache(&pdev->dev, res->start, res_size);
268 if (!imx2_wdt.base) {
269 dev_err(&pdev->dev, "ioremap failed\n");
270 return -ENOMEM;
271 }
272
273 imx2_wdt.clk = clk_get_sys("imx-wdt.0", NULL);
274 if (IS_ERR(imx2_wdt.clk)) {
275 dev_err(&pdev->dev, "can't get Watchdog clock\n");
276 return PTR_ERR(imx2_wdt.clk);
277 }
278
279 imx2_wdt.timeout = clamp_t(unsigned, timeout, 1, IMX2_WDT_MAX_TIME);
280 if (imx2_wdt.timeout != timeout)
281 dev_warn(&pdev->dev, "Initial timeout out of range! "
282 "Clamped from %u to %u\n", timeout, imx2_wdt.timeout);
283
284 setup_timer(&imx2_wdt.timer, imx2_wdt_timer_ping, 0);
285
286 imx2_wdt_miscdev.parent = &pdev->dev;
287 ret = misc_register(&imx2_wdt_miscdev);
288 if (ret)
289 goto fail;
290
291 dev_info(&pdev->dev,
292 "IMX2+ Watchdog Timer enabled. timeout=%ds (nowayout=%d)\n",
293 imx2_wdt.timeout, nowayout);
294 return 0;
295
296fail:
297 imx2_wdt_miscdev.parent = NULL;
298 clk_put(imx2_wdt.clk);
299 return ret;
300}
301
302static int __exit imx2_wdt_remove(struct platform_device *pdev)
303{
304 misc_deregister(&imx2_wdt_miscdev);
305
306 if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
307 del_timer_sync(&imx2_wdt.timer);
308
309 dev_crit(imx2_wdt_miscdev.parent,
310 "Device removed: Expect reboot!\n");
311 } else
312 clk_put(imx2_wdt.clk);
313
314 imx2_wdt_miscdev.parent = NULL;
315 return 0;
316}
317
318static void imx2_wdt_shutdown(struct platform_device *pdev)
319{
320 if (test_bit(IMX2_WDT_STATUS_STARTED, &imx2_wdt.status)) {
321 /* we are running, we need to delete the timer but will give
322 * max timeout before reboot will take place */
323 del_timer_sync(&imx2_wdt.timer);
324 imx2_wdt_set_timeout(IMX2_WDT_MAX_TIME);
325 imx2_wdt_ping();
326
327 dev_crit(imx2_wdt_miscdev.parent,
328 "Device shutdown: Expect reboot!\n");
329 }
330}
331
332static struct platform_driver imx2_wdt_driver = {
333 .probe = imx2_wdt_probe,
334 .remove = __exit_p(imx2_wdt_remove),
335 .shutdown = imx2_wdt_shutdown,
336 .driver = {
337 .name = DRIVER_NAME,
338 .owner = THIS_MODULE,
339 },
340};
341
342static int __init imx2_wdt_init(void)
343{
344 return platform_driver_probe(&imx2_wdt_driver, imx2_wdt_probe);
345}
346module_init(imx2_wdt_init);
347
348static void __exit imx2_wdt_exit(void)
349{
350 platform_driver_unregister(&imx2_wdt_driver);
351}
352module_exit(imx2_wdt_exit);
353
354MODULE_AUTHOR("Wolfram Sang");
355MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
356MODULE_LICENSE("GPL v2");
357MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
358MODULE_ALIAS("platform:" DRIVER_NAME);