]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/char/watchdog/mv64x60_wdt.c
[WATCHDOG] mv64x60_wdt: Support for WDIOC_SETTIMEOUT ioctl
[linux-2.6-omap-h63xx.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         void __user *argp = (void __user *)arg;
136         static struct watchdog_info info = {
137                 .options =      WDIOF_SETTIMEOUT        |
138                                 WDIOF_KEEPALIVEPING,
139                 .firmware_version = 0,
140                 .identity = "MV64x60 watchdog",
141         };
142
143         switch (cmd) {
144         case WDIOC_GETSUPPORT:
145                 if (copy_to_user(argp, &info, sizeof(info)))
146                         return -EFAULT;
147                 break;
148
149         case WDIOC_GETSTATUS:
150         case WDIOC_GETBOOTSTATUS:
151                 if (put_user(wdt_status, (int __user *)argp))
152                         return -EFAULT;
153                 wdt_status &= ~WDIOF_KEEPALIVEPING;
154                 break;
155
156         case WDIOC_GETTEMP:
157                 return -EOPNOTSUPP;
158
159         case WDIOC_SETOPTIONS:
160                 return -EOPNOTSUPP;
161
162         case WDIOC_KEEPALIVE:
163                 mv64x60_wdt_service();
164                 wdt_status |= WDIOF_KEEPALIVEPING;
165                 break;
166
167         case WDIOC_SETTIMEOUT:
168                 if (get_user(timeout, (int __user *)argp))
169                         return -EFAULT;
170                 mv64x60_wdt_set_timeout(timeout);
171                 /* Fall through */
172
173         case WDIOC_GETTIMEOUT:
174                 if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
175                         return -EFAULT;
176                 break;
177
178         default:
179                 return -ENOTTY;
180         }
181
182         return 0;
183 }
184
185 static const struct file_operations mv64x60_wdt_fops = {
186         .owner = THIS_MODULE,
187         .llseek = no_llseek,
188         .write = mv64x60_wdt_write,
189         .ioctl = mv64x60_wdt_ioctl,
190         .open = mv64x60_wdt_open,
191         .release = mv64x60_wdt_release,
192 };
193
194 static struct miscdevice mv64x60_wdt_miscdev = {
195         .minor = WATCHDOG_MINOR,
196         .name = "watchdog",
197         .fops = &mv64x60_wdt_fops,
198 };
199
200 static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
201 {
202         struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
203         struct resource *r;
204         int timeout = 10;
205
206         bus_clk = 133;                  /* in MHz */
207         if (pdata) {
208                 timeout = pdata->timeout;
209                 bus_clk = pdata->bus_clk;
210         }
211
212         /* Since bus_clk is truncated MHz, actual frequency could be
213          * up to 1MHz higher.  Round up, since it's better to time out
214          * too late than too soon.
215          */
216         bus_clk++;
217         bus_clk *= 1000000;             /* convert to Hz */
218
219         r = platform_get_resource(dev, IORESOURCE_MEM, 0);
220         if (!r)
221                 return -ENODEV;
222
223         mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);
224         if (mv64x60_wdt_regs == NULL)
225                 return -ENOMEM;
226
227         mv64x60_wdt_set_timeout(timeout);
228
229         return misc_register(&mv64x60_wdt_miscdev);
230 }
231
232 static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
233 {
234         misc_deregister(&mv64x60_wdt_miscdev);
235
236         mv64x60_wdt_service();
237         mv64x60_wdt_handler_disable();
238
239         iounmap(mv64x60_wdt_regs);
240
241         return 0;
242 }
243
244 static struct platform_driver mv64x60_wdt_driver = {
245         .probe = mv64x60_wdt_probe,
246         .remove = __devexit_p(mv64x60_wdt_remove),
247         .driver = {
248                 .owner = THIS_MODULE,
249                 .name = MV64x60_WDT_NAME,
250         },
251 };
252
253 static int __init mv64x60_wdt_init(void)
254 {
255         printk(KERN_INFO "MV64x60 watchdog driver\n");
256
257         return platform_driver_register(&mv64x60_wdt_driver);
258 }
259
260 static void __exit mv64x60_wdt_exit(void)
261 {
262         platform_driver_unregister(&mv64x60_wdt_driver);
263 }
264
265 module_init(mv64x60_wdt_init);
266 module_exit(mv64x60_wdt_exit);
267
268 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
269 MODULE_DESCRIPTION("MV64x60 watchdog driver");
270 MODULE_LICENSE("GPL");
271 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);