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 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 #include <linux/module.h>
14 #include <linux/moduleparam.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
18 #include <linux/miscdevice.h>
19 #include <linux/platform_device.h>
20 #include <linux/watchdog.h>
21 #include <linux/uaccess.h>
22 #include <linux/mfd/wm8350/core.h>
24 static int nowayout
= WATCHDOG_NOWAYOUT
;
25 module_param(nowayout
, int, 0);
26 MODULE_PARM_DESC(nowayout
,
27 "Watchdog cannot be stopped once started (default="
28 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
30 static unsigned long wm8350_wdt_users
;
31 static struct miscdevice wm8350_wdt_miscdev
;
32 static int wm8350_wdt_expect_close
;
33 static DEFINE_MUTEX(wdt_mutex
);
36 int time
; /* Seconds */
37 u16 val
; /* To be set in WM8350_SYSTEM_CONTROL_2 */
38 } wm8350_wdt_cfgs
[] = {
44 static struct wm8350
*get_wm8350(void)
46 return dev_get_drvdata(wm8350_wdt_miscdev
.parent
);
49 static int wm8350_wdt_set_timeout(struct wm8350
*wm8350
, u16 value
)
54 mutex_lock(&wdt_mutex
);
55 wm8350_reg_unlock(wm8350
);
57 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
58 reg
&= ~WM8350_WDOG_TO_MASK
;
60 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
62 wm8350_reg_lock(wm8350
);
63 mutex_unlock(&wdt_mutex
);
68 static int wm8350_wdt_start(struct wm8350
*wm8350
)
73 mutex_lock(&wdt_mutex
);
74 wm8350_reg_unlock(wm8350
);
76 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
77 reg
&= ~WM8350_WDOG_MODE_MASK
;
79 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
81 wm8350_reg_lock(wm8350
);
82 mutex_unlock(&wdt_mutex
);
87 static int wm8350_wdt_stop(struct wm8350
*wm8350
)
92 mutex_lock(&wdt_mutex
);
93 wm8350_reg_unlock(wm8350
);
95 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
96 reg
&= ~WM8350_WDOG_MODE_MASK
;
97 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
99 wm8350_reg_lock(wm8350
);
100 mutex_unlock(&wdt_mutex
);
105 static int wm8350_wdt_kick(struct wm8350
*wm8350
)
110 mutex_lock(&wdt_mutex
);
112 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
113 ret
= wm8350_reg_write(wm8350
, WM8350_SYSTEM_CONTROL_2
, reg
);
115 mutex_unlock(&wdt_mutex
);
120 static int wm8350_wdt_open(struct inode
*inode
, struct file
*file
)
122 struct wm8350
*wm8350
= get_wm8350();
128 if (test_and_set_bit(0, &wm8350_wdt_users
))
131 ret
= wm8350_wdt_start(wm8350
);
135 return nonseekable_open(inode
, file
);
138 static int wm8350_wdt_release(struct inode
*inode
, struct file
*file
)
140 struct wm8350
*wm8350
= get_wm8350();
142 if (wm8350_wdt_expect_close
)
143 wm8350_wdt_stop(wm8350
);
145 dev_warn(wm8350
->dev
, "Watchdog device closed uncleanly\n");
146 wm8350_wdt_kick(wm8350
);
149 clear_bit(0, &wm8350_wdt_users
);
154 static ssize_t
wm8350_wdt_write(struct file
*file
,
155 const char __user
*data
, size_t count
,
158 struct wm8350
*wm8350
= get_wm8350();
162 wm8350_wdt_kick(wm8350
);
165 /* In case it was set long ago */
166 wm8350_wdt_expect_close
= 0;
168 /* scan to see whether or not we got the magic
170 for (i
= 0; i
!= count
; i
++) {
172 if (get_user(c
, data
+ i
))
175 wm8350_wdt_expect_close
= 42;
182 static const struct watchdog_info ident
= {
183 .options
= WDIOF_SETTIMEOUT
| WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
,
184 .identity
= "WM8350 Watchdog",
187 static long wm8350_wdt_ioctl(struct file
*file
, unsigned int cmd
,
190 struct wm8350
*wm8350
= get_wm8350();
191 int ret
= -ENOTTY
, time
, i
;
192 void __user
*argp
= (void __user
*)arg
;
193 int __user
*p
= argp
;
197 case WDIOC_GETSUPPORT
:
198 ret
= copy_to_user(argp
, &ident
, sizeof(ident
)) ? -EFAULT
: 0;
201 case WDIOC_GETSTATUS
:
202 case WDIOC_GETBOOTSTATUS
:
203 ret
= put_user(0, p
);
206 case WDIOC_SETOPTIONS
:
210 if (get_user(options
, p
))
215 /* Setting both simultaneously means at least one must fail */
216 if (options
== WDIOS_DISABLECARD
)
217 ret
= wm8350_wdt_stop(wm8350
);
219 if (options
== WDIOS_ENABLECARD
)
220 ret
= wm8350_wdt_start(wm8350
);
224 case WDIOC_KEEPALIVE
:
225 ret
= wm8350_wdt_kick(wm8350
);
228 case WDIOC_SETTIMEOUT
:
229 ret
= get_user(time
, p
);
237 wm8350_wdt_stop(wm8350
);
241 for (i
= 0; i
< ARRAY_SIZE(wm8350_wdt_cfgs
); i
++)
242 if (wm8350_wdt_cfgs
[i
].time
== time
)
244 if (i
== ARRAY_SIZE(wm8350_wdt_cfgs
))
247 ret
= wm8350_wdt_set_timeout(wm8350
,
248 wm8350_wdt_cfgs
[i
].val
);
251 case WDIOC_GETTIMEOUT
:
252 reg
= wm8350_reg_read(wm8350
, WM8350_SYSTEM_CONTROL_2
);
253 reg
&= WM8350_WDOG_TO_MASK
;
254 for (i
= 0; i
< ARRAY_SIZE(wm8350_wdt_cfgs
); i
++)
255 if (wm8350_wdt_cfgs
[i
].val
== reg
)
257 if (i
== ARRAY_SIZE(wm8350_wdt_cfgs
)) {
258 dev_warn(wm8350
->dev
,
259 "Unknown watchdog configuration: %x\n", reg
);
262 ret
= put_user(wm8350_wdt_cfgs
[i
].time
, p
);
269 static const struct file_operations wm8350_wdt_fops
= {
270 .owner
= THIS_MODULE
,
272 .write
= wm8350_wdt_write
,
273 .unlocked_ioctl
= wm8350_wdt_ioctl
,
274 .open
= wm8350_wdt_open
,
275 .release
= wm8350_wdt_release
,
278 static struct miscdevice wm8350_wdt_miscdev
= {
279 .minor
= WATCHDOG_MINOR
,
281 .fops
= &wm8350_wdt_fops
,
284 static int __devinit
wm8350_wdt_probe(struct platform_device
*pdev
)
286 struct wm8350
*wm8350
= platform_get_drvdata(pdev
);
289 pr_err("No driver data supplied\n");
293 /* Default to 4s timeout */
294 wm8350_wdt_set_timeout(wm8350
, 0x05);
296 wm8350_wdt_miscdev
.parent
= &pdev
->dev
;
298 return misc_register(&wm8350_wdt_miscdev
);
301 static int __devexit
wm8350_wdt_remove(struct platform_device
*pdev
)
303 misc_deregister(&wm8350_wdt_miscdev
);
308 static struct platform_driver wm8350_wdt_driver
= {
309 .probe
= wm8350_wdt_probe
,
310 .remove
= __devexit_p(wm8350_wdt_remove
),
312 .name
= "wm8350-wdt",
316 module_platform_driver(wm8350_wdt_driver
);
318 MODULE_AUTHOR("Mark Brown");
319 MODULE_DESCRIPTION("WM8350 Watchdog");
320 MODULE_LICENSE("GPL");
321 MODULE_ALIAS("platform:wm8350-wdt");