]> pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/char/watchdog/mv64x60_wdt.c
f1516b4b22a1a1fe161e1cc252aa61bbf2eabde2
[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
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         int timeout;
122         void __user *argp = (void __user *)arg;
123         static struct watchdog_info info = {
124                 .options = WDIOF_KEEPALIVEPING,
125                 .firmware_version = 0,
126                 .identity = "MV64x60 watchdog",
127         };
128
129         switch (cmd) {
130         case WDIOC_GETSUPPORT:
131                 if (copy_to_user(argp, &info, sizeof(info)))
132                         return -EFAULT;
133                 break;
134
135         case WDIOC_GETSTATUS:
136         case WDIOC_GETBOOTSTATUS:
137                 if (put_user(wdt_status, (int __user *)argp))
138                         return -EFAULT;
139                 wdt_status &= ~WDIOF_KEEPALIVEPING;
140                 break;
141
142         case WDIOC_GETTEMP:
143                 return -EOPNOTSUPP;
144
145         case WDIOC_SETOPTIONS:
146                 return -EOPNOTSUPP;
147
148         case WDIOC_KEEPALIVE:
149                 mv64x60_wdt_service();
150                 wdt_status |= WDIOF_KEEPALIVEPING;
151                 break;
152
153         case WDIOC_SETTIMEOUT:
154                 return -EOPNOTSUPP;
155
156         case WDIOC_GETTIMEOUT:
157                 timeout = mv64x60_wdt_timeout * HZ;
158                 if (put_user(timeout, (int __user *)argp))
159                         return -EFAULT;
160                 break;
161
162         default:
163                 return -ENOTTY;
164         }
165
166         return 0;
167 }
168
169 static const struct file_operations mv64x60_wdt_fops = {
170         .owner = THIS_MODULE,
171         .llseek = no_llseek,
172         .write = mv64x60_wdt_write,
173         .ioctl = mv64x60_wdt_ioctl,
174         .open = mv64x60_wdt_open,
175         .release = mv64x60_wdt_release,
176 };
177
178 static struct miscdevice mv64x60_wdt_miscdev = {
179         .minor = WATCHDOG_MINOR,
180         .name = "watchdog",
181         .fops = &mv64x60_wdt_fops,
182 };
183
184 static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
185 {
186         struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
187         int bus_clk = 133;
188         struct resource *r;
189
190         mv64x60_wdt_timeout = 10;
191         if (pdata) {
192                 mv64x60_wdt_timeout = pdata->timeout;
193                 bus_clk = pdata->bus_clk;
194         }
195
196         r = platform_get_resource(dev, IORESOURCE_MEM, 0);
197         if (!r)
198                 return -ENODEV;
199
200         mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);
201         if (mv64x60_wdt_regs == NULL)
202                 return -ENOMEM;
203
204         writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
205                mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
206
207         return misc_register(&mv64x60_wdt_miscdev);
208 }
209
210 static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
211 {
212         misc_deregister(&mv64x60_wdt_miscdev);
213
214         mv64x60_wdt_service();
215         mv64x60_wdt_handler_disable();
216
217         iounmap(mv64x60_wdt_regs);
218
219         return 0;
220 }
221
222 static struct platform_driver mv64x60_wdt_driver = {
223         .probe = mv64x60_wdt_probe,
224         .remove = __devexit_p(mv64x60_wdt_remove),
225         .driver = {
226                 .owner = THIS_MODULE,
227                 .name = MV64x60_WDT_NAME,
228         },
229 };
230
231 static int __init mv64x60_wdt_init(void)
232 {
233         printk(KERN_INFO "MV64x60 watchdog driver\n");
234
235         return platform_driver_register(&mv64x60_wdt_driver);
236 }
237
238 static void __exit mv64x60_wdt_exit(void)
239 {
240         platform_driver_unregister(&mv64x60_wdt_driver);
241 }
242
243 module_init(mv64x60_wdt_init);
244 module_exit(mv64x60_wdt_exit);
245
246 MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
247 MODULE_DESCRIPTION("MV64x60 watchdog driver");
248 MODULE_LICENSE("GPL");
249 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);