]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/char/watchdog/mv64x60_wdt.c
[WATCHDOG] mv64x60_wdt: Check return value of nonseekable_open
[net-next-2.6.git] / drivers / char / watchdog / mv64x60_wdt.c
CommitLineData
3be10211
JC
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
3be10211
JC
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>
d052d1be
RK
24#include <linux/platform_device.h>
25
7e07a159 26#include <linux/mv643xx.h>
3be10211
JC
27#include <asm/uaccess.h>
28#include <asm/io.h>
29
8a5cfa64
DF
30#define MV64x60_WDT_WDC_OFFSET 0
31
3be10211
JC
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
42static unsigned long wdt_flags;
43static int wdt_status;
8a5cfa64 44static void __iomem *mv64x60_wdt_regs;
3be10211
JC
45static int mv64x60_wdt_timeout;
46
47static void mv64x60_wdt_reg_write(u32 val)
48{
49 /* Allow write only to CTL1 / CTL2 fields, retaining values in
50 * other fields.
51 */
8a5cfa64 52 u32 data = readl(mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
3be10211
JC
53 data &= ~(MV64x60_WDC_CTL1_MASK | MV64x60_WDC_CTL2_MASK);
54 data |= val;
8a5cfa64 55 writel(data, mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
3be10211
JC
56}
57
58static 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
65static 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
75static 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
85static 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
861e5137 93 return nonseekable_open(inode, file);
3be10211
JC
94}
95
96static 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
b2846dfa 109static ssize_t mv64x60_wdt_write(struct file *file, const char __user *data,
3be10211
JC
110 size_t len, loff_t * ppos)
111{
3be10211
JC
112 if (len)
113 mv64x60_wdt_service();
114
115 return len;
116}
117
118static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,
119 unsigned int cmd, unsigned long arg)
120{
121 int timeout;
b2846dfa 122 void __user *argp = (void __user *)arg;
3be10211
JC
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:
b2846dfa 131 if (copy_to_user(argp, &info, sizeof(info)))
3be10211
JC
132 return -EFAULT;
133 break;
134
135 case WDIOC_GETSTATUS:
136 case WDIOC_GETBOOTSTATUS:
b2846dfa 137 if (put_user(wdt_status, (int __user *)argp))
3be10211
JC
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;
b2846dfa 158 if (put_user(timeout, (int __user *)argp))
3be10211
JC
159 return -EFAULT;
160 break;
161
162 default:
795b89d2 163 return -ENOTTY;
3be10211
JC
164 }
165
166 return 0;
167}
168
62322d25 169static const struct file_operations mv64x60_wdt_fops = {
3be10211
JC
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
178static struct miscdevice mv64x60_wdt_miscdev = {
179 .minor = WATCHDOG_MINOR,
180 .name = "watchdog",
181 .fops = &mv64x60_wdt_fops,
182};
183
3ae5eaec 184static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
3be10211 185{
3ae5eaec 186 struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
3be10211 187 int bus_clk = 133;
8a5cfa64 188 struct resource *r;
3be10211
JC
189
190 mv64x60_wdt_timeout = 10;
191 if (pdata) {
192 mv64x60_wdt_timeout = pdata->timeout;
193 bus_clk = pdata->bus_clk;
194 }
195
8a5cfa64
DF
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;
3be10211
JC
203
204 writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
8a5cfa64 205 mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
3be10211
JC
206
207 return misc_register(&mv64x60_wdt_miscdev);
208}
209
3ae5eaec 210static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
3be10211
JC
211{
212 misc_deregister(&mv64x60_wdt_miscdev);
213
214 mv64x60_wdt_service();
215 mv64x60_wdt_handler_disable();
216
8a5cfa64
DF
217 iounmap(mv64x60_wdt_regs);
218
3be10211
JC
219 return 0;
220}
221
3ae5eaec 222static struct platform_driver mv64x60_wdt_driver = {
3be10211
JC
223 .probe = mv64x60_wdt_probe,
224 .remove = __devexit_p(mv64x60_wdt_remove),
3ae5eaec
RK
225 .driver = {
226 .owner = THIS_MODULE,
227 .name = MV64x60_WDT_NAME,
228 },
3be10211
JC
229};
230
3be10211
JC
231static int __init mv64x60_wdt_init(void)
232{
3be10211
JC
233 printk(KERN_INFO "MV64x60 watchdog driver\n");
234
422db8d2 235 return platform_driver_register(&mv64x60_wdt_driver);
3be10211
JC
236}
237
238static void __exit mv64x60_wdt_exit(void)
239{
3ae5eaec 240 platform_driver_unregister(&mv64x60_wdt_driver);
3be10211
JC
241}
242
243module_init(mv64x60_wdt_init);
244module_exit(mv64x60_wdt_exit);
245
246MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
247MODULE_DESCRIPTION("MV64x60 watchdog driver");
248MODULE_LICENSE("GPL");
249MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);