Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-for-linus-2.6
[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/config.h>
19 #include <linux/fs.h>
20 #include <linux/init.h>
21 #include <linux/kernel.h>
22 #include <linux/miscdevice.h>
23 #include <linux/module.h>
24 #include <linux/watchdog.h>
25 #include <asm/mv64x60.h>
26 #include <asm/uaccess.h>
27 #include <asm/io.h>
28
29 /* MV64x60 WDC (config) register access definitions */
30 #define MV64x60_WDC_CTL1_MASK   (3 << 24)
31 #define MV64x60_WDC_CTL1(val)   ((val & 3) << 24)
32 #define MV64x60_WDC_CTL2_MASK   (3 << 26)
33 #define MV64x60_WDC_CTL2(val)   ((val & 3) << 26)
34
35 /* Flags bits */
36 #define MV64x60_WDOG_FLAG_OPENED        0
37 #define MV64x60_WDOG_FLAG_ENABLED       1
38
39 static unsigned long wdt_flags;
40 static int wdt_status;
41 static void __iomem *mv64x60_regs;
42 static int mv64x60_wdt_timeout;
43
44 static void mv64x60_wdt_reg_write(u32 val)
45 {
46         /* Allow write only to CTL1 / CTL2 fields, retaining values in
47          * other fields.
48          */
49         u32 data = readl(mv64x60_regs + MV64x60_WDT_WDC);
50         data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK);
51         data |= val;
52         writel(data, mv64x60_regs + MV64x60_WDT_WDC);
53 }
54
55 static void mv64x60_wdt_service(void)
56 {
57         /* Write 01 followed by 10 to CTL2 */
58         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x01));
59         mv64x60_wdt_reg_write(MV64x60_WDC_CTL2(0x02));
60 }
61
62 static void mv64x60_wdt_handler_disable(void)
63 {
64         if (test_and_clear_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
65                 /* Write 01 followed by 10 to CTL1 */
66                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
67                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
68                 printk(KERN_NOTICE "mv64x60_wdt: watchdog deactivated\n");
69         }
70 }
71
72 static void mv64x60_wdt_handler_enable(void)
73 {
74         if (!test_and_set_bit(MV64x60_WDOG_FLAG_ENABLED, &wdt_flags)) {
75                 /* Write 01 followed by 10 to CTL1 */
76                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x01));
77                 mv64x60_wdt_reg_write(MV64x60_WDC_CTL1(0x02));
78                 printk(KERN_NOTICE "mv64x60_wdt: watchdog activated\n");
79         }
80 }
81
82 static int mv64x60_wdt_open(struct inode *inode, struct file *file)
83 {
84         if (test_and_set_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags))
85                 return -EBUSY;
86
87         mv64x60_wdt_service();
88         mv64x60_wdt_handler_enable();
89
90         return 0;
91 }
92
93 static int mv64x60_wdt_release(struct inode *inode, struct file *file)
94 {
95         mv64x60_wdt_service();
96
97 #if !defined(CONFIG_WATCHDOG_NOWAYOUT)
98         mv64x60_wdt_handler_disable();
99 #endif
100
101         clear_bit(MV64x60_WDOG_FLAG_OPENED, &wdt_flags);
102
103         return 0;
104 }
105
106 static ssize_t mv64x60_wdt_write(struct file *file, const char *data,
107                                  size_t len, loff_t * ppos)
108 {
109         if (*ppos != file->f_pos)
110                 return -ESPIPE;
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         int timeout;
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((void *)arg, &info, sizeof(info)))
131                         return -EFAULT;
132                 break;
133
134         case WDIOC_GETSTATUS:
135         case WDIOC_GETBOOTSTATUS:
136                 if (put_user(wdt_status, (int *)arg))
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                 timeout = mv64x60_wdt_timeout * HZ;
157                 if (put_user(timeout, (int *)arg))
158                         return -EFAULT;
159                 break;
160
161         default:
162                 return -ENOIOCTLCMD;
163         }
164
165         return 0;
166 }
167
168 static struct file_operations mv64x60_wdt_fops = {
169         .owner = THIS_MODULE,
170         .llseek = no_llseek,
171         .write = mv64x60_wdt_write,
172         .ioctl = mv64x60_wdt_ioctl,
173         .open = mv64x60_wdt_open,
174         .release = mv64x60_wdt_release,
175 };
176
177 static struct miscdevice mv64x60_wdt_miscdev = {
178         .minor = WATCHDOG_MINOR,
179         .name = "watchdog",
180         .fops = &mv64x60_wdt_fops,
181 };
182
183 static int __devinit mv64x60_wdt_probe(struct device *dev)
184 {
185         struct platform_device *pd = to_platform_device(dev);
186         struct mv64x60_wdt_pdata *pdata = pd->dev.platform_data;
187         int bus_clk = 133;
188
189         mv64x60_wdt_timeout = 10;
190         if (pdata) {
191                 mv64x60_wdt_timeout = pdata->timeout;
192                 bus_clk = pdata->bus_clk;
193         }
194
195         mv64x60_regs = mv64x60_get_bridge_vbase();
196
197         writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
198                mv64x60_regs + MV64x60_WDT_WDC);
199
200         return misc_register(&mv64x60_wdt_miscdev);
201 }
202
203 static int __devexit mv64x60_wdt_remove(struct device *dev)
204 {
205         misc_deregister(&mv64x60_wdt_miscdev);
206
207         mv64x60_wdt_service();
208         mv64x60_wdt_handler_disable();
209
210         return 0;
211 }
212
213 static struct device_driver mv64x60_wdt_driver = {
214         .name = MV64x60_WDT_NAME,
215         .bus = &platform_bus_type,
216         .probe = mv64x60_wdt_probe,
217         .remove = __devexit_p(mv64x60_wdt_remove),
218 };
219
220 static struct platform_device *mv64x60_wdt_dev;
221
222 static int __init mv64x60_wdt_init(void)
223 {
224         int ret;
225
226         printk(KERN_INFO "MV64x60 watchdog driver\n");
227
228         mv64x60_wdt_dev = platform_device_register_simple(MV64x60_WDT_NAME,
229                                                           -1, NULL, 0);
230         if (IS_ERR(mv64x60_wdt_dev)) {
231                 ret = PTR_ERR(mv64x60_wdt_dev);
232                 goto out;
233         }
234
235         ret = driver_register(&mv64x60_wdt_driver);
236       out:
237         return ret;
238 }
239
240 static void __exit mv64x60_wdt_exit(void)
241 {
242         driver_unregister(&mv64x60_wdt_driver);
243         platform_device_unregister(mv64x60_wdt_dev);
244 }
245
246 module_init(mv64x60_wdt_init);
247 module_exit(mv64x60_wdt_exit);
248
249 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
250 MODULE_DESCRIPTION("MV64x60 watchdog driver");
251 MODULE_LICENSE("GPL");
252 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);