]> err.no Git - linux-2.6/blob - drivers/char/watchdog/mv64x60_wdt.c
420c7b82f4ca5a1e0dba9afbda53be61ffbf7a46
[linux-2.6] / 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
47 static void mv64x60_wdt_reg_write(u32 val)
48 {
49         /* Allow write only to CTL1 / CTL2 fields, retaining values in
50          * other fields.
51          */
52         u32 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
53         data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK);
54         data |= val;
55         writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
56 }
57
58 static void mv64x60_wdt_service(void)
59 {
60         /* Write 01 followed by 10 to CTL2 */
61         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01));
62         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02));
63 }
64
65 static void mv64x60_wdt_handler_disable(void)
66 {
67         if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
68                 /* Write 01 followed by 10 to CTL1 */
69                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
70                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
71                 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
72         }
73 }
74
75 static void mv64x60_wdt_handler_enable(void)
76 {
77         if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
78                 /* Write 01 followed by 10 to CTL1 */
79                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
80                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
81                 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
82         }
83 }
84
85 static int mv64x60_wdt_open(struct inode *inode, struct file *file)
86 {
87         if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
88                 return -EBUSY;
89
90         mv64x60_wdt_service();
91         mv64x60_wdt_handler_enable();
92
93         return nonseekable_open(inode, file);
94 }
95
96 static int mv64x60_wdt_release(struct inode *inode, struct file *file)
97 {
98         mv64x60_wdt_service();
99
100 #if !defined(CONFIG_WATCHDOG_NOWAYOUT)
101         mv64x60_wdt_handler_disable();
102 #endif
103
104         clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
105
106         return 0;
107 }
108
109 static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
110                                  size_t len, loff_t * ppos)
111 {
112         if (len)
113                 mv64x60_wdt_service();
114
115         return len;
116 }
117
118 static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,
119                              unsigned int cmd, unsigned long arg)
120 {
121         void __user *argp = (void __user *)arg;
122         static struct watchdog_info info = {
123                 .options = WDIOF_KEEPALIVEPING,
124                 .firmware_version = 0,
125                 .identity = "MV64x60 watchdog",
126         };
127
128         switch (cmd) {
129         case WDIOC_GETSUPPORT:
130                 if (copy_to_user(argp, &info, sizeof(info)))
131                         return -EFAULT;
132                 break;
133
134         case WDIOC_GETSTATUS:
135         case WDIOC_GETBOOTSTATUS:
136                 if (put_user(wdt_status, (int __user *)argp))
137                         return -EFAULT;
138                 wdt_status &= ~WDIOF_KEEPALIVEPING;
139                 break;
140
141         case WDIOC_GETTEMP:
142                 return -EOPNOTSUPP;
143
144         case WDIOC_SETOPTIONS:
145                 return -EOPNOTSUPP;
146
147         case WDIOC_KEEPALIVE:
148                 mv64x60_wdt_service();
149                 wdt_status |= WDIOF_KEEPALIVEPING;
150                 break;
151
152         case WDIOC_SETTIMEOUT:
153                 return -EOPNOTSUPP;
154
155         case WDIOC_GETTIMEOUT:
156                 if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
157                         return -EFAULT;
158                 break;
159
160         default:
161                 return -ENOTTY;
162         }
163
164         return 0;
165 }
166
167 static const struct file_operations mv64x60_wdt_fops = {
168         .owner = THIS_MODULE,
169         .llseek = no_llseek,
170         .write = mv64x60_wdt_write,
171         .ioctl = mv64x60_wdt_ioctl,
172         .open = mv64x60_wdt_open,
173         .release = mv64x60_wdt_release,
174 };
175
176 static struct miscdevice mv64x60_wdt_miscdev = {
177         .minor = WATCHDOG_MINOR,
178         .name = "watchdog",
179         .fops = &mv64x60_wdt_fops,
180 };
181
182 static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
183 {
184         struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
185         int bus_clk = 133;
186         struct resource *r;
187
188         mv64x60_wdt_timeout = 10;
189         if (pdata) {
190                 mv64x60_wdt_timeout = pdata->timeout;
191                 bus_clk = pdata->bus_clk;
192         }
193
194         r = platform_get_resource(dev, IORESOURCE_MEM, 0);
195         if (!r)
196                 return -ENODEV;
197
198         mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);
199         if (mv64x60_wdt_regs == NULL)
200                 return -ENOMEM;
201
202         writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
203                mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
204
205         return misc_register(&mv64x60_wdt_miscdev);
206 }
207
208 static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
209 {
210         misc_deregister(&mv64x60_wdt_miscdev);
211
212         mv64x60_wdt_service();
213         mv64x60_wdt_handler_disable();
214
215         iounmap(mv64x60_wdt_regs);
216
217         return 0;
218 }
219
220 static struct platform_driver mv64x60_wdt_driver = {
221         .probe = mv64x60_wdt_probe,
222         .remove = __devexit_p(mv64x60_wdt_remove),
223         .driver = {
224                 .owner = THIS_MODULE,
225                 .name = MV64x60_WDT_NAME,
226         },
227 };
228
229 static int __init mv64x60_wdt_init(void)
230 {
231         printk(KERN_INFO "MV64x60 watchdog driver\n");
232
233         return platform_driver_register(&mv64x60_wdt_driver);
234 }
235
236 static void __exit mv64x60_wdt_exit(void)
237 {
238         platform_driver_unregister(&mv64x60_wdt_driver);
239 }
240
241 module_init(mv64x60_wdt_init);
242 module_exit(mv64x60_wdt_exit);
243
244 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
245 MODULE_DESCRIPTION("MV64x60 watchdog driver");
246 MODULE_LICENSE("GPL");
247 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);