2 * Watchdog driver for the wm8350
4 * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation
11 #include <linux/module.h>
12 #include <linux/moduleparam.h>
13 #include <linux/types.h>
14 #include <linux/kernel.h>
16 #include <linux/miscdevice.h>
17 #include <linux/platform_device.h>
18 #include <linux/watchdog.h>
19 #include <linux/uaccess.h>
20 #include <linux/mfd/wm8350/core.h>
22 static int nowayout
= WATCHDOG_NOWAYOUT
;
23 module_param(nowayout
, int, 0);
24 MODULE_PARM_DESC(nowayout
,
25 "Watchdog cannot be stopped once started (default="
26 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
28 static unsigned long wm8350_wdt_users
;
29 static struct miscdevice wm8350_wdt_miscdev
;
30 static int wm8350_wdt_expect_close
;
31 static DEFINE_MUTEX(wdt_mutex
);
34 int time
; /* Seconds */
35 u16 val
; /* To be set in WM8350_SYSTEM_CONTROL_2 */
36 } wm8350_wdt_cfgs
[] = {
42 static struct wm8350
*get_wm8350(void)
44 return dev_get_drvdata(wm8350_wdt_miscdev
.parent
);
47 static int wm8350_wdt_set_timeout(struct wm8350
*wm8350
, u16 value
)
52 mutex_lock(&wdt_mutex
);
53 wm8350_reg_unlock(wm8350
);
55 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
56 reg
&= ~WM8350_WDOG_TO_MASK
;
58 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
60 wm8350_reg_lock(wm8350
);
61 mutex_unlock(&wdt_mutex
);
66 static int wm8350_wdt_start(struct wm8350
*wm8350
)
71 mutex_lock(&wdt_mutex
);
72 wm8350_reg_unlock(wm8350
);
74 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
75 reg
&= ~WM8350_WDOG_MODE_MASK
;
77 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
79 wm8350_reg_lock(wm8350
);
80 mutex_unlock(&wdt_mutex
);
85 static int wm8350_wdt_stop(struct wm8350
*wm8350
)
90 mutex_lock(&wdt_mutex
);
91 wm8350_reg_unlock(wm8350
);
93 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
94 reg
&= ~WM8350_WDOG_MODE_MASK
;
95 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
97 wm8350_reg_lock(wm8350
);
98 mutex_unlock(&wdt_mutex
);
103 static int wm8350_wdt_kick(struct wm8350
*wm8350
)
108 mutex_lock(&wdt_mutex
);
110 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
111 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
113 mutex_unlock(&wdt_mutex
);
118 static int wm8350_wdt_open(struct inode
*inode
, struct file
*file
)
120 struct wm8350
*wm8350
= get_wm8350();
126 if (test_and_set_bit(0, &wm8350_wdt_users
))
129 ret
= wm8350_wdt_start(wm8350
);
133 return nonseekable_open(inode
, file
);
136 static int wm8350_wdt_release(struct inode
*inode
, struct file
*file
)
138 struct wm8350
*wm8350
= get_wm8350();
140 if (wm8350_wdt_expect_close
)
141 wm8350_wdt_stop(wm8350
);
143 dev_warn(wm8350
->dev
, "Watchdog device closed uncleanly\n");
144 wm8350_wdt_kick(wm8350
);
147 clear_bit(0, &wm8350_wdt_users
);
152 static ssize_t
wm8350_wdt_write(struct file
*file
,
153 const char __user
*data
, size_t count
,
156 struct wm8350
*wm8350
= get_wm8350();
160 wm8350_wdt_kick(wm8350
);
163 /* In case it was set long ago */
164 wm8350_wdt_expect_close
= 0;
166 /* scan to see whether or not we got the magic
168 for (i
= 0; i
!= count
; i
++) {
170 if (get_user(c
, data
+ i
))
173 wm8350_wdt_expect_close
= 42;
180 static const struct watchdog_info ident
= {
181 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
182 .identity
= "WM8350 Watchdog",
185 static long wm8350_wdt_ioctl(struct file
*file
, unsigned int cmd
,
188 struct wm8350
*wm8350
= get_wm8350();
189 int ret
= -ENOTTY
, time
, i
;
190 void __user
*argp
= (void __user
*)arg
;
191 int __user
*p
= argp
;
195 case WDIOC_GETSUPPORT
:
196 ret
= copy_to_user(argp
, &ident
, sizeof(ident
)) ? -EFAULT
: 0;
199 case WDIOC_GETSTATUS
:
200 case WDIOC_GETBOOTSTATUS
:
201 ret
= put_user(0, p
);
204 case WDIOC_SETOPTIONS
:
208 if (get_user(options
, p
))
213 /* Setting both simultaneously means at least one must fail */
214 if (options
== WDIOS_DISABLECARD
)
215 ret
= wm8350_wdt_stop(wm8350
);
217 if (options
== WDIOS_ENABLECARD
)
218 ret
= wm8350_wdt_start(wm8350
);
222 case WDIOC_KEEPALIVE
:
223 ret
= wm8350_wdt_kick(wm8350
);
226 case WDIOC_SETTIMEOUT
:
227 ret
= get_user(time
, p
);
235 wm8350_wdt_stop(wm8350
);
239 for (i
= 0; i
< ARRAY_SIZE(wm8350_wdt_cfgs
); i
++)
240 if (wm8350_wdt_cfgs
[i
].time
== time
)
242 if (i
== ARRAY_SIZE(wm8350_wdt_cfgs
))
245 ret
= wm8350_wdt_set_timeout(wm8350
,
246 wm8350_wdt_cfgs
[i
].val
);
249 case WDIOC_GETTIMEOUT
:
250 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
251 reg
&= WM8350_WDOG_TO_MASK
;
252 for (i
= 0; i
< ARRAY_SIZE(wm8350_wdt_cfgs
); i
++)
253 if (wm8350_wdt_cfgs
[i
].val
== reg
)
255 if (i
== ARRAY_SIZE(wm8350_wdt_cfgs
)) {
256 dev_warn(wm8350
->dev
,
257 "Unknown watchdog configuration: %x\n", reg
);
260 ret
= put_user(wm8350_wdt_cfgs
[i
].time
, p
);
267 static const struct file_operations wm8350_wdt_fops
= {
268 .owner
= THIS_MODULE
,
270 .write
= wm8350_wdt_write
,
271 .unlocked_ioctl
= wm8350_wdt_ioctl
,
272 .open
= wm8350_wdt_open
,
273 .release
= wm8350_wdt_release
,
276 static struct miscdevice wm8350_wdt_miscdev
= {
277 .minor
= WATCHDOG_MINOR
,
279 .fops
= &wm8350_wdt_fops
,
282 static int __devinit
wm8350_wdt_probe(struct platform_device
*pdev
)
284 struct wm8350
*wm8350
= platform_get_drvdata(pdev
);
287 pr_err("No driver data supplied\n");
291 /* Default to 4s timeout */
292 wm8350_wdt_set_timeout(wm8350
, 0x05);
294 wm8350_wdt_miscdev
.parent
= &pdev
->dev
;
296 return misc_register(&wm8350_wdt_miscdev
);
299 static int __devexit
wm8350_wdt_remove(struct platform_device
*pdev
)
301 misc_deregister(&wm8350_wdt_miscdev
);
306 static struct platform_driver wm8350_wdt_driver
= {
307 .probe
= wm8350_wdt_probe
,
308 .remove
= __devexit_p(wm8350_wdt_remove
),
310 .name
= "wm8350-wdt",
314 module_platform_driver(wm8350_wdt_driver
);
316 MODULE_AUTHOR("Mark Brown");
317 MODULE_DESCRIPTION("WM8350 Watchdog");
318 MODULE_LICENSE("GPL");
319 MODULE_ALIAS("platform:wm8350-wdt");