diff options
Diffstat (limited to 'drivers/watchdog/cpwd.c')
-rw-r--r-- | drivers/watchdog/cpwd.c | 101 |
1 files changed, 51 insertions, 50 deletions
diff --git a/drivers/watchdog/cpwd.c b/drivers/watchdog/cpwd.c index 084dfe9cecfb..41070e4771a0 100644 --- a/drivers/watchdog/cpwd.c +++ b/drivers/watchdog/cpwd.c | |||
@@ -1,13 +1,13 @@ | |||
1 | /* cpwd.c - driver implementation for hardware watchdog | 1 | /* cpwd.c - driver implementation for hardware watchdog |
2 | * timers found on Sun Microsystems CP1400 and CP1500 boards. | 2 | * timers found on Sun Microsystems CP1400 and CP1500 boards. |
3 | * | 3 | * |
4 | * This device supports both the generic Linux watchdog | 4 | * This device supports both the generic Linux watchdog |
5 | * interface and Solaris-compatible ioctls as best it is | 5 | * interface and Solaris-compatible ioctls as best it is |
6 | * able. | 6 | * able. |
7 | * | 7 | * |
8 | * NOTE: CP1400 systems appear to have a defective intr_mask | 8 | * NOTE: CP1400 systems appear to have a defective intr_mask |
9 | * register on the PLD, preventing the disabling of | 9 | * register on the PLD, preventing the disabling of |
10 | * timer interrupts. We use a timer to periodically | 10 | * timer interrupts. We use a timer to periodically |
11 | * reset 'stopped' watchdogs on affected platforms. | 11 | * reset 'stopped' watchdogs on affected platforms. |
12 | * | 12 | * |
13 | * Copyright (c) 2000 Eric Brower (ebrower@usa.net) | 13 | * Copyright (c) 2000 Eric Brower (ebrower@usa.net) |
@@ -28,10 +28,9 @@ | |||
28 | #include <linux/io.h> | 28 | #include <linux/io.h> |
29 | #include <linux/of.h> | 29 | #include <linux/of.h> |
30 | #include <linux/of_device.h> | 30 | #include <linux/of_device.h> |
31 | #include <linux/uaccess.h> | ||
31 | 32 | ||
32 | #include <asm/irq.h> | 33 | #include <asm/irq.h> |
33 | #include <asm/uaccess.h> | ||
34 | |||
35 | #include <asm/watchdog.h> | 34 | #include <asm/watchdog.h> |
36 | 35 | ||
37 | #define DRIVER_NAME "cpwd" | 36 | #define DRIVER_NAME "cpwd" |
@@ -43,8 +42,8 @@ | |||
43 | #define WD_BLIMIT 0xFFFF | 42 | #define WD_BLIMIT 0xFFFF |
44 | 43 | ||
45 | #define WD0_MINOR 212 | 44 | #define WD0_MINOR 212 |
46 | #define WD1_MINOR 213 | 45 | #define WD1_MINOR 213 |
47 | #define WD2_MINOR 214 | 46 | #define WD2_MINOR 214 |
48 | 47 | ||
49 | /* Internal driver definitions. */ | 48 | /* Internal driver definitions. */ |
50 | #define WD0_ID 0 | 49 | #define WD0_ID 0 |
@@ -91,16 +90,16 @@ struct cpwd { | |||
91 | 90 | ||
92 | static struct cpwd *cpwd_device; | 91 | static struct cpwd *cpwd_device; |
93 | 92 | ||
94 | /* Sun uses Altera PLD EPF8820ATC144-4 | 93 | /* Sun uses Altera PLD EPF8820ATC144-4 |
95 | * providing three hardware watchdogs: | 94 | * providing three hardware watchdogs: |
96 | * | 95 | * |
97 | * 1) RIC - sends an interrupt when triggered | 96 | * 1) RIC - sends an interrupt when triggered |
98 | * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU | 97 | * 2) XIR - asserts XIR_B_RESET when triggered, resets CPU |
99 | * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board | 98 | * 3) POR - asserts POR_B_RESET when triggered, resets CPU, backplane, board |
100 | * | 99 | * |
101 | *** Timer register block definition (struct wd_timer_regblk) | 100 | *** Timer register block definition (struct wd_timer_regblk) |
102 | * | 101 | * |
103 | * dcntr and limit registers (halfword access): | 102 | * dcntr and limit registers (halfword access): |
104 | * ------------------- | 103 | * ------------------- |
105 | * | 15 | ...| 1 | 0 | | 104 | * | 15 | ...| 1 | 0 | |
106 | * ------------------- | 105 | * ------------------- |
@@ -108,7 +107,8 @@ static struct cpwd *cpwd_device; | |||
108 | * ------------------- | 107 | * ------------------- |
109 | * dcntr - Current 16-bit downcounter value. | 108 | * dcntr - Current 16-bit downcounter value. |
110 | * When downcounter reaches '0' watchdog expires. | 109 | * When downcounter reaches '0' watchdog expires. |
111 | * Reading this register resets downcounter with 'limit' value. | 110 | * Reading this register resets downcounter with |
111 | * 'limit' value. | ||
112 | * limit - 16-bit countdown value in 1/10th second increments. | 112 | * limit - 16-bit countdown value in 1/10th second increments. |
113 | * Writing this register begins countdown with input value. | 113 | * Writing this register begins countdown with input value. |
114 | * Reading from this register does not affect counter. | 114 | * Reading from this register does not affect counter. |
@@ -158,11 +158,11 @@ static int wd0_timeout = 0; | |||
158 | static int wd1_timeout = 0; | 158 | static int wd1_timeout = 0; |
159 | static int wd2_timeout = 0; | 159 | static int wd2_timeout = 0; |
160 | 160 | ||
161 | module_param (wd0_timeout, int, 0); | 161 | module_param(wd0_timeout, int, 0); |
162 | MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); | 162 | MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs"); |
163 | module_param (wd1_timeout, int, 0); | 163 | module_param(wd1_timeout, int, 0); |
164 | MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); | 164 | MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs"); |
165 | module_param (wd2_timeout, int, 0); | 165 | module_param(wd2_timeout, int, 0); |
166 | MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); | 166 | MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs"); |
167 | 167 | ||
168 | MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); | 168 | MODULE_AUTHOR("Eric Brower <ebrower@usa.net>"); |
@@ -201,9 +201,9 @@ static u8 cpwd_readb(void __iomem *addr) | |||
201 | static void cpwd_toggleintr(struct cpwd *p, int index, int enable) | 201 | static void cpwd_toggleintr(struct cpwd *p, int index, int enable) |
202 | { | 202 | { |
203 | unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK); | 203 | unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK); |
204 | unsigned char setregs = | 204 | unsigned char setregs = |
205 | (index == -1) ? | 205 | (index == -1) ? |
206 | (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : | 206 | (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) : |
207 | (p->devs[index].intr_mask); | 207 | (p->devs[index].intr_mask); |
208 | 208 | ||
209 | if (enable == WD_INTR_ON) | 209 | if (enable == WD_INTR_ON) |
@@ -303,24 +303,24 @@ static int cpwd_getstatus(struct cpwd *p, int index) | |||
303 | unsigned char ret = WD_STOPPED; | 303 | unsigned char ret = WD_STOPPED; |
304 | 304 | ||
305 | /* determine STOPPED */ | 305 | /* determine STOPPED */ |
306 | if (!stat) | 306 | if (!stat) |
307 | return ret; | 307 | return ret; |
308 | 308 | ||
309 | /* determine EXPIRED vs FREERUN vs RUNNING */ | 309 | /* determine EXPIRED vs FREERUN vs RUNNING */ |
310 | else if (WD_S_EXPIRED & stat) { | 310 | else if (WD_S_EXPIRED & stat) { |
311 | ret = WD_EXPIRED; | 311 | ret = WD_EXPIRED; |
312 | } else if(WD_S_RUNNING & stat) { | 312 | } else if (WD_S_RUNNING & stat) { |
313 | if (intr & p->devs[index].intr_mask) { | 313 | if (intr & p->devs[index].intr_mask) { |
314 | ret = WD_FREERUN; | 314 | ret = WD_FREERUN; |
315 | } else { | 315 | } else { |
316 | /* Fudge WD_EXPIRED status for defective CP1400-- | 316 | /* Fudge WD_EXPIRED status for defective CP1400-- |
317 | * IF timer is running | 317 | * IF timer is running |
318 | * AND brokenstop is set | 318 | * AND brokenstop is set |
319 | * AND an interrupt has been serviced | 319 | * AND an interrupt has been serviced |
320 | * we are WD_EXPIRED. | 320 | * we are WD_EXPIRED. |
321 | * | 321 | * |
322 | * IF timer is running | 322 | * IF timer is running |
323 | * AND brokenstop is set | 323 | * AND brokenstop is set |
324 | * AND no interrupt has been serviced | 324 | * AND no interrupt has been serviced |
325 | * we are WD_FREERUN. | 325 | * we are WD_FREERUN. |
326 | */ | 326 | */ |
@@ -329,7 +329,8 @@ static int cpwd_getstatus(struct cpwd *p, int index) | |||
329 | if (p->devs[index].runstatus & WD_STAT_SVCD) { | 329 | if (p->devs[index].runstatus & WD_STAT_SVCD) { |
330 | ret = WD_EXPIRED; | 330 | ret = WD_EXPIRED; |
331 | } else { | 331 | } else { |
332 | /* we could as well pretend we are expired */ | 332 | /* we could as well pretend |
333 | * we are expired */ | ||
333 | ret = WD_FREERUN; | 334 | ret = WD_FREERUN; |
334 | } | 335 | } |
335 | } else { | 336 | } else { |
@@ -342,7 +343,7 @@ static int cpwd_getstatus(struct cpwd *p, int index) | |||
342 | if (p->devs[index].runstatus & WD_STAT_SVCD) | 343 | if (p->devs[index].runstatus & WD_STAT_SVCD) |
343 | ret |= WD_SERVICED; | 344 | ret |= WD_SERVICED; |
344 | 345 | ||
345 | return(ret); | 346 | return ret; |
346 | } | 347 | } |
347 | 348 | ||
348 | static irqreturn_t cpwd_interrupt(int irq, void *dev_id) | 349 | static irqreturn_t cpwd_interrupt(int irq, void *dev_id) |
@@ -367,22 +368,22 @@ static int cpwd_open(struct inode *inode, struct file *f) | |||
367 | struct cpwd *p = cpwd_device; | 368 | struct cpwd *p = cpwd_device; |
368 | 369 | ||
369 | lock_kernel(); | 370 | lock_kernel(); |
370 | switch(iminor(inode)) { | 371 | switch (iminor(inode)) { |
371 | case WD0_MINOR: | 372 | case WD0_MINOR: |
372 | case WD1_MINOR: | 373 | case WD1_MINOR: |
373 | case WD2_MINOR: | 374 | case WD2_MINOR: |
374 | break; | 375 | break; |
375 | 376 | ||
376 | default: | 377 | default: |
377 | unlock_kernel(); | 378 | unlock_kernel(); |
378 | return -ENODEV; | 379 | return -ENODEV; |
379 | } | 380 | } |
380 | 381 | ||
381 | /* Register IRQ on first open of device */ | 382 | /* Register IRQ on first open of device */ |
382 | if (!p->initialized) { | 383 | if (!p->initialized) { |
383 | if (request_irq(p->irq, &cpwd_interrupt, | 384 | if (request_irq(p->irq, &cpwd_interrupt, |
384 | IRQF_SHARED, DRIVER_NAME, p)) { | 385 | IRQF_SHARED, DRIVER_NAME, p)) { |
385 | printk(KERN_ERR PFX "Cannot register IRQ %d\n", | 386 | printk(KERN_ERR PFX "Cannot register IRQ %d\n", |
386 | p->irq); | 387 | p->irq); |
387 | unlock_kernel(); | 388 | unlock_kernel(); |
388 | return -EBUSY; | 389 | return -EBUSY; |
@@ -400,8 +401,7 @@ static int cpwd_release(struct inode *inode, struct file *file) | |||
400 | return 0; | 401 | return 0; |
401 | } | 402 | } |
402 | 403 | ||
403 | static int cpwd_ioctl(struct inode *inode, struct file *file, | 404 | static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
404 | unsigned int cmd, unsigned long arg) | ||
405 | { | 405 | { |
406 | static struct watchdog_info info = { | 406 | static struct watchdog_info info = { |
407 | .options = WDIOF_SETTIMEOUT, | 407 | .options = WDIOF_SETTIMEOUT, |
@@ -409,6 +409,7 @@ static int cpwd_ioctl(struct inode *inode, struct file *file, | |||
409 | .identity = DRIVER_NAME, | 409 | .identity = DRIVER_NAME, |
410 | }; | 410 | }; |
411 | void __user *argp = (void __user *)arg; | 411 | void __user *argp = (void __user *)arg; |
412 | struct inode *inode = file->f_path.dentry->d_inode; | ||
412 | int index = iminor(inode) - WD0_MINOR; | 413 | int index = iminor(inode) - WD0_MINOR; |
413 | struct cpwd *p = cpwd_device; | 414 | struct cpwd *p = cpwd_device; |
414 | int setopt = 0; | 415 | int setopt = 0; |
@@ -442,7 +443,7 @@ static int cpwd_ioctl(struct inode *inode, struct file *file, | |||
442 | cpwd_starttimer(p, index); | 443 | cpwd_starttimer(p, index); |
443 | } else { | 444 | } else { |
444 | return -EINVAL; | 445 | return -EINVAL; |
445 | } | 446 | } |
446 | break; | 447 | break; |
447 | 448 | ||
448 | /* Solaris-compatible IOCTLs */ | 449 | /* Solaris-compatible IOCTLs */ |
@@ -458,7 +459,7 @@ static int cpwd_ioctl(struct inode *inode, struct file *file, | |||
458 | 459 | ||
459 | case WIOCSTOP: | 460 | case WIOCSTOP: |
460 | if (p->enabled) | 461 | if (p->enabled) |
461 | return(-EINVAL); | 462 | return -EINVAL; |
462 | 463 | ||
463 | cpwd_stoptimer(p, index); | 464 | cpwd_stoptimer(p, index); |
464 | break; | 465 | break; |
@@ -481,7 +482,7 @@ static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, | |||
481 | case WIOCSTOP: | 482 | case WIOCSTOP: |
482 | case WIOCGSTAT: | 483 | case WIOCGSTAT: |
483 | lock_kernel(); | 484 | lock_kernel(); |
484 | rval = cpwd_ioctl(file->f_path.dentry->d_inode, file, cmd, arg); | 485 | rval = cpwd_ioctl(file, cmd, arg); |
485 | unlock_kernel(); | 486 | unlock_kernel(); |
486 | break; | 487 | break; |
487 | 488 | ||
@@ -493,7 +494,7 @@ static long cpwd_compat_ioctl(struct file *file, unsigned int cmd, | |||
493 | return rval; | 494 | return rval; |
494 | } | 495 | } |
495 | 496 | ||
496 | static ssize_t cpwd_write(struct file *file, const char __user *buf, | 497 | static ssize_t cpwd_write(struct file *file, const char __user *buf, |
497 | size_t count, loff_t *ppos) | 498 | size_t count, loff_t *ppos) |
498 | { | 499 | { |
499 | struct inode *inode = file->f_path.dentry->d_inode; | 500 | struct inode *inode = file->f_path.dentry->d_inode; |
@@ -508,20 +509,20 @@ static ssize_t cpwd_write(struct file *file, const char __user *buf, | |||
508 | return 0; | 509 | return 0; |
509 | } | 510 | } |
510 | 511 | ||
511 | static ssize_t cpwd_read(struct file * file, char __user *buffer, | 512 | static ssize_t cpwd_read(struct file *file, char __user *buffer, |
512 | size_t count, loff_t *ppos) | 513 | size_t count, loff_t *ppos) |
513 | { | 514 | { |
514 | return -EINVAL; | 515 | return -EINVAL; |
515 | } | 516 | } |
516 | 517 | ||
517 | static const struct file_operations cpwd_fops = { | 518 | static const struct file_operations cpwd_fops = { |
518 | .owner = THIS_MODULE, | 519 | .owner = THIS_MODULE, |
519 | .ioctl = cpwd_ioctl, | 520 | .unlocked_ioctl = cpwd_ioctl, |
520 | .compat_ioctl = cpwd_compat_ioctl, | 521 | .compat_ioctl = cpwd_compat_ioctl, |
521 | .open = cpwd_open, | 522 | .open = cpwd_open, |
522 | .write = cpwd_write, | 523 | .write = cpwd_write, |
523 | .read = cpwd_read, | 524 | .read = cpwd_read, |
524 | .release = cpwd_release, | 525 | .release = cpwd_release, |
525 | }; | 526 | }; |
526 | 527 | ||
527 | static int __devinit cpwd_probe(struct of_device *op, | 528 | static int __devinit cpwd_probe(struct of_device *op, |