Skip to content

Commit bb2fd8a

Browse files
Wolfram SangWim Van Sebroeck
authored andcommitted
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 <[email protected]> Cc: Vladimir Zapolskiy <[email protected]> Cc: Sascha Hauer <[email protected]> Tested-by: Juergen Beisert <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent 100fb76 commit bb2fd8a

File tree

3 files changed

+371
-0
lines changed

3 files changed

+371
-0
lines changed

drivers/watchdog/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,18 @@ config MAX63XX_WATCHDOG
312312
help
313313
Support for memory mapped max63{69,70,71,72,73,74} watchdog timer.
314314

315+
config 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+
315327
# AVR32 Architecture
316328

317329
config AT32AP700X_WDT

drivers/watchdog/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o
4747
obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
4848
obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o
4949
obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o
50+
obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o
5051

5152
# AVR32 Architecture
5253
obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o

drivers/watchdog/imx2_wdt.c

Lines changed: 358 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,358 @@
1+
/*
2+
* Watchdog driver for IMX2 and later processors
3+
*
4+
* Copyright (C) 2010 Wolfram Sang, Pengutronix e.K. <[email protected]>
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+
58+
static 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+
66+
static struct miscdevice imx2_wdt_miscdev;
67+
68+
static int nowayout = WATCHDOG_NOWAYOUT;
69+
module_param(nowayout, int, 0);
70+
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
71+
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
72+
73+
74+
static unsigned timeout = IMX2_WDT_DEFAULT_TIME;
75+
module_param(timeout, uint, 0);
76+
MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
77+
__MODULE_STRING(IMX2_WDT_DEFAULT_TIME) ")");
78+
79+
static const struct watchdog_info imx2_wdt_info = {
80+
.identity = "imx2+ watchdog",
81+
.options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
82+
};
83+
84+
static 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+
104+
static 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+
110+
static 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+
117+
static 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+
131+
static 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+
138+
static 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+
148+
static 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+
157+
static 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+
172+
static 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+
210+
static 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+
232+
static 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+
241+
static struct miscdevice imx2_wdt_miscdev = {
242+
.minor = WATCHDOG_MINOR,
243+
.name = "watchdog",
244+
.fops = &imx2_wdt_fops,
245+
};
246+
247+
static 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+
296+
fail:
297+
imx2_wdt_miscdev.parent = NULL;
298+
clk_put(imx2_wdt.clk);
299+
return ret;
300+
}
301+
302+
static 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+
318+
static 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+
332+
static 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+
342+
static int __init imx2_wdt_init(void)
343+
{
344+
return platform_driver_probe(&imx2_wdt_driver, imx2_wdt_probe);
345+
}
346+
module_init(imx2_wdt_init);
347+
348+
static void __exit imx2_wdt_exit(void)
349+
{
350+
platform_driver_unregister(&imx2_wdt_driver);
351+
}
352+
module_exit(imx2_wdt_exit);
353+
354+
MODULE_AUTHOR("Wolfram Sang");
355+
MODULE_DESCRIPTION("Watchdog driver for IMX2 and later");
356+
MODULE_LICENSE("GPL v2");
357+
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
358+
MODULE_ALIAS("platform:" DRIVER_NAME);

0 commit comments

Comments
 (0)