]> bbs.cooldavid.org Git - net-next-2.6.git/blame - drivers/char/watchdog/mv64x60_wdt.c
[WATCHDOG] mv64x60_wdt: Fix WDIOC_GETTIMEOUT return value
[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{
b2846dfa 121 void __user *argp = (void __user *)arg;
3be10211
JC
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:
b2846dfa 130 if (copy_to_user(argp, &info, sizeof(info)))
3be10211
JC
131 return -EFAULT;
132 break;
133
134 case WDIOC_GETSTATUS:
135 case WDIOC_GETBOOTSTATUS:
b2846dfa 136 if (put_user(wdt_status, (int __user *)argp))
3be10211
JC
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:
264f0991 156 if (put_user(mv64x60_wdt_timeout, (int __user *)argp))
3be10211
JC
157 return -EFAULT;
158 break;
159
160 default:
795b89d2 161 return -ENOTTY;
3be10211
JC
162 }
163
164 return 0;
165}
166
62322d25 167static const struct file_operations mv64x60_wdt_fops = {
3be10211
JC
168 .owner = THIS_MODULE,
169 .llseek = no_llseek,
170 .write = mv64x60_wdt_write,
171 .ioctl = mv64x60_wdt_ioctl,
172 .open = mv64x60_wdt_open,
173 .release = mv64x60_wdt_release,
174};
175
176static struct miscdevice mv64x60_wdt_miscdev = {
177 .minor = WATCHDOG_MINOR,
178 .name = "watchdog",
179 .fops = &mv64x60_wdt_fops,
180};
181
3ae5eaec 182static int __devinit mv64x60_wdt_probe(struct platform_device *dev)
3be10211 183{
3ae5eaec 184 struct mv64x60_wdt_pdata *pdata = dev->dev.platform_data;
3be10211 185 int bus_clk = 133;
8a5cfa64 186 struct resource *r;
3be10211
JC
187
188 mv64x60_wdt_timeout = 10;
189 if (pdata) {
190 mv64x60_wdt_timeout = pdata->timeout;
191 bus_clk = pdata->bus_clk;
192 }
193
8a5cfa64
DF
194 r = platform_get_resource(dev, IORESOURCE_MEM, 0);
195 if (!r)
196 return -ENODEV;
197
198 mv64x60_wdt_regs = ioremap(r->start, r->end - r->start + 1);
199 if (mv64x60_wdt_regs == NULL)
200 return -ENOMEM;
3be10211
JC
201
202 writel((mv64x60_wdt_timeout * (bus_clk * 1000000)) >> 8,
8a5cfa64 203 mv64x60_wdt_regs + MV64x60_WDT_WDC_OFFSET);
3be10211
JC
204
205 return misc_register(&mv64x60_wdt_miscdev);
206}
207
3ae5eaec 208static int __devexit mv64x60_wdt_remove(struct platform_device *dev)
3be10211
JC
209{
210 misc_deregister(&mv64x60_wdt_miscdev);
211
212 mv64x60_wdt_service();
213 mv64x60_wdt_handler_disable();
214
8a5cfa64
DF
215 iounmap(mv64x60_wdt_regs);
216
3be10211
JC
217 return 0;
218}
219
3ae5eaec 220static struct platform_driver mv64x60_wdt_driver = {
3be10211
JC
221 .probe = mv64x60_wdt_probe,
222 .remove = __devexit_p(mv64x60_wdt_remove),
3ae5eaec
RK
223 .driver = {
224 .owner = THIS_MODULE,
225 .name = MV64x60_WDT_NAME,
226 },
3be10211
JC
227};
228
3be10211
JC
229static int __init mv64x60_wdt_init(void)
230{
3be10211
JC
231 printk(KERN_INFO "MV64x60 watchdog driver\n");
232
422db8d2 233 return platform_driver_register(&mv64x60_wdt_driver);
3be10211
JC
234}
235
236static void __exit mv64x60_wdt_exit(void)
237{
3ae5eaec 238 platform_driver_unregister(&mv64x60_wdt_driver);
3be10211
JC
239}
240
241module_init(mv64x60_wdt_init);
242module_exit(mv64x60_wdt_exit);
243
244MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
245MODULE_DESCRIPTION("MV64x60 watchdog driver");
246MODULE_LICENSE("GPL");
247MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);