Merge branch 'writeback-for-linus' of git://github.com/fengguang/linux
[pandora-kernel.git] / drivers / watchdog / lantiq_wdt.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify it
3  *  under the terms of the GNU General Public License version 2 as published
4  *  by the Free Software Foundation.
5  *
6  *  Copyright (C) 2010 John Crispin <blogic@openwrt.org>
7  *  Based on EP93xx wdt driver
8  */
9
10 #include <linux/module.h>
11 #include <linux/fs.h>
12 #include <linux/miscdevice.h>
13 #include <linux/watchdog.h>
14 #include <linux/platform_device.h>
15 #include <linux/uaccess.h>
16 #include <linux/clk.h>
17 #include <linux/io.h>
18
19 #include <lantiq.h>
20
21 /* Section 3.4 of the datasheet
22  * The password sequence protects the WDT control register from unintended
23  * write actions, which might cause malfunction of the WDT.
24  *
25  * essentially the following two magic passwords need to be written to allow
26  * IO access to the WDT core
27  */
28 #define LTQ_WDT_PW1             0x00BE0000
29 #define LTQ_WDT_PW2             0x00DC0000
30
31 #define LTQ_WDT_CR              0x0     /* watchdog control register */
32 #define LTQ_WDT_SR              0x8     /* watchdog status register */
33
34 #define LTQ_WDT_SR_EN           (0x1 << 31)     /* enable bit */
35 #define LTQ_WDT_SR_PWD          (0x3 << 26)     /* turn on power */
36 #define LTQ_WDT_SR_CLKDIV       (0x3 << 24)     /* turn on clock and set */
37                                                 /* divider to 0x40000 */
38 #define LTQ_WDT_DIVIDER         0x40000
39 #define LTQ_MAX_TIMEOUT         ((1 << 16) - 1) /* the reload field is 16 bit */
40
41 static int nowayout = WATCHDOG_NOWAYOUT;
42
43 static void __iomem *ltq_wdt_membase;
44 static unsigned long ltq_io_region_clk_rate;
45
46 static unsigned long ltq_wdt_bootstatus;
47 static unsigned long ltq_wdt_in_use;
48 static int ltq_wdt_timeout = 30;
49 static int ltq_wdt_ok_to_close;
50
51 static void
52 ltq_wdt_enable(void)
53 {
54         unsigned long int timeout = ltq_wdt_timeout *
55                         (ltq_io_region_clk_rate / LTQ_WDT_DIVIDER) + 0x1000;
56         if (timeout > LTQ_MAX_TIMEOUT)
57                 timeout = LTQ_MAX_TIMEOUT;
58
59         /* write the first password magic */
60         ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
61         /* write the second magic plus the configuration and new timeout */
62         ltq_w32(LTQ_WDT_SR_EN | LTQ_WDT_SR_PWD | LTQ_WDT_SR_CLKDIV |
63                 LTQ_WDT_PW2 | timeout, ltq_wdt_membase + LTQ_WDT_CR);
64 }
65
66 static void
67 ltq_wdt_disable(void)
68 {
69         /* write the first password magic */
70         ltq_w32(LTQ_WDT_PW1, ltq_wdt_membase + LTQ_WDT_CR);
71         /* write the second password magic with no config
72          * this turns the watchdog off
73          */
74         ltq_w32(LTQ_WDT_PW2, ltq_wdt_membase + LTQ_WDT_CR);
75 }
76
77 static ssize_t
78 ltq_wdt_write(struct file *file, const char __user *data,
79                 size_t len, loff_t *ppos)
80 {
81         if (len) {
82                 if (!nowayout) {
83                         size_t i;
84
85                         ltq_wdt_ok_to_close = 0;
86                         for (i = 0; i != len; i++) {
87                                 char c;
88
89                                 if (get_user(c, data + i))
90                                         return -EFAULT;
91                                 if (c == 'V')
92                                         ltq_wdt_ok_to_close = 1;
93                                 else
94                                         ltq_wdt_ok_to_close = 0;
95                         }
96                 }
97                 ltq_wdt_enable();
98         }
99
100         return len;
101 }
102
103 static struct watchdog_info ident = {
104         .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
105                         WDIOF_CARDRESET,
106         .identity = "ltq_wdt",
107 };
108
109 static long
110 ltq_wdt_ioctl(struct file *file,
111                 unsigned int cmd, unsigned long arg)
112 {
113         int ret = -ENOTTY;
114
115         switch (cmd) {
116         case WDIOC_GETSUPPORT:
117                 ret = copy_to_user((struct watchdog_info __user *)arg, &ident,
118                                 sizeof(ident)) ? -EFAULT : 0;
119                 break;
120
121         case WDIOC_GETBOOTSTATUS:
122                 ret = put_user(ltq_wdt_bootstatus, (int __user *)arg);
123                 break;
124
125         case WDIOC_GETSTATUS:
126                 ret = put_user(0, (int __user *)arg);
127                 break;
128
129         case WDIOC_SETTIMEOUT:
130                 ret = get_user(ltq_wdt_timeout, (int __user *)arg);
131                 if (!ret)
132                         ltq_wdt_enable();
133                 /* intentional drop through */
134         case WDIOC_GETTIMEOUT:
135                 ret = put_user(ltq_wdt_timeout, (int __user *)arg);
136                 break;
137
138         case WDIOC_KEEPALIVE:
139                 ltq_wdt_enable();
140                 ret = 0;
141                 break;
142         }
143         return ret;
144 }
145
146 static int
147 ltq_wdt_open(struct inode *inode, struct file *file)
148 {
149         if (test_and_set_bit(0, &ltq_wdt_in_use))
150                 return -EBUSY;
151         ltq_wdt_in_use = 1;
152         ltq_wdt_enable();
153
154         return nonseekable_open(inode, file);
155 }
156
157 static int
158 ltq_wdt_release(struct inode *inode, struct file *file)
159 {
160         if (ltq_wdt_ok_to_close)
161                 ltq_wdt_disable();
162         else
163                 pr_err("ltq_wdt: watchdog closed without warning\n");
164         ltq_wdt_ok_to_close = 0;
165         clear_bit(0, &ltq_wdt_in_use);
166
167         return 0;
168 }
169
170 static const struct file_operations ltq_wdt_fops = {
171         .owner          = THIS_MODULE,
172         .write          = ltq_wdt_write,
173         .unlocked_ioctl = ltq_wdt_ioctl,
174         .open           = ltq_wdt_open,
175         .release        = ltq_wdt_release,
176         .llseek         = no_llseek,
177 };
178
179 static struct miscdevice ltq_wdt_miscdev = {
180         .minor  = WATCHDOG_MINOR,
181         .name   = "watchdog",
182         .fops   = &ltq_wdt_fops,
183 };
184
185 static int __init
186 ltq_wdt_probe(struct platform_device *pdev)
187 {
188         struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
189         struct clk *clk;
190
191         if (!res) {
192                 dev_err(&pdev->dev, "cannot obtain I/O memory region");
193                 return -ENOENT;
194         }
195         res = devm_request_mem_region(&pdev->dev, res->start,
196                 resource_size(res), dev_name(&pdev->dev));
197         if (!res) {
198                 dev_err(&pdev->dev, "cannot request I/O memory region");
199                 return -EBUSY;
200         }
201         ltq_wdt_membase = devm_ioremap_nocache(&pdev->dev, res->start,
202                 resource_size(res));
203         if (!ltq_wdt_membase) {
204                 dev_err(&pdev->dev, "cannot remap I/O memory region\n");
205                 return -ENOMEM;
206         }
207
208         /* we do not need to enable the clock as it is always running */
209         clk = clk_get(&pdev->dev, "io");
210         WARN_ON(!clk);
211         ltq_io_region_clk_rate = clk_get_rate(clk);
212         clk_put(clk);
213
214         if (ltq_reset_cause() == LTQ_RST_CAUSE_WDTRST)
215                 ltq_wdt_bootstatus = WDIOF_CARDRESET;
216
217         return misc_register(&ltq_wdt_miscdev);
218 }
219
220 static int __devexit
221 ltq_wdt_remove(struct platform_device *pdev)
222 {
223         misc_deregister(&ltq_wdt_miscdev);
224
225         if (ltq_wdt_membase)
226                 iounmap(ltq_wdt_membase);
227
228         return 0;
229 }
230
231
232 static struct platform_driver ltq_wdt_driver = {
233         .remove = __devexit_p(ltq_wdt_remove),
234         .driver = {
235                 .name = "ltq_wdt",
236                 .owner = THIS_MODULE,
237         },
238 };
239
240 static int __init
241 init_ltq_wdt(void)
242 {
243         return platform_driver_probe(&ltq_wdt_driver, ltq_wdt_probe);
244 }
245
246 static void __exit
247 exit_ltq_wdt(void)
248 {
249         return platform_driver_unregister(&ltq_wdt_driver);
250 }
251
252 module_init(init_ltq_wdt);
253 module_exit(exit_ltq_wdt);
254
255 module_param(nowayout, int, 0);
256 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
257
258 MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
259 MODULE_DESCRIPTION("Lantiq SoC Watchdog");
260 MODULE_LICENSE("GPL");
261 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);