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