]> Pileus Git - ~andy/linux/blob - drivers/watchdog/wm8350_wdt.c
watchdog: Use pr_<fmt> and pr_<level>
[~andy/linux] / drivers / watchdog / wm8350_wdt.c
1 /*
2  * Watchdog driver for the wm8350
3  *
4  * Copyright (C) 2007, 2008 Wolfson Microelectronics <linux@wolfsonmicro.com>
5  *
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
9  */
10
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13 #include <linux/module.h>
14 #include <linux/moduleparam.h>
15 #include <linux/types.h>
16 #include <linux/kernel.h>
17 #include <linux/fs.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>
23
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) ")");
29
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);
34
35 static struct {
36         int time;  /* Seconds */
37         u16 val;   /* To be set in WM8350_SYSTEM_CONTROL_2 */
38 } wm8350_wdt_cfgs[] = {
39         { 1, 0x02 },
40         { 2, 0x04 },
41         { 4, 0x05 },
42 };
43
44 static struct wm8350 *get_wm8350(void)
45 {
46         return dev_get_drvdata(wm8350_wdt_miscdev.parent);
47 }
48
49 static int wm8350_wdt_set_timeout(struct wm8350 *wm8350, u16 value)
50 {
51         int ret;
52         u16 reg;
53
54         mutex_lock(&wdt_mutex);
55         wm8350_reg_unlock(wm8350);
56
57         reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
58         reg &= ~WM8350_WDOG_TO_MASK;
59         reg |= value;
60         ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
61
62         wm8350_reg_lock(wm8350);
63         mutex_unlock(&wdt_mutex);
64
65         return ret;
66 }
67
68 static int wm8350_wdt_start(struct wm8350 *wm8350)
69 {
70         int ret;
71         u16 reg;
72
73         mutex_lock(&wdt_mutex);
74         wm8350_reg_unlock(wm8350);
75
76         reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
77         reg &= ~WM8350_WDOG_MODE_MASK;
78         reg |= 0x20;
79         ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
80
81         wm8350_reg_lock(wm8350);
82         mutex_unlock(&wdt_mutex);
83
84         return ret;
85 }
86
87 static int wm8350_wdt_stop(struct wm8350 *wm8350)
88 {
89         int ret;
90         u16 reg;
91
92         mutex_lock(&wdt_mutex);
93         wm8350_reg_unlock(wm8350);
94
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);
98
99         wm8350_reg_lock(wm8350);
100         mutex_unlock(&wdt_mutex);
101
102         return ret;
103 }
104
105 static int wm8350_wdt_kick(struct wm8350 *wm8350)
106 {
107         int ret;
108         u16 reg;
109
110         mutex_lock(&wdt_mutex);
111
112         reg = wm8350_reg_read(wm8350, WM8350_SYSTEM_CONTROL_2);
113         ret = wm8350_reg_write(wm8350, WM8350_SYSTEM_CONTROL_2, reg);
114
115         mutex_unlock(&wdt_mutex);
116
117         return ret;
118 }
119
120 static int wm8350_wdt_open(struct inode *inode, struct file *file)
121 {
122         struct wm8350 *wm8350 = get_wm8350();
123         int ret;
124
125         if (!wm8350)
126                 return -ENODEV;
127
128         if (test_and_set_bit(0, &wm8350_wdt_users))
129                 return -EBUSY;
130
131         ret = wm8350_wdt_start(wm8350);
132         if (ret != 0)
133                 return ret;
134
135         return nonseekable_open(inode, file);
136 }
137
138 static int wm8350_wdt_release(struct inode *inode, struct file *file)
139 {
140         struct wm8350 *wm8350 = get_wm8350();
141
142         if (wm8350_wdt_expect_close)
143                 wm8350_wdt_stop(wm8350);
144         else {
145                 dev_warn(wm8350->dev, "Watchdog device closed uncleanly\n");
146                 wm8350_wdt_kick(wm8350);
147         }
148
149         clear_bit(0, &wm8350_wdt_users);
150
151         return 0;
152 }
153
154 static ssize_t wm8350_wdt_write(struct file *file,
155                                 const char __user *data, size_t count,
156                                 loff_t *ppos)
157 {
158         struct wm8350 *wm8350 = get_wm8350();
159         size_t i;
160
161         if (count) {
162                 wm8350_wdt_kick(wm8350);
163
164                 if (!nowayout) {
165                         /* In case it was set long ago */
166                         wm8350_wdt_expect_close = 0;
167
168                         /* scan to see whether or not we got the magic
169                            character */
170                         for (i = 0; i != count; i++) {
171                                 char c;
172                                 if (get_user(c, data + i))
173                                         return -EFAULT;
174                                 if (c == 'V')
175                                         wm8350_wdt_expect_close = 42;
176                         }
177                 }
178         }
179         return count;
180 }
181
182 static const struct watchdog_info ident = {
183         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
184         .identity = "WM8350 Watchdog",
185 };
186
187 static long wm8350_wdt_ioctl(struct file *file, unsigned int cmd,
188                              unsigned long arg)
189 {
190         struct wm8350 *wm8350 = get_wm8350();
191         int ret = -ENOTTY, time, i;
192         void __user *argp = (void __user *)arg;
193         int __user *p = argp;
194         u16 reg;
195
196         switch (cmd) {
197         case WDIOC_GETSUPPORT:
198                 ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
199                 break;
200
201         case WDIOC_GETSTATUS:
202         case WDIOC_GETBOOTSTATUS:
203                 ret = put_user(0, p);
204                 break;
205
206         case WDIOC_SETOPTIONS:
207         {
208                 int options;
209
210                 if (get_user(options, p))
211                         return -EFAULT;
212
213                 ret = -EINVAL;
214
215                 /* Setting both simultaneously means at least one must fail */
216                 if (options == WDIOS_DISABLECARD)
217                         ret = wm8350_wdt_stop(wm8350);
218
219                 if (options == WDIOS_ENABLECARD)
220                         ret = wm8350_wdt_start(wm8350);
221                 break;
222         }
223
224         case WDIOC_KEEPALIVE:
225                 ret = wm8350_wdt_kick(wm8350);
226                 break;
227
228         case WDIOC_SETTIMEOUT:
229                 ret = get_user(time, p);
230                 if (ret)
231                         break;
232
233                 if (time == 0) {
234                         if (nowayout)
235                                 ret = -EINVAL;
236                         else
237                                 wm8350_wdt_stop(wm8350);
238                         break;
239                 }
240
241                 for (i = 0; i < ARRAY_SIZE(wm8350_wdt_cfgs); i++)
242                         if (wm8350_wdt_cfgs[i].time == time)
243                                 break;
244                 if (i == ARRAY_SIZE(wm8350_wdt_cfgs))
245                         ret = -EINVAL;
246                 else
247                         ret = wm8350_wdt_set_timeout(wm8350,
248                                                      wm8350_wdt_cfgs[i].val);
249                 break;
250
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)
256                                 break;
257                 if (i == ARRAY_SIZE(wm8350_wdt_cfgs)) {
258                         dev_warn(wm8350->dev,
259                                  "Unknown watchdog configuration: %x\n", reg);
260                         ret = -EINVAL;
261                 } else
262                         ret = put_user(wm8350_wdt_cfgs[i].time, p);
263
264         }
265
266         return ret;
267 }
268
269 static const struct file_operations wm8350_wdt_fops = {
270         .owner = THIS_MODULE,
271         .llseek = no_llseek,
272         .write = wm8350_wdt_write,
273         .unlocked_ioctl = wm8350_wdt_ioctl,
274         .open = wm8350_wdt_open,
275         .release = wm8350_wdt_release,
276 };
277
278 static struct miscdevice wm8350_wdt_miscdev = {
279         .minor = WATCHDOG_MINOR,
280         .name = "watchdog",
281         .fops = &wm8350_wdt_fops,
282 };
283
284 static int __devinit wm8350_wdt_probe(struct platform_device *pdev)
285 {
286         struct wm8350 *wm8350 = platform_get_drvdata(pdev);
287
288         if (!wm8350) {
289                 pr_err("No driver data supplied\n");
290                 return -ENODEV;
291         }
292
293         /* Default to 4s timeout */
294         wm8350_wdt_set_timeout(wm8350, 0x05);
295
296         wm8350_wdt_miscdev.parent = &pdev->dev;
297
298         return misc_register(&wm8350_wdt_miscdev);
299 }
300
301 static int __devexit wm8350_wdt_remove(struct platform_device *pdev)
302 {
303         misc_deregister(&wm8350_wdt_miscdev);
304
305         return 0;
306 }
307
308 static struct platform_driver wm8350_wdt_driver = {
309         .probe = wm8350_wdt_probe,
310         .remove = __devexit_p(wm8350_wdt_remove),
311         .driver = {
312                 .name = "wm8350-wdt",
313         },
314 };
315
316 module_platform_driver(wm8350_wdt_driver);
317
318 MODULE_AUTHOR("Mark Brown");
319 MODULE_DESCRIPTION("WM8350 Watchdog");
320 MODULE_LICENSE("GPL");
321 MODULE_ALIAS("platform:wm8350-wdt");