aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-23 19:36:30 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-10-23 19:36:30 -0400
commitaf76bbabbdf5cebea6a3863446f9f74b469c4bdc (patch)
tree04f171157bd4c43a7fff841f310cb543ec31966c /drivers
parent2024da603978882d102a34d47828a205fffb338e (diff)
parent06063e26bc3ab62aa7aca874c6ce9e7638673838 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog
* git://git.kernel.org/pub/scm/linux/kernel/git/wim/linux-2.6-watchdog: [WATCHDOG] Documentation/watchdog/src/watchdog-simple.c: improve this code [WATCHDOG] AR7: watchdog timer [WATCHDOG] Linux kernel IPC SBC Watchdog Timer driver
Diffstat (limited to 'drivers')
-rw-r--r--drivers/watchdog/Kconfig13
-rw-r--r--drivers/watchdog/Makefile1
-rw-r--r--drivers/watchdog/ar7_wdt.c349
3 files changed, 360 insertions, 3 deletions
diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 37bddc1802de..81db48f07ca1 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -343,11 +343,12 @@ config IBMASR
343 module will be called ibmasr. 343 module will be called ibmasr.
344 344
345config WAFER_WDT 345config WAFER_WDT
346 tristate "ICP Wafer 5823 Single Board Computer Watchdog" 346 tristate "ICP Single Board Computer Watchdog Timer"
347 depends on X86 347 depends on X86
348 help 348 help
349 This is a driver for the hardware watchdog on the ICP Wafer 5823 349 This is a driver for the hardware watchdog on the ICP Single
350 Single Board Computer (and probably other similar models). 350 Board Computer. This driver is working on (at least) the following
351 IPC SBC's: Wafer 5823, Rocky 4783, Rocky 3703 and Rocky 3782.
351 352
352 To compile this driver as a module, choose M here: the 353 To compile this driver as a module, choose M here: the
353 module will be called wafer5823wdt. 354 module will be called wafer5823wdt.
@@ -609,6 +610,12 @@ config WDT_RM9K_GPI
609 To compile this driver as a module, choose M here: the 610 To compile this driver as a module, choose M here: the
610 module will be called rm9k_wdt. 611 module will be called rm9k_wdt.
611 612
613config AR7_WDT
614 tristate "TI AR7 Watchdog Timer"
615 depends on AR7
616 help
617 Hardware driver for the TI AR7 Watchdog Timer.
618
612# PARISC Architecture 619# PARISC Architecture
613 620
614# POWERPC Architecture 621# POWERPC Architecture
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 389f8b14ccc4..7d9e5734f8bb 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -90,6 +90,7 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o
90obj-$(CONFIG_INDYDOG) += indydog.o 90obj-$(CONFIG_INDYDOG) += indydog.o
91obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o 91obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o
92obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o 92obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o
93obj-$(CONFIG_AR7_WDT) += ar7_wdt.o
93 94
94# PARISC Architecture 95# PARISC Architecture
95 96
diff --git a/drivers/watchdog/ar7_wdt.c b/drivers/watchdog/ar7_wdt.c
new file mode 100644
index 000000000000..cdaab8c3d3d0
--- /dev/null
+++ b/drivers/watchdog/ar7_wdt.c
@@ -0,0 +1,349 @@
1/*
2 * drivers/watchdog/ar7_wdt.c
3 *
4 * Copyright (C) 2007 Nicolas Thill <nico@openwrt.org>
5 * Copyright (c) 2005 Enrik Berkhan <Enrik.Berkhan@akk.org>
6 *
7 * Some code taken from:
8 * National Semiconductor SCx200 Watchdog support
9 * Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 */
25
26#include <linux/module.h>
27#include <linux/moduleparam.h>
28#include <linux/errno.h>
29#include <linux/init.h>
30#include <linux/miscdevice.h>
31#include <linux/watchdog.h>
32#include <linux/notifier.h>
33#include <linux/reboot.h>
34#include <linux/fs.h>
35#include <linux/ioport.h>
36#include <linux/io.h>
37#include <linux/uaccess.h>
38
39#include <asm/addrspace.h>
40#include <asm/ar7/ar7.h>
41
42#define DRVNAME "ar7_wdt"
43#define LONGNAME "TI AR7 Watchdog Timer"
44
45MODULE_AUTHOR("Nicolas Thill <nico@openwrt.org>");
46MODULE_DESCRIPTION(LONGNAME);
47MODULE_LICENSE("GPL");
48MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
49
50static int margin = 60;
51module_param(margin, int, 0);
52MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
53
54static int nowayout = WATCHDOG_NOWAYOUT;
55module_param(nowayout, int, 0);
56MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
57
58#define READ_REG(x) readl((void __iomem *)&(x))
59#define WRITE_REG(x, v) writel((v), (void __iomem *)&(x))
60
61struct ar7_wdt {
62 u32 kick_lock;
63 u32 kick;
64 u32 change_lock;
65 u32 change;
66 u32 disable_lock;
67 u32 disable;
68 u32 prescale_lock;
69 u32 prescale;
70};
71
72static struct semaphore open_semaphore;
73static unsigned expect_close;
74
75/* XXX currently fixed, allows max margin ~68.72 secs */
76#define prescale_value 0xffff
77
78/* Offset of the WDT registers */
79static unsigned long ar7_regs_wdt;
80/* Pointer to the remapped WDT IO space */
81static struct ar7_wdt *ar7_wdt;
82static void ar7_wdt_get_regs(void)
83{
84 u16 chip_id = ar7_chip_id();
85 switch (chip_id) {
86 case AR7_CHIP_7100:
87 case AR7_CHIP_7200:
88 ar7_regs_wdt = AR7_REGS_WDT;
89 break;
90 default:
91 ar7_regs_wdt = UR8_REGS_WDT;
92 break;
93 }
94}
95
96
97static void ar7_wdt_kick(u32 value)
98{
99 WRITE_REG(ar7_wdt->kick_lock, 0x5555);
100 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 1) {
101 WRITE_REG(ar7_wdt->kick_lock, 0xaaaa);
102 if ((READ_REG(ar7_wdt->kick_lock) & 3) == 3) {
103 WRITE_REG(ar7_wdt->kick, value);
104 return;
105 }
106 }
107 printk(KERN_ERR DRVNAME ": failed to unlock WDT kick reg\n");
108}
109
110static void ar7_wdt_prescale(u32 value)
111{
112 WRITE_REG(ar7_wdt->prescale_lock, 0x5a5a);
113 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 1) {
114 WRITE_REG(ar7_wdt->prescale_lock, 0xa5a5);
115 if ((READ_REG(ar7_wdt->prescale_lock) & 3) == 3) {
116 WRITE_REG(ar7_wdt->prescale, value);
117 return;
118 }
119 }
120 printk(KERN_ERR DRVNAME ": failed to unlock WDT prescale reg\n");
121}
122
123static void ar7_wdt_change(u32 value)
124{
125 WRITE_REG(ar7_wdt->change_lock, 0x6666);
126 if ((READ_REG(ar7_wdt->change_lock) & 3) == 1) {
127 WRITE_REG(ar7_wdt->change_lock, 0xbbbb);
128 if ((READ_REG(ar7_wdt->change_lock) & 3) == 3) {
129 WRITE_REG(ar7_wdt->change, value);
130 return;
131 }
132 }
133 printk(KERN_ERR DRVNAME ": failed to unlock WDT change reg\n");
134}
135
136static void ar7_wdt_disable(u32 value)
137{
138 WRITE_REG(ar7_wdt->disable_lock, 0x7777);
139 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 1) {
140 WRITE_REG(ar7_wdt->disable_lock, 0xcccc);
141 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 2) {
142 WRITE_REG(ar7_wdt->disable_lock, 0xdddd);
143 if ((READ_REG(ar7_wdt->disable_lock) & 3) == 3) {
144 WRITE_REG(ar7_wdt->disable, value);
145 return;
146 }
147 }
148 }
149 printk(KERN_ERR DRVNAME ": failed to unlock WDT disable reg\n");
150}
151
152static void ar7_wdt_update_margin(int new_margin)
153{
154 u32 change;
155
156 change = new_margin * (ar7_vbus_freq() / prescale_value);
157 if (change < 1) change = 1;
158 if (change > 0xffff) change = 0xffff;
159 ar7_wdt_change(change);
160 margin = change * prescale_value / ar7_vbus_freq();
161 printk(KERN_INFO DRVNAME
162 ": timer margin %d seconds (prescale %d, change %d, freq %d)\n",
163 margin, prescale_value, change, ar7_vbus_freq());
164}
165
166static void ar7_wdt_enable_wdt(void)
167{
168 printk(KERN_DEBUG DRVNAME ": enabling watchdog timer\n");
169 ar7_wdt_disable(1);
170 ar7_wdt_kick(1);
171}
172
173static void ar7_wdt_disable_wdt(void)
174{
175 printk(KERN_DEBUG DRVNAME ": disabling watchdog timer\n");
176 ar7_wdt_disable(0);
177}
178
179static int ar7_wdt_open(struct inode *inode, struct file *file)
180{
181 /* only allow one at a time */
182 if (down_trylock(&open_semaphore))
183 return -EBUSY;
184 ar7_wdt_enable_wdt();
185 expect_close = 0;
186
187 return nonseekable_open(inode, file);
188}
189
190static int ar7_wdt_release(struct inode *inode, struct file *file)
191{
192 if (!expect_close)
193 printk(KERN_WARNING DRVNAME
194 ": watchdog device closed unexpectedly,"
195 "will not disable the watchdog timer\n");
196 else if (!nowayout)
197 ar7_wdt_disable_wdt();
198
199 up(&open_semaphore);
200
201 return 0;
202}
203
204static int ar7_wdt_notify_sys(struct notifier_block *this,
205 unsigned long code, void *unused)
206{
207 if (code == SYS_HALT || code == SYS_POWER_OFF)
208 if (!nowayout)
209 ar7_wdt_disable_wdt();
210
211 return NOTIFY_DONE;
212}
213
214static struct notifier_block ar7_wdt_notifier = {
215 .notifier_call = ar7_wdt_notify_sys
216};
217
218static ssize_t ar7_wdt_write(struct file *file, const char *data,
219 size_t len, loff_t *ppos)
220{
221 /* check for a magic close character */
222 if (len) {
223 size_t i;
224
225 ar7_wdt_kick(1);
226
227 expect_close = 0;
228 for (i = 0; i < len; ++i) {
229 char c;
230 if (get_user(c, data+i))
231 return -EFAULT;
232 if (c == 'V')
233 expect_close = 1;
234 }
235
236 }
237 return len;
238}
239
240static int ar7_wdt_ioctl(struct inode *inode, struct file *file,
241 unsigned int cmd, unsigned long arg)
242{
243 static struct watchdog_info ident = {
244 .identity = LONGNAME,
245 .firmware_version = 1,
246 .options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
247 };
248 int new_margin;
249
250 switch (cmd) {
251 default:
252 return -ENOTTY;
253 case WDIOC_GETSUPPORT:
254 if (copy_to_user((struct watchdog_info *)arg, &ident,
255 sizeof(ident)))
256 return -EFAULT;
257 return 0;
258 case WDIOC_GETSTATUS:
259 case WDIOC_GETBOOTSTATUS:
260 if (put_user(0, (int *)arg))
261 return -EFAULT;
262 return 0;
263 case WDIOC_KEEPALIVE:
264 ar7_wdt_kick(1);
265 return 0;
266 case WDIOC_SETTIMEOUT:
267 if (get_user(new_margin, (int *)arg))
268 return -EFAULT;
269 if (new_margin < 1)
270 return -EINVAL;
271
272 ar7_wdt_update_margin(new_margin);
273 ar7_wdt_kick(1);
274
275 case WDIOC_GETTIMEOUT:
276 if (put_user(margin, (int *)arg))
277 return -EFAULT;
278 return 0;
279 }
280}
281
282static struct file_operations ar7_wdt_fops = {
283 .owner = THIS_MODULE,
284 .write = ar7_wdt_write,
285 .ioctl = ar7_wdt_ioctl,
286 .open = ar7_wdt_open,
287 .release = ar7_wdt_release,
288};
289
290static struct miscdevice ar7_wdt_miscdev = {
291 .minor = WATCHDOG_MINOR,
292 .name = "watchdog",
293 .fops = &ar7_wdt_fops,
294};
295
296static int __init ar7_wdt_init(void)
297{
298 int rc;
299
300 ar7_wdt_get_regs();
301
302 if (!request_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt),
303 LONGNAME)) {
304 printk(KERN_WARNING DRVNAME ": watchdog I/O region busy\n");
305 return -EBUSY;
306 }
307
308 ar7_wdt = (struct ar7_wdt *)
309 ioremap(ar7_regs_wdt, sizeof(struct ar7_wdt));
310
311 ar7_wdt_disable_wdt();
312 ar7_wdt_prescale(prescale_value);
313 ar7_wdt_update_margin(margin);
314
315 sema_init(&open_semaphore, 1);
316
317 rc = register_reboot_notifier(&ar7_wdt_notifier);
318 if (rc) {
319 printk(KERN_ERR DRVNAME
320 ": unable to register reboot notifier\n");
321 goto out_alloc;
322 }
323
324 rc = misc_register(&ar7_wdt_miscdev);
325 if (rc) {
326 printk(KERN_ERR DRVNAME ": unable to register misc device\n");
327 goto out_register;
328 }
329 goto out;
330
331out_register:
332 unregister_reboot_notifier(&ar7_wdt_notifier);
333out_alloc:
334 iounmap(ar7_wdt);
335 release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
336out:
337 return rc;
338}
339
340static void __exit ar7_wdt_cleanup(void)
341{
342 misc_deregister(&ar7_wdt_miscdev);
343 unregister_reboot_notifier(&ar7_wdt_notifier);
344 iounmap(ar7_wdt);
345 release_mem_region(ar7_regs_wdt, sizeof(struct ar7_wdt));
346}
347
348module_init(ar7_wdt_init);
349module_exit(ar7_wdt_cleanup);