diff options
Diffstat (limited to 'drivers/watchdog')
-rw-r--r-- | drivers/watchdog/Makefile | 2 | ||||
-rw-r--r-- | drivers/watchdog/riowd.c | 259 |
2 files changed, 261 insertions, 0 deletions
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index ca3dc043d786..57c7e8bd8c88 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile | |||
@@ -122,6 +122,8 @@ obj-$(CONFIG_SH_WDT) += shwdt.o | |||
122 | 122 | ||
123 | # SPARC64 Architecture | 123 | # SPARC64 Architecture |
124 | 124 | ||
125 | obj-$(CONFIG_WATCHDOG_RIO) += riowd.o | ||
126 | |||
125 | # XTENSA Architecture | 127 | # XTENSA Architecture |
126 | 128 | ||
127 | # Architecture Independant | 129 | # Architecture Independant |
diff --git a/drivers/watchdog/riowd.c b/drivers/watchdog/riowd.c new file mode 100644 index 000000000000..98bb60ff02f2 --- /dev/null +++ b/drivers/watchdog/riowd.c | |||
@@ -0,0 +1,259 @@ | |||
1 | /* riowd.c - driver for hw watchdog inside Super I/O of RIO | ||
2 | * | ||
3 | * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) | ||
4 | */ | ||
5 | |||
6 | #include <linux/kernel.h> | ||
7 | #include <linux/module.h> | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/fs.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/miscdevice.h> | ||
13 | #include <linux/smp_lock.h> | ||
14 | #include <linux/watchdog.h> | ||
15 | #include <linux/of.h> | ||
16 | #include <linux/of_device.h> | ||
17 | |||
18 | #include <asm/io.h> | ||
19 | #include <asm/uaccess.h> | ||
20 | |||
21 | |||
22 | /* RIO uses the NatSemi Super I/O power management logical device | ||
23 | * as its' watchdog. | ||
24 | * | ||
25 | * When the watchdog triggers, it asserts a line to the BBC (Boot Bus | ||
26 | * Controller) of the machine. The BBC can only be configured to | ||
27 | * trigger a power-on reset when the signal is asserted. The BBC | ||
28 | * can be configured to ignore the signal entirely as well. | ||
29 | * | ||
30 | * The only Super I/O device register we care about is at index | ||
31 | * 0x05 (WDTO_INDEX) which is the watchdog time-out in minutes (1-255). | ||
32 | * If set to zero, this disables the watchdog. When set, the system | ||
33 | * must periodically (before watchdog expires) clear (set to zero) and | ||
34 | * re-set the watchdog else it will trigger. | ||
35 | * | ||
36 | * There are two other indexed watchdog registers inside this Super I/O | ||
37 | * logical device, but they are unused. The first, at index 0x06 is | ||
38 | * the watchdog control and can be used to make the watchdog timer re-set | ||
39 | * when the PS/2 mouse or serial lines show activity. The second, at | ||
40 | * index 0x07 is merely a sampling of the line from the watchdog to the | ||
41 | * BBC. | ||
42 | * | ||
43 | * The watchdog device generates no interrupts. | ||
44 | */ | ||
45 | |||
46 | MODULE_AUTHOR("David S. Miller <davem@davemloft.net>"); | ||
47 | MODULE_DESCRIPTION("Hardware watchdog driver for Sun RIO"); | ||
48 | MODULE_SUPPORTED_DEVICE("watchdog"); | ||
49 | MODULE_LICENSE("GPL"); | ||
50 | |||
51 | #define RIOWD_NAME "pmc" | ||
52 | #define PFX RIOWD_NAME ": " | ||
53 | |||
54 | struct riowd { | ||
55 | void __iomem *regs; | ||
56 | spinlock_t lock; | ||
57 | }; | ||
58 | |||
59 | static struct riowd *riowd_device; | ||
60 | |||
61 | #define WDTO_INDEX 0x05 | ||
62 | |||
63 | static int riowd_timeout = 1; /* in minutes */ | ||
64 | module_param(riowd_timeout, int, 0); | ||
65 | MODULE_PARM_DESC(riowd_timeout, "Watchdog timeout in minutes"); | ||
66 | |||
67 | static void riowd_writereg(struct riowd *p, u8 val, int index) | ||
68 | { | ||
69 | unsigned long flags; | ||
70 | |||
71 | spin_lock_irqsave(&p->lock, flags); | ||
72 | writeb(index, p->regs + 0); | ||
73 | writeb(val, p->regs + 1); | ||
74 | spin_unlock_irqrestore(&p->lock, flags); | ||
75 | } | ||
76 | |||
77 | static int riowd_open(struct inode *inode, struct file *filp) | ||
78 | { | ||
79 | cycle_kernel_lock(); | ||
80 | nonseekable_open(inode, filp); | ||
81 | return 0; | ||
82 | } | ||
83 | |||
84 | static int riowd_release(struct inode *inode, struct file *filp) | ||
85 | { | ||
86 | return 0; | ||
87 | } | ||
88 | |||
89 | static int riowd_ioctl(struct inode *inode, struct file *filp, | ||
90 | unsigned int cmd, unsigned long arg) | ||
91 | { | ||
92 | static struct watchdog_info info = { | ||
93 | .options = WDIOF_SETTIMEOUT, | ||
94 | .firmware_version = 1, | ||
95 | .identity = "riowd", | ||
96 | }; | ||
97 | void __user *argp = (void __user *)arg; | ||
98 | struct riowd *p = riowd_device; | ||
99 | unsigned int options; | ||
100 | int new_margin; | ||
101 | |||
102 | switch (cmd) { | ||
103 | case WDIOC_GETSUPPORT: | ||
104 | if (copy_to_user(argp, &info, sizeof(info))) | ||
105 | return -EFAULT; | ||
106 | break; | ||
107 | |||
108 | case WDIOC_GETSTATUS: | ||
109 | case WDIOC_GETBOOTSTATUS: | ||
110 | if (put_user(0, (int __user *)argp)) | ||
111 | return -EFAULT; | ||
112 | break; | ||
113 | |||
114 | case WDIOC_KEEPALIVE: | ||
115 | riowd_writereg(p, riowd_timeout, WDTO_INDEX); | ||
116 | break; | ||
117 | |||
118 | case WDIOC_SETOPTIONS: | ||
119 | if (copy_from_user(&options, argp, sizeof(options))) | ||
120 | return -EFAULT; | ||
121 | |||
122 | if (options & WDIOS_DISABLECARD) | ||
123 | riowd_writereg(p, 0, WDTO_INDEX); | ||
124 | else if (options & WDIOS_ENABLECARD) | ||
125 | riowd_writereg(p, riowd_timeout, WDTO_INDEX); | ||
126 | else | ||
127 | return -EINVAL; | ||
128 | |||
129 | break; | ||
130 | |||
131 | case WDIOC_SETTIMEOUT: | ||
132 | if (get_user(new_margin, (int __user *)argp)) | ||
133 | return -EFAULT; | ||
134 | if ((new_margin < 60) || (new_margin > (255 * 60))) | ||
135 | return -EINVAL; | ||
136 | riowd_timeout = (new_margin + 59) / 60; | ||
137 | riowd_writereg(p, riowd_timeout, WDTO_INDEX); | ||
138 | /* Fall */ | ||
139 | |||
140 | case WDIOC_GETTIMEOUT: | ||
141 | return put_user(riowd_timeout * 60, (int __user *)argp); | ||
142 | |||
143 | default: | ||
144 | return -EINVAL; | ||
145 | }; | ||
146 | |||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | static ssize_t riowd_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) | ||
151 | { | ||
152 | struct riowd *p = riowd_device; | ||
153 | |||
154 | if (count) { | ||
155 | riowd_writereg(p, riowd_timeout, WDTO_INDEX); | ||
156 | return 1; | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static const struct file_operations riowd_fops = { | ||
163 | .owner = THIS_MODULE, | ||
164 | .llseek = no_llseek, | ||
165 | .ioctl = riowd_ioctl, | ||
166 | .open = riowd_open, | ||
167 | .write = riowd_write, | ||
168 | .release = riowd_release, | ||
169 | }; | ||
170 | |||
171 | static struct miscdevice riowd_miscdev = { | ||
172 | .minor = WATCHDOG_MINOR, | ||
173 | .name = "watchdog", | ||
174 | .fops = &riowd_fops | ||
175 | }; | ||
176 | |||
177 | static int __devinit riowd_probe(struct of_device *op, | ||
178 | const struct of_device_id *match) | ||
179 | { | ||
180 | struct riowd *p; | ||
181 | int err = -EINVAL; | ||
182 | |||
183 | if (riowd_device) | ||
184 | goto out; | ||
185 | |||
186 | err = -ENOMEM; | ||
187 | p = kzalloc(sizeof(*p), GFP_KERNEL); | ||
188 | if (!p) | ||
189 | goto out; | ||
190 | |||
191 | spin_lock_init(&p->lock); | ||
192 | |||
193 | p->regs = of_ioremap(&op->resource[0], 0, 2, "riowd"); | ||
194 | if (!p->regs) { | ||
195 | printk(KERN_ERR PFX "Cannot map registers.\n"); | ||
196 | goto out_free; | ||
197 | } | ||
198 | |||
199 | err = misc_register(&riowd_miscdev); | ||
200 | if (err) { | ||
201 | printk(KERN_ERR PFX "Cannot register watchdog misc device.\n"); | ||
202 | goto out_iounmap; | ||
203 | } | ||
204 | |||
205 | printk(KERN_INFO PFX "Hardware watchdog [%i minutes], " | ||
206 | "regs at %p\n", riowd_timeout, p->regs); | ||
207 | |||
208 | dev_set_drvdata(&op->dev, p); | ||
209 | riowd_device = p; | ||
210 | err = 0; | ||
211 | |||
212 | out_iounmap: | ||
213 | of_iounmap(&op->resource[0], p->regs, 2); | ||
214 | |||
215 | out_free: | ||
216 | kfree(p); | ||
217 | |||
218 | out: | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | static int __devexit riowd_remove(struct of_device *op) | ||
223 | { | ||
224 | struct riowd *p = dev_get_drvdata(&op->dev); | ||
225 | |||
226 | misc_deregister(&riowd_miscdev); | ||
227 | of_iounmap(&op->resource[0], p->regs, 2); | ||
228 | kfree(p); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | static struct of_device_id riowd_match[] = { | ||
234 | { | ||
235 | .name = "pmc", | ||
236 | }, | ||
237 | {}, | ||
238 | }; | ||
239 | MODULE_DEVICE_TABLE(of, riowd_match); | ||
240 | |||
241 | static struct of_platform_driver riowd_driver = { | ||
242 | .name = "riowd", | ||
243 | .match_table = riowd_match, | ||
244 | .probe = riowd_probe, | ||
245 | .remove = __devexit_p(riowd_remove), | ||
246 | }; | ||
247 | |||
248 | static int __init riowd_init(void) | ||
249 | { | ||
250 | return of_register_driver(&riowd_driver, &of_bus_type); | ||
251 | } | ||
252 | |||
253 | static void __exit riowd_exit(void) | ||
254 | { | ||
255 | of_unregister_driver(&riowd_driver); | ||
256 | } | ||
257 | |||
258 | module_init(riowd_init); | ||
259 | module_exit(riowd_exit); | ||