diff options
author | Woody Suwalski <woodys@xandros.com> | 2006-01-08 04:00:31 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-01-08 23:12:39 -0500 |
commit | 4dab06fa7b6d07ee95c8bba5ddd0840633069159 (patch) | |
tree | 18c54a1af00ec4effc062d67f45dfc3d8bc096ee /drivers/char | |
parent | 70c00ba0bbfb583e39b964ebd88620c18aa02c62 (diff) |
[PATCH] ARM Netwinder watchdog wdt977 update
Cleanup for the ARM-only watchdog driver wdt977.
This is probably the last update, since we want to merge with w83977f_wdt.
Jose Goncalves has ported this driver to i386, so probably we can iron out
configuration differences.
Signed-off-by: Woody Suwalski <woodys@xandros.com>
Cc: Russell King <rmk@arm.linux.org.uk>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/watchdog/wdt977.c | 216 |
1 files changed, 141 insertions, 75 deletions
diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c index 44d49dfacbb3..3843900e94c4 100644 --- a/drivers/char/watchdog/wdt977.c +++ b/drivers/char/watchdog/wdt977.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Wdt977 0.03: A Watchdog Device for Netwinder W83977AF chip | 2 | * Wdt977 0.04: A Watchdog Device for Netwinder W83977AF chip |
3 | * | 3 | * |
4 | * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>) | 4 | * (c) Copyright 1998 Rebel.com (Woody Suwalski <woody@netwinder.org>) |
5 | * | 5 | * |
@@ -18,6 +18,8 @@ | |||
18 | * from minutes to seconds. | 18 | * from minutes to seconds. |
19 | * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in | 19 | * 07-Jul-2003 Daniele Bellucci: Audit return code of misc_register in |
20 | * nwwatchdog_init. | 20 | * nwwatchdog_init. |
21 | * 25-Oct-2005 Woody Suwalski: Convert addresses to #defs, add spinlocks | ||
22 | * remove limitiation to be used on Netwinders only | ||
21 | */ | 23 | */ |
22 | 24 | ||
23 | #include <linux/module.h> | 25 | #include <linux/module.h> |
@@ -28,6 +30,7 @@ | |||
28 | #include <linux/fs.h> | 30 | #include <linux/fs.h> |
29 | #include <linux/miscdevice.h> | 31 | #include <linux/miscdevice.h> |
30 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/ioport.h> | ||
31 | #include <linux/watchdog.h> | 34 | #include <linux/watchdog.h> |
32 | #include <linux/notifier.h> | 35 | #include <linux/notifier.h> |
33 | #include <linux/reboot.h> | 36 | #include <linux/reboot.h> |
@@ -37,8 +40,18 @@ | |||
37 | #include <asm/mach-types.h> | 40 | #include <asm/mach-types.h> |
38 | #include <asm/uaccess.h> | 41 | #include <asm/uaccess.h> |
39 | 42 | ||
40 | #define PFX "Wdt977: " | 43 | #define WATCHDOG_VERSION "0.04" |
41 | #define WATCHDOG_MINOR 130 | 44 | #define WATCHDOG_NAME "Wdt977" |
45 | #define PFX WATCHDOG_NAME ": " | ||
46 | #define DRIVER_VERSION WATCHDOG_NAME " driver, v" WATCHDOG_VERSION "\n" | ||
47 | |||
48 | #define IO_INDEX_PORT 0x370 /* on some systems it can be 0x3F0 */ | ||
49 | #define IO_DATA_PORT (IO_INDEX_PORT+1) | ||
50 | |||
51 | #define UNLOCK_DATA 0x87 | ||
52 | #define LOCK_DATA 0xAA | ||
53 | #define DEVICE_REGISTER 0x07 | ||
54 | |||
42 | 55 | ||
43 | #define DEFAULT_TIMEOUT 60 /* default timeout in seconds */ | 56 | #define DEFAULT_TIMEOUT 60 /* default timeout in seconds */ |
44 | 57 | ||
@@ -47,6 +60,7 @@ static int timeoutM; /* timeout in minutes */ | |||
47 | static unsigned long timer_alive; | 60 | static unsigned long timer_alive; |
48 | static int testmode; | 61 | static int testmode; |
49 | static char expect_close; | 62 | static char expect_close; |
63 | static spinlock_t spinlock; | ||
50 | 64 | ||
51 | module_param(timeout, int, 0); | 65 | module_param(timeout, int, 0); |
52 | MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); | 66 | MODULE_PARM_DESC(timeout,"Watchdog timeout in seconds (60..15300), default=" __MODULE_STRING(DEFAULT_TIMEOUT) ")"); |
@@ -63,9 +77,13 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CON | |||
63 | 77 | ||
64 | static int wdt977_start(void) | 78 | static int wdt977_start(void) |
65 | { | 79 | { |
80 | unsigned long flags; | ||
81 | |||
82 | spin_lock_irqsave(&spinlock, flags); | ||
83 | |||
66 | /* unlock the SuperIO chip */ | 84 | /* unlock the SuperIO chip */ |
67 | outb(0x87,0x370); | 85 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
68 | outb(0x87,0x370); | 86 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
69 | 87 | ||
70 | /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 | 88 | /* select device Aux2 (device=8) and set watchdog regs F2, F3 and F4 |
71 | * F2 has the timeout in minutes | 89 | * F2 has the timeout in minutes |
@@ -73,28 +91,29 @@ static int wdt977_start(void) | |||
73 | * at timeout, and to reset timer on kbd/mouse activity (not impl.) | 91 | * at timeout, and to reset timer on kbd/mouse activity (not impl.) |
74 | * F4 is used to just clear the TIMEOUT'ed state (bit 0) | 92 | * F4 is used to just clear the TIMEOUT'ed state (bit 0) |
75 | */ | 93 | */ |
76 | outb(0x07,0x370); | 94 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
77 | outb(0x08,0x371); | 95 | outb_p(0x08, IO_DATA_PORT); |
78 | outb(0xF2,0x370); | 96 | outb_p(0xF2, IO_INDEX_PORT); |
79 | outb(timeoutM,0x371); | 97 | outb_p(timeoutM, IO_DATA_PORT); |
80 | outb(0xF3,0x370); | 98 | outb_p(0xF3, IO_INDEX_PORT); |
81 | outb(0x00,0x371); /* another setting is 0E for kbd/mouse/LED */ | 99 | outb_p(0x00, IO_DATA_PORT); /* another setting is 0E for kbd/mouse/LED */ |
82 | outb(0xF4,0x370); | 100 | outb_p(0xF4, IO_INDEX_PORT); |
83 | outb(0x00,0x371); | 101 | outb_p(0x00, IO_DATA_PORT); |
84 | 102 | ||
85 | /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ | 103 | /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ |
86 | /* in test mode watch the bit 1 on F4 to indicate "triggered" */ | 104 | /* in test mode watch the bit 1 on F4 to indicate "triggered" */ |
87 | if (!testmode) | 105 | if (!testmode) |
88 | { | 106 | { |
89 | outb(0x07,0x370); | 107 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
90 | outb(0x07,0x371); | 108 | outb_p(0x07, IO_DATA_PORT); |
91 | outb(0xE6,0x370); | 109 | outb_p(0xE6, IO_INDEX_PORT); |
92 | outb(0x08,0x371); | 110 | outb_p(0x08, IO_DATA_PORT); |
93 | } | 111 | } |
94 | 112 | ||
95 | /* lock the SuperIO chip */ | 113 | /* lock the SuperIO chip */ |
96 | outb(0xAA,0x370); | 114 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
97 | 115 | ||
116 | spin_unlock_irqrestore(&spinlock, flags); | ||
98 | printk(KERN_INFO PFX "activated.\n"); | 117 | printk(KERN_INFO PFX "activated.\n"); |
99 | 118 | ||
100 | return 0; | 119 | return 0; |
@@ -106,35 +125,39 @@ static int wdt977_start(void) | |||
106 | 125 | ||
107 | static int wdt977_stop(void) | 126 | static int wdt977_stop(void) |
108 | { | 127 | { |
128 | unsigned long flags; | ||
129 | spin_lock_irqsave(&spinlock, flags); | ||
130 | |||
109 | /* unlock the SuperIO chip */ | 131 | /* unlock the SuperIO chip */ |
110 | outb(0x87,0x370); | 132 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
111 | outb(0x87,0x370); | 133 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
112 | 134 | ||
113 | /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 | 135 | /* select device Aux2 (device=8) and set watchdog regs F2,F3 and F4 |
114 | * F3 is reset to its default state | 136 | * F3 is reset to its default state |
115 | * F4 can clear the TIMEOUT'ed state (bit 0) - back to default | 137 | * F4 can clear the TIMEOUT'ed state (bit 0) - back to default |
116 | * We can not use GP17 as a PowerLed, as we use its usage as a RedLed | 138 | * We can not use GP17 as a PowerLed, as we use its usage as a RedLed |
117 | */ | 139 | */ |
118 | outb(0x07,0x370); | 140 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
119 | outb(0x08,0x371); | 141 | outb_p(0x08, IO_DATA_PORT); |
120 | outb(0xF2,0x370); | 142 | outb_p(0xF2, IO_INDEX_PORT); |
121 | outb(0xFF,0x371); | 143 | outb_p(0xFF, IO_DATA_PORT); |
122 | outb(0xF3,0x370); | 144 | outb_p(0xF3, IO_INDEX_PORT); |
123 | outb(0x00,0x371); | 145 | outb_p(0x00, IO_DATA_PORT); |
124 | outb(0xF4,0x370); | 146 | outb_p(0xF4, IO_INDEX_PORT); |
125 | outb(0x00,0x371); | 147 | outb_p(0x00, IO_DATA_PORT); |
126 | outb(0xF2,0x370); | 148 | outb_p(0xF2, IO_INDEX_PORT); |
127 | outb(0x00,0x371); | 149 | outb_p(0x00, IO_DATA_PORT); |
128 | 150 | ||
129 | /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ | 151 | /* at last select device Aux1 (dev=7) and set GP16 as a watchdog output */ |
130 | outb(0x07,0x370); | 152 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
131 | outb(0x07,0x371); | 153 | outb_p(0x07, IO_DATA_PORT); |
132 | outb(0xE6,0x370); | 154 | outb_p(0xE6, IO_INDEX_PORT); |
133 | outb(0x08,0x371); | 155 | outb_p(0x08, IO_DATA_PORT); |
134 | 156 | ||
135 | /* lock the SuperIO chip */ | 157 | /* lock the SuperIO chip */ |
136 | outb(0xAA,0x370); | 158 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
137 | 159 | ||
160 | spin_unlock_irqrestore(&spinlock, flags); | ||
138 | printk(KERN_INFO PFX "shutdown.\n"); | 161 | printk(KERN_INFO PFX "shutdown.\n"); |
139 | 162 | ||
140 | return 0; | 163 | return 0; |
@@ -147,19 +170,23 @@ static int wdt977_stop(void) | |||
147 | 170 | ||
148 | static int wdt977_keepalive(void) | 171 | static int wdt977_keepalive(void) |
149 | { | 172 | { |
173 | unsigned long flags; | ||
174 | spin_lock_irqsave(&spinlock, flags); | ||
175 | |||
150 | /* unlock the SuperIO chip */ | 176 | /* unlock the SuperIO chip */ |
151 | outb(0x87,0x370); | 177 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
152 | outb(0x87,0x370); | 178 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
153 | 179 | ||
154 | /* select device Aux2 (device=8) and kicks watchdog reg F2 */ | 180 | /* select device Aux2 (device=8) and kicks watchdog reg F2 */ |
155 | /* F2 has the timeout in minutes */ | 181 | /* F2 has the timeout in minutes */ |
156 | outb(0x07,0x370); | 182 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
157 | outb(0x08,0x371); | 183 | outb_p(0x08, IO_DATA_PORT); |
158 | outb(0xF2,0x370); | 184 | outb_p(0xF2, IO_INDEX_PORT); |
159 | outb(timeoutM,0x371); | 185 | outb_p(timeoutM, IO_DATA_PORT); |
160 | 186 | ||
161 | /* lock the SuperIO chip */ | 187 | /* lock the SuperIO chip */ |
162 | outb(0xAA,0x370); | 188 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
189 | spin_unlock_irqrestore(&spinlock, flags); | ||
163 | 190 | ||
164 | return 0; | 191 | return 0; |
165 | } | 192 | } |
@@ -198,22 +225,26 @@ static int wdt977_set_timeout(int t) | |||
198 | static int wdt977_get_status(int *status) | 225 | static int wdt977_get_status(int *status) |
199 | { | 226 | { |
200 | int new_status; | 227 | int new_status; |
228 | unsigned long flags; | ||
201 | 229 | ||
202 | *status=0; | 230 | spin_lock_irqsave(&spinlock, flags); |
203 | 231 | ||
204 | /* unlock the SuperIO chip */ | 232 | /* unlock the SuperIO chip */ |
205 | outb(0x87,0x370); | 233 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
206 | outb(0x87,0x370); | 234 | outb_p(UNLOCK_DATA, IO_INDEX_PORT); |
207 | 235 | ||
208 | /* select device Aux2 (device=8) and read watchdog reg F4 */ | 236 | /* select device Aux2 (device=8) and read watchdog reg F4 */ |
209 | outb(0x07,0x370); | 237 | outb_p(DEVICE_REGISTER, IO_INDEX_PORT); |
210 | outb(0x08,0x371); | 238 | outb_p(0x08, IO_DATA_PORT); |
211 | outb(0xF4,0x370); | 239 | outb_p(0xF4, IO_INDEX_PORT); |
212 | new_status = inb(0x371); | 240 | new_status = inb_p(IO_DATA_PORT); |
213 | 241 | ||
214 | /* lock the SuperIO chip */ | 242 | /* lock the SuperIO chip */ |
215 | outb(0xAA,0x370); | 243 | outb_p(LOCK_DATA, IO_INDEX_PORT); |
216 | 244 | ||
245 | spin_unlock_irqrestore(&spinlock, flags); | ||
246 | |||
247 | *status=0; | ||
217 | if (new_status & 1) | 248 | if (new_status & 1) |
218 | *status |= WDIOF_CARDRESET; | 249 | *status |= WDIOF_CARDRESET; |
219 | 250 | ||
@@ -249,8 +280,8 @@ static int wdt977_release(struct inode *inode, struct file *file) | |||
249 | wdt977_stop(); | 280 | wdt977_stop(); |
250 | clear_bit(0,&timer_alive); | 281 | clear_bit(0,&timer_alive); |
251 | } else { | 282 | } else { |
252 | printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); | ||
253 | wdt977_keepalive(); | 283 | wdt977_keepalive(); |
284 | printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n"); | ||
254 | } | 285 | } |
255 | expect_close = 0; | 286 | expect_close = 0; |
256 | return 0; | 287 | return 0; |
@@ -271,14 +302,17 @@ static int wdt977_release(struct inode *inode, struct file *file) | |||
271 | static ssize_t wdt977_write(struct file *file, const char __user *buf, | 302 | static ssize_t wdt977_write(struct file *file, const char __user *buf, |
272 | size_t count, loff_t *ppos) | 303 | size_t count, loff_t *ppos) |
273 | { | 304 | { |
274 | if (count) { | 305 | if (count) |
275 | if (!nowayout) { | 306 | { |
307 | if (!nowayout) | ||
308 | { | ||
276 | size_t i; | 309 | size_t i; |
277 | 310 | ||
278 | /* In case it was set long ago */ | 311 | /* In case it was set long ago */ |
279 | expect_close = 0; | 312 | expect_close = 0; |
280 | 313 | ||
281 | for (i = 0; i != count; i++) { | 314 | for (i = 0; i != count; i++) |
315 | { | ||
282 | char c; | 316 | char c; |
283 | if (get_user(c, buf + i)) | 317 | if (get_user(c, buf + i)) |
284 | return -EFAULT; | 318 | return -EFAULT; |
@@ -287,6 +321,7 @@ static ssize_t wdt977_write(struct file *file, const char __user *buf, | |||
287 | } | 321 | } |
288 | } | 322 | } |
289 | 323 | ||
324 | /* someone wrote to us, we should restart timer */ | ||
290 | wdt977_keepalive(); | 325 | wdt977_keepalive(); |
291 | } | 326 | } |
292 | return count; | 327 | return count; |
@@ -308,7 +343,7 @@ static struct watchdog_info ident = { | |||
308 | WDIOF_MAGICCLOSE | | 343 | WDIOF_MAGICCLOSE | |
309 | WDIOF_KEEPALIVEPING, | 344 | WDIOF_KEEPALIVEPING, |
310 | .firmware_version = 1, | 345 | .firmware_version = 1, |
311 | .identity = "Winbond 83977", | 346 | .identity = WATCHDOG_NAME, |
312 | }; | 347 | }; |
313 | 348 | ||
314 | static int wdt977_ioctl(struct inode *inode, struct file *file, | 349 | static int wdt977_ioctl(struct inode *inode, struct file *file, |
@@ -405,50 +440,81 @@ static struct notifier_block wdt977_notifier = { | |||
405 | .notifier_call = wdt977_notify_sys, | 440 | .notifier_call = wdt977_notify_sys, |
406 | }; | 441 | }; |
407 | 442 | ||
408 | static int __init nwwatchdog_init(void) | 443 | static int __init wd977_init(void) |
409 | { | 444 | { |
410 | int retval; | 445 | int rc; |
411 | if (!machine_is_netwinder()) | 446 | |
412 | return -ENODEV; | 447 | //if (!machine_is_netwinder()) |
448 | // return -ENODEV; | ||
449 | |||
450 | printk(KERN_INFO PFX DRIVER_VERSION); | ||
451 | |||
452 | spin_lock_init(&spinlock); | ||
413 | 453 | ||
414 | /* Check that the timeout value is within it's range ; if not reset to the default */ | 454 | /* Check that the timeout value is within it's range ; if not reset to the default */ |
415 | if (wdt977_set_timeout(timeout)) { | 455 | if (wdt977_set_timeout(timeout)) |
456 | { | ||
416 | wdt977_set_timeout(DEFAULT_TIMEOUT); | 457 | wdt977_set_timeout(DEFAULT_TIMEOUT); |
417 | printk(KERN_INFO PFX "timeout value must be 60<timeout<15300, using %d\n", | 458 | printk(KERN_INFO PFX "timeout value must be 60<timeout<15300, using %d\n", |
418 | DEFAULT_TIMEOUT); | 459 | DEFAULT_TIMEOUT); |
419 | } | 460 | } |
420 | 461 | ||
421 | retval = register_reboot_notifier(&wdt977_notifier); | 462 | /* on Netwinder the IOports are already reserved by |
422 | if (retval) { | 463 | * arch/arm/mach-footbridge/netwinder-hw.c |
423 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", | 464 | */ |
424 | retval); | 465 | if (!machine_is_netwinder()) |
425 | return retval; | 466 | { |
467 | if (!request_region(IO_INDEX_PORT, 2, WATCHDOG_NAME)) | ||
468 | { | ||
469 | printk(KERN_ERR PFX "I/O address 0x%04x already in use\n", | ||
470 | IO_INDEX_PORT); | ||
471 | rc = -EIO; | ||
472 | goto err_out; | ||
473 | } | ||
426 | } | 474 | } |
427 | 475 | ||
428 | retval = misc_register(&wdt977_miscdev); | 476 | rc = misc_register(&wdt977_miscdev); |
429 | if (retval) { | 477 | if (rc) |
478 | { | ||
430 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", | 479 | printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", |
431 | WATCHDOG_MINOR, retval); | 480 | wdt977_miscdev.minor, rc); |
432 | unregister_reboot_notifier(&wdt977_notifier); | 481 | goto err_out_region; |
433 | return retval; | 482 | } |
483 | |||
484 | rc = register_reboot_notifier(&wdt977_notifier); | ||
485 | if (rc) | ||
486 | { | ||
487 | printk(KERN_ERR PFX "cannot register reboot notifier (err=%d)\n", | ||
488 | rc); | ||
489 | goto err_out_miscdev; | ||
434 | } | 490 | } |
435 | 491 | ||
436 | printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d, testmode = %i)\n", | 492 | printk(KERN_INFO PFX "initialized. timeout=%d sec (nowayout=%d, testmode=%i)\n", |
437 | timeout, nowayout, testmode); | 493 | timeout, nowayout, testmode); |
438 | 494 | ||
439 | return 0; | 495 | return 0; |
496 | |||
497 | err_out_miscdev: | ||
498 | misc_deregister(&wdt977_miscdev); | ||
499 | err_out_region: | ||
500 | if (!machine_is_netwinder()) | ||
501 | release_region(IO_INDEX_PORT,2); | ||
502 | err_out: | ||
503 | return rc; | ||
440 | } | 504 | } |
441 | 505 | ||
442 | static void __exit nwwatchdog_exit(void) | 506 | static void __exit wd977_exit(void) |
443 | { | 507 | { |
508 | wdt977_stop(); | ||
444 | misc_deregister(&wdt977_miscdev); | 509 | misc_deregister(&wdt977_miscdev); |
445 | unregister_reboot_notifier(&wdt977_notifier); | 510 | unregister_reboot_notifier(&wdt977_notifier); |
511 | release_region(IO_INDEX_PORT,2); | ||
446 | } | 512 | } |
447 | 513 | ||
448 | module_init(nwwatchdog_init); | 514 | module_init(wd977_init); |
449 | module_exit(nwwatchdog_exit); | 515 | module_exit(wd977_exit); |
450 | 516 | ||
451 | MODULE_AUTHOR("Woody Suwalski <woody@netwinder.org>"); | 517 | MODULE_AUTHOR("Woody Suwalski <woodys@xandros.com>"); |
452 | MODULE_DESCRIPTION("W83977AF Watchdog driver"); | 518 | MODULE_DESCRIPTION("W83977AF Watchdog driver"); |
453 | MODULE_LICENSE("GPL"); | 519 | MODULE_LICENSE("GPL"); |
454 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); | 520 | MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); |