]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/char/watchdog/mv64x60_wdt.c
7b4812776382e0c0bf7dae0aeb976402509f3462
[karo-tx-linux.git] / drivers / char / watchdog / mv64x60_wdt.c
1 /*
2  * mv64x60_wdt.c - MV64X60 (Marvell Discovery) watchdog userspace interface
3  *
4  * Author: James Chapman <jchapman@katalix.com>
5  *
6  * Platform-specific setup code should configure the dog to generate
7  * interrupt or reset as required.  This code only enables/disables
8  * and services the watchdog.
9  *
10  * Derived from mpc8xx_wdt.c, with the following copyright.
11  * 
12  * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
13  * the terms of the GNU General Public License version 2. This program
14  * is licensed "as is" without any warranty of any kind, whether express
15  * or implied.
16  */
17
18 #include <linux/fs.h>
19 #include <linux/init.h>
20 #include <linux/kernel.h>
21 #include <linux/miscdevice.h>
22 #include <linux/module.h>
23 #include <linux/watchdog.h>
24 #include <linux/platform_device.h>
25
26 #include <linux/mv643xx.h>
27 #include <asm/uaccess.h>
28 #include <asm/io.h>
29
30 #define MV64x60_WDT_WDC_OFFSET  0
31
32 /* MV64x60 WDC (config) register access definitions */
33 #define MV64x60_WDC_CTL1_MASK   (3 << 24)
34 #define MV64x60_WDC_CTL1(val)   ((val & 3) << 24)
35 #define MV64x60_WDC_CTL2_MASK   (3 << 26)
36 #define MV64x60_WDC_CTL2(val)   ((val & 3) << 26)
37
38 /* Flags bits */
39 #define MV64x60_WDOG_FLAG_OPENED        0
40 #define MV64x60_WDOG_FLAG_ENABLED       1
41
42 static unsigned long wdt_flags;
43 static int wdt_status;
44 static void __iomem *mv64x60_wdt_regs;
45 static int mv64x60_wdt_timeout;
46 static unsigned int bus_clk;
47
48 static void mv64x60_wdt_reg_write(u32 val)
49 {
50         /* Allow write only to CTL1 / CTL2 fields, retaining values in
51          * other fields.
52          */
53         u32 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
54         data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK);
55         data |= val;
56         writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
57 }
58
59 static void mv64x60_wdt_service(void)
60 {
61         /* Write 01 followed by 10 to CTL2 */
62         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01));
63         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02));
64 }
65
66 static void mv64x60_wdt_handler_disable(void)
67 {
68         if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
69                 /* Write 01 followed by 10 to CTL1 */
70                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
71                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
72                 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
73         }
74 }
75
76 static void mv64x60_wdt_handler_enable(void)
77 {
78         if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
79                 /* Write 01 followed by 10 to CTL1 */
80                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
81                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
82                 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
83         }
84 }
85
86 static void mv64x60_wdt_set_timeout(int timeout)
87 {
88         /* maximum bus cycle count is 0xFFFFFFFF */
89         if (timeout > 0xFFFFFFFF / bus_clk)
90                 timeout = 0xFFFFFFFF / bus_clk;
91
92         mv64x60_wdt_timeout = timeout;
93         writel((timeout * bus_clk) >> 8,
94                mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
95         mv64x60_wdt_service();
96 }
97
98 static int mv64x60_wdt_open(struct inode *inode, struct file *file)
99 {
100         if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
101                 return -EBUSY;
102
103         mv64x60_wdt_service();
104         mv64x60_wdt_handler_enable();
105
106         return nonseekable_open(inode, file);
107 }
108
109 static int mv64x60_wdt_release(struct inode *inode, struct file *file)
110 {
111         mv64x60_wdt_service();
112
113 #if !defined(CONFIG_WATCHDOG_NOWAYOUT)
114         mv64x60_wdt_handler_disable();
115 #endif
116
117         clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
118
119         return 0;
120 }
121
122 static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
123                                  size_t len, loff_t * ppos)
124 {
125         if (len)
126                 mv64x60_wdt_service();
127
128         return len;
129 }
130
131 static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,
132                              unsigned int cmd, unsigned long arg)
133 {
134         int timeout;
135         int options;
136         void __user *argp = (void __user *)arg;
137         static struct watchdog_info info = {
138                 .options =      WDIOF_SETTIMEOUT        |
139                                 WDIOF_KEEPALIVEPING,
140                 .firmware_version = 0,
141                 .identity = "MV64x60 watchdog",
142         };
143
144         switch (cmd) {
145         case WDIOC_GETSUPPORT:
146                 if (copy_to_user(argp, &info, sizeof(info)))
147                         return -EFAULT;
148                 break;
149
150         case WDIOC_GETSTATUS:
151         case WDIOC_GETBOOTSTATUS:
152                 if (put_user(wdt_status, (int __user *)argp))
153                         return -EFAULT;
154                 wdt_status &= ~WDIOF_KEEPALIVEPING;
155                 break;
156
157         case WDIOC_GETTEMP:
158                 return -EOPNOTSUPP;
159
160         case WDIOC_SETOPTIONS:
161                 if (get_user(options, (int __user *)argp))
162                         return -EFAULT;
163
164                 if (options & WDIOS_DISABLECARD)
165                         mv64x60_wdt_handler_disable();
166
167                 if (options & WDIOS_ENABLECARD)
168                         mv64x60_wdt_handler_enable();
169                 break;
170
171         case WDIOC_KEEPALIVE:
172                 mv64x60_wdt_service();
173                 wdt_status |= WDIOF_KEEPALIVEPING;
174                 break;
175
176         case WDIOC_SETTIMEOUT:
177                 if (get_user(timeout, (int __user *)argp))
178                         return -EFAULT;
179                 mv64x60_wdt_set_timeout(timeout);
180                 /* Fall through */
181
182         case WDIOC_GETTIMEOUT:
183                 if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
184                         return -EFAULT;
185                 break;
186
187         default:
188                 return -ENOTTY;
189         }
190
191         return 0;
192 }
193
194 static const struct file_operations mv64x60_wdt_fops = {
195         .owner = THIS_MODULE,
196         .llseek = no_llseek,
197         .write = mv64x60_wdt_write,
198         .ioctl = mv64x60_wdt_ioctl,
199         .open = mv64x60_wdt_open,
200         .release = mv64x60_wdt_release,
201 };
202
203 static struct miscdevice mv64x60_wdt_miscdev = {
204         .minor = WATCHDOG_MINOR,
205         .name = "watchdog",
206         .fops = &mv64x60_wdt_fops,
207 };
208
209 static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
210 {
211         struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
212         struct resource *r;
213         int timeout = 10;
214
215         bus_clk = 133;                  /* in MHz */
216         if (pdata) {
217                 timeout = pdata->timeout;
218                 bus_clk = pdata->bus_clk;
219         }
220
221         /* Since bus_clk is truncated MHz, actual frequency could be
222          * up to 1MHz higher.  Round up, since it's better to time out
223          * too late than too soon.
224          */
225         bus_clk++;
226         bus_clk *= 1000000;             /* convert to Hz */
227
228         r = platform_get_resource(dev, IORESOURCE_MEM, 0);
229         if (!r)
230                 return -ENODEV;
231
232         mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);
233         if (mv64x60_wdt_regs == NULL)
234                 return -ENOMEM;
235
236         mv64x60_wdt_set_timeout(timeout);
237
238         return misc_register(&mv64x60_wdt_miscdev);
239 }
240
241 static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
242 {
243         misc_deregister(&mv64x60_wdt_miscdev);
244
245         mv64x60_wdt_service();
246         mv64x60_wdt_handler_disable();
247
248         iounmap(mv64x60_wdt_regs);
249
250         return 0;
251 }
252
253 static struct platform_driver mv64x60_wdt_driver = {
254         .probe = mv64x60_wdt_probe,
255         .remove = __devexit_p(mv64x60_wdt_remove),
256         .driver = {
257                 .owner = THIS_MODULE,
258                 .name = MV64x60_WDT_NAME,
259         },
260 };
261
262 static int __init mv64x60_wdt_init(void)
263 {
264         printk(KERN_INFO "MV64x60 watchdog driver\n");
265
266         return platform_driver_register(&mv64x60_wdt_driver);
267 }
268
269 static void __exit mv64x60_wdt_exit(void)
270 {
271         platform_driver_unregister(&mv64x60_wdt_driver);
272 }
273
274 module_init(mv64x60_wdt_init);
275 module_exit(mv64x60_wdt_exit);
276
277 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
278 MODULE_DESCRIPTION("MV64x60 watchdog driver");
279 MODULE_LICENSE("GPL");
280 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);