]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/watchdog/adx_wdt.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-2.6
[karo-tx-linux.git] / drivers / watchdog / adx_wdt.c
1 /*
2  * Copyright (C) 2008-2009 Avionic Design GmbH
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8
9 #include <linux/fs.h>
10 #include <linux/gfp.h>
11 #include <linux/io.h>
12 #include <linux/miscdevice.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/types.h>
16 #include <linux/uaccess.h>
17 #include <linux/watchdog.h>
18
19 #define WATCHDOG_NAME "adx-wdt"
20
21 /* register offsets */
22 #define ADX_WDT_CONTROL         0x00
23 #define ADX_WDT_CONTROL_ENABLE  (1 << 0)
24 #define ADX_WDT_CONTROL_nRESET  (1 << 1)
25 #define ADX_WDT_TIMEOUT         0x08
26
27 static struct platform_device *adx_wdt_dev;
28 static unsigned long driver_open;
29
30 #define WDT_STATE_STOP  0
31 #define WDT_STATE_START 1
32
33 struct adx_wdt {
34         void __iomem *base;
35         unsigned long timeout;
36         unsigned int state;
37         unsigned int wake;
38         spinlock_t lock;
39 };
40
41 static const struct watchdog_info adx_wdt_info = {
42         .identity = "Avionic Design Xanthos Watchdog",
43         .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
44 };
45
46 static void adx_wdt_start_locked(struct adx_wdt *wdt)
47 {
48         u32 ctrl;
49
50         ctrl = readl(wdt->base + ADX_WDT_CONTROL);
51         ctrl |= ADX_WDT_CONTROL_ENABLE;
52         writel(ctrl, wdt->base + ADX_WDT_CONTROL);
53         wdt->state = WDT_STATE_START;
54 }
55
56 static void adx_wdt_start(struct adx_wdt *wdt)
57 {
58         unsigned long flags;
59
60         spin_lock_irqsave(&wdt->lock, flags);
61         adx_wdt_start_locked(wdt);
62         spin_unlock_irqrestore(&wdt->lock, flags);
63 }
64
65 static void adx_wdt_stop_locked(struct adx_wdt *wdt)
66 {
67         u32 ctrl;
68
69         ctrl = readl(wdt->base + ADX_WDT_CONTROL);
70         ctrl &= ~ADX_WDT_CONTROL_ENABLE;
71         writel(ctrl, wdt->base + ADX_WDT_CONTROL);
72         wdt->state = WDT_STATE_STOP;
73 }
74
75 static void adx_wdt_stop(struct adx_wdt *wdt)
76 {
77         unsigned long flags;
78
79         spin_lock_irqsave(&wdt->lock, flags);
80         adx_wdt_stop_locked(wdt);
81         spin_unlock_irqrestore(&wdt->lock, flags);
82 }
83
84 static void adx_wdt_set_timeout(struct adx_wdt *wdt, unsigned long seconds)
85 {
86         unsigned long timeout = seconds * 1000;
87         unsigned long flags;
88         unsigned int state;
89
90         spin_lock_irqsave(&wdt->lock, flags);
91         state = wdt->state;
92         adx_wdt_stop_locked(wdt);
93         writel(timeout, wdt->base + ADX_WDT_TIMEOUT);
94
95         if (state == WDT_STATE_START)
96                 adx_wdt_start_locked(wdt);
97
98         wdt->timeout = timeout;
99         spin_unlock_irqrestore(&wdt->lock, flags);
100 }
101
102 static void adx_wdt_get_timeout(struct adx_wdt *wdt, unsigned long *seconds)
103 {
104         *seconds = wdt->timeout / 1000;
105 }
106
107 static void adx_wdt_keepalive(struct adx_wdt *wdt)
108 {
109         unsigned long flags;
110
111         spin_lock_irqsave(&wdt->lock, flags);
112         writel(wdt->timeout, wdt->base + ADX_WDT_TIMEOUT);
113         spin_unlock_irqrestore(&wdt->lock, flags);
114 }
115
116 static int adx_wdt_open(struct inode *inode, struct file *file)
117 {
118         struct adx_wdt *wdt = platform_get_drvdata(adx_wdt_dev);
119
120         if (test_and_set_bit(0, &driver_open))
121                 return -EBUSY;
122
123         file->private_data = wdt;
124         adx_wdt_set_timeout(wdt, 30);
125         adx_wdt_start(wdt);
126
127         return nonseekable_open(inode, file);
128 }
129
130 static int adx_wdt_release(struct inode *inode, struct file *file)
131 {
132         struct adx_wdt *wdt = file->private_data;
133
134         adx_wdt_stop(wdt);
135         clear_bit(0, &driver_open);
136
137         return 0;
138 }
139
140 static long adx_wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
141 {
142         struct adx_wdt *wdt = file->private_data;
143         void __user *argp = (void __user *)arg;
144         unsigned long __user *p = argp;
145         unsigned long seconds = 0;
146         unsigned int options;
147         long ret = -EINVAL;
148
149         switch (cmd) {
150         case WDIOC_GETSUPPORT:
151                 if (copy_to_user(argp, &adx_wdt_info, sizeof(adx_wdt_info)))
152                         return -EFAULT;
153                 else
154                         return 0;
155
156         case WDIOC_GETSTATUS:
157         case WDIOC_GETBOOTSTATUS:
158                 return put_user(0, p);
159
160         case WDIOC_KEEPALIVE:
161                 adx_wdt_keepalive(wdt);
162                 return 0;
163
164         case WDIOC_SETTIMEOUT:
165                 if (get_user(seconds, p))
166                         return -EFAULT;
167
168                 adx_wdt_set_timeout(wdt, seconds);
169
170                 /* fallthrough */
171         case WDIOC_GETTIMEOUT:
172                 adx_wdt_get_timeout(wdt, &seconds);
173                 return put_user(seconds, p);
174
175         case WDIOC_SETOPTIONS:
176                 if (copy_from_user(&options, argp, sizeof(options)))
177                         return -EFAULT;
178
179                 if (options & WDIOS_DISABLECARD) {
180                         adx_wdt_stop(wdt);
181                         ret = 0;
182                 }
183
184                 if (options & WDIOS_ENABLECARD) {
185                         adx_wdt_start(wdt);
186                         ret = 0;
187                 }
188
189                 return ret;
190
191         default:
192                 break;
193         }
194
195         return -ENOTTY;
196 }
197
198 static ssize_t adx_wdt_write(struct file *file, const char __user *data,
199                 size_t len, loff_t *ppos)
200 {
201         struct adx_wdt *wdt = file->private_data;
202
203         if (len)
204                 adx_wdt_keepalive(wdt);
205
206         return len;
207 }
208
209 static const struct file_operations adx_wdt_fops = {
210         .owner = THIS_MODULE,
211         .llseek = no_llseek,
212         .open = adx_wdt_open,
213         .release = adx_wdt_release,
214         .unlocked_ioctl = adx_wdt_ioctl,
215         .write = adx_wdt_write,
216 };
217
218 static struct miscdevice adx_wdt_miscdev = {
219         .minor = WATCHDOG_MINOR,
220         .name = "watchdog",
221         .fops = &adx_wdt_fops,
222 };
223
224 static int __devinit adx_wdt_probe(struct platform_device *pdev)
225 {
226         struct resource *res;
227         struct adx_wdt *wdt;
228         int ret = 0;
229         u32 ctrl;
230
231         wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
232         if (!wdt) {
233                 dev_err(&pdev->dev, "cannot allocate WDT structure\n");
234                 return -ENOMEM;
235         }
236
237         spin_lock_init(&wdt->lock);
238
239         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
240         if (!res) {
241                 dev_err(&pdev->dev, "cannot obtain I/O memory region\n");
242                 return -ENXIO;
243         }
244
245         res = devm_request_mem_region(&pdev->dev, res->start,
246                         resource_size(res), res->name);
247         if (!res) {
248                 dev_err(&pdev->dev, "cannot request I/O memory region\n");
249                 return -ENXIO;
250         }
251
252         wdt->base = devm_ioremap_nocache(&pdev->dev, res->start,
253                         resource_size(res));
254         if (!wdt->base) {
255                 dev_err(&pdev->dev, "cannot remap I/O memory region\n");
256                 return -ENXIO;
257         }
258
259         /* disable watchdog and reboot on timeout */
260         ctrl = readl(wdt->base + ADX_WDT_CONTROL);
261         ctrl &= ~ADX_WDT_CONTROL_ENABLE;
262         ctrl &= ~ADX_WDT_CONTROL_nRESET;
263         writel(ctrl, wdt->base + ADX_WDT_CONTROL);
264
265         platform_set_drvdata(pdev, wdt);
266         adx_wdt_dev = pdev;
267
268         ret = misc_register(&adx_wdt_miscdev);
269         if (ret) {
270                 dev_err(&pdev->dev, "cannot register miscdev on minor %d "
271                                 "(err=%d)\n", WATCHDOG_MINOR, ret);
272                 return ret;
273         }
274
275         return 0;
276 }
277
278 static int __devexit adx_wdt_remove(struct platform_device *pdev)
279 {
280         struct adx_wdt *wdt = platform_get_drvdata(pdev);
281
282         misc_deregister(&adx_wdt_miscdev);
283         adx_wdt_stop(wdt);
284         platform_set_drvdata(pdev, NULL);
285
286         return 0;
287 }
288
289 static void adx_wdt_shutdown(struct platform_device *pdev)
290 {
291         struct adx_wdt *wdt = platform_get_drvdata(pdev);
292         adx_wdt_stop(wdt);
293 }
294
295 #ifdef CONFIG_PM
296 static int adx_wdt_suspend(struct device *dev)
297 {
298         struct platform_device *pdev = to_platform_device(dev);
299         struct adx_wdt *wdt = platform_get_drvdata(pdev);
300
301         wdt->wake = (wdt->state == WDT_STATE_START) ? 1 : 0;
302         adx_wdt_stop(wdt);
303
304         return 0;
305 }
306
307 static int adx_wdt_resume(struct device *dev)
308 {
309         struct platform_device *pdev = to_platform_device(dev);
310         struct adx_wdt *wdt = platform_get_drvdata(pdev);
311
312         if (wdt->wake)
313                 adx_wdt_start(wdt);
314
315         return 0;
316 }
317
318 static const struct dev_pm_ops adx_wdt_pm_ops = {
319         .suspend = adx_wdt_suspend,
320         .resume = adx_wdt_resume,
321 };
322
323 #  define ADX_WDT_PM_OPS        (&adx_wdt_pm_ops)
324 #else
325 #  define ADX_WDT_PM_OPS        NULL
326 #endif
327
328 static struct platform_driver adx_wdt_driver = {
329         .probe = adx_wdt_probe,
330         .remove = __devexit_p(adx_wdt_remove),
331         .shutdown = adx_wdt_shutdown,
332         .driver = {
333                 .name = WATCHDOG_NAME,
334                 .owner = THIS_MODULE,
335                 .pm = ADX_WDT_PM_OPS,
336         },
337 };
338
339 static int __init adx_wdt_init(void)
340 {
341         return platform_driver_register(&adx_wdt_driver);
342 }
343
344 static void __exit adx_wdt_exit(void)
345 {
346         platform_driver_unregister(&adx_wdt_driver);
347 }
348
349 module_init(adx_wdt_init);
350 module_exit(adx_wdt_exit);
351
352 MODULE_DESCRIPTION("Avionic Design Xanthos Watchdog Driver");
353 MODULE_LICENSE("GPL v2");
354 MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
355 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);