1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/fs.h>
20#include <linux/errno.h>
21#include <linux/major.h>
22#include <linux/init.h>
23#include <linux/miscdevice.h>
24#include <linux/interrupt.h>
25#include <linux/ioport.h>
26#include <linux/timer.h>
27#include <linux/smp_lock.h>
28#include <linux/io.h>
29#include <linux/of.h>
30#include <linux/of_device.h>
31#include <linux/uaccess.h>
32
33#include <asm/irq.h>
34#include <asm/watchdog.h>
35
36#define DRIVER_NAME "cpwd"
37#define PFX DRIVER_NAME ": "
38
39#define WD_OBPNAME "watchdog"
40#define WD_BADMODEL "SUNW,501-5336"
41#define WD_BTIMEOUT (jiffies + (HZ * 1000))
42#define WD_BLIMIT 0xFFFF
43
44#define WD0_MINOR 212
45#define WD1_MINOR 213
46#define WD2_MINOR 214
47
48
49#define WD0_ID 0
50#define WD1_ID 1
51#define WD2_ID 2
52#define WD_NUMDEVS 3
53
54#define WD_INTR_OFF 0
55#define WD_INTR_ON 1
56
57#define WD_STAT_INIT 0x01
58#define WD_STAT_BSTOP 0x02
59#define WD_STAT_SVCD 0x04
60
61
62
63#define WD0_INTR_MASK 0x01
64#define WD1_INTR_MASK 0x02
65#define WD2_INTR_MASK 0x04
66
67#define WD_S_RUNNING 0x01
68#define WD_S_EXPIRED 0x02
69
70struct cpwd {
71 void __iomem *regs;
72 spinlock_t lock;
73
74 unsigned int irq;
75
76 unsigned long timeout;
77 bool enabled;
78 bool reboot;
79 bool broken;
80 bool initialized;
81
82 struct {
83 struct miscdevice misc;
84 void __iomem *regs;
85 u8 intr_mask;
86 u8 runstatus;
87 u16 timeout;
88 } devs[WD_NUMDEVS];
89};
90
91static struct cpwd *cpwd_device;
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142#define WD_TIMER_REGSZ 16
143#define WD0_OFF 0
144#define WD1_OFF (WD_TIMER_REGSZ * 1)
145#define WD2_OFF (WD_TIMER_REGSZ * 2)
146#define PLD_OFF (WD_TIMER_REGSZ * 3)
147
148#define WD_DCNTR 0x00
149#define WD_LIMIT 0x04
150#define WD_STATUS 0x08
151
152#define PLD_IMASK (PLD_OFF + 0x00)
153#define PLD_STATUS (PLD_OFF + 0x04)
154
155static struct timer_list cpwd_timer;
156
157static int wd0_timeout = 0;
158static int wd1_timeout = 0;
159static int wd2_timeout = 0;
160
161module_param(wd0_timeout, int, 0);
162MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
163module_param(wd1_timeout, int, 0);
164MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
165module_param(wd2_timeout, int, 0);
166MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
167
168MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
169MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
170MODULE_LICENSE("GPL");
171MODULE_SUPPORTED_DEVICE("watchdog");
172
173static void cpwd_writew(u16 val, void __iomem *addr)
174{
175 writew(cpu_to_le16(val), addr);
176}
177static u16 cpwd_readw(void __iomem *addr)
178{
179 u16 val = readw(addr);
180
181 return le16_to_cpu(val);
182}
183
184static void cpwd_writeb(u8 val, void __iomem *addr)
185{
186 writeb(val, addr);
187}
188
189static u8 cpwd_readb(void __iomem *addr)
190{
191 return readb(addr);
192}
193
194
195
196
197
198
199
200
201static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
202{
203 unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
204 unsigned char setregs =
205 (index == -1) ?
206 (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
207 (p->devs[index].intr_mask);
208
209 if (enable == WD_INTR_ON)
210 curregs &= ~setregs;
211 else
212 curregs |= setregs;
213
214 cpwd_writeb(curregs, p->regs + PLD_IMASK);
215}
216
217
218
219
220static void cpwd_resetbrokentimer(struct cpwd *p, int index)
221{
222 cpwd_toggleintr(p, index, WD_INTR_ON);
223 cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
224}
225
226
227
228
229
230
231static void cpwd_brokentimer(unsigned long data)
232{
233 struct cpwd *p = (struct cpwd *) data;
234 int id, tripped = 0;
235
236
237
238
239 if (timer_pending(&cpwd_timer))
240 del_timer(&cpwd_timer);
241
242 for (id = 0; id < WD_NUMDEVS; id++) {
243 if (p->devs[id].runstatus & WD_STAT_BSTOP) {
244 ++tripped;
245 cpwd_resetbrokentimer(p, id);
246 }
247 }
248
249 if (tripped) {
250
251 cpwd_timer.expires = WD_BTIMEOUT;
252 add_timer(&cpwd_timer);
253 }
254}
255
256
257
258
259static void cpwd_pingtimer(struct cpwd *p, int index)
260{
261 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
262 cpwd_readw(p->devs[index].regs + WD_DCNTR);
263}
264
265
266
267
268
269static void cpwd_stoptimer(struct cpwd *p, int index)
270{
271 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
272 cpwd_toggleintr(p, index, WD_INTR_OFF);
273
274 if (p->broken) {
275 p->devs[index].runstatus |= WD_STAT_BSTOP;
276 cpwd_brokentimer((unsigned long) p);
277 }
278 }
279}
280
281
282
283
284
285
286
287
288static void cpwd_starttimer(struct cpwd *p, int index)
289{
290 if (p->broken)
291 p->devs[index].runstatus &= ~WD_STAT_BSTOP;
292
293 p->devs[index].runstatus &= ~WD_STAT_SVCD;
294
295 cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
296 cpwd_toggleintr(p, index, WD_INTR_ON);
297}
298
299static int cpwd_getstatus(struct cpwd *p, int index)
300{
301 unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
302 unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
303 unsigned char ret = WD_STOPPED;
304
305
306 if (!stat)
307 return ret;
308
309
310 else if (WD_S_EXPIRED & stat) {
311 ret = WD_EXPIRED;
312 } else if (WD_S_RUNNING & stat) {
313 if (intr & p->devs[index].intr_mask) {
314 ret = WD_FREERUN;
315 } else {
316
317
318
319
320
321
322
323
324
325
326
327 if (p->broken &&
328 (p->devs[index].runstatus & WD_STAT_BSTOP)) {
329 if (p->devs[index].runstatus & WD_STAT_SVCD) {
330 ret = WD_EXPIRED;
331 } else {
332
333
334 ret = WD_FREERUN;
335 }
336 } else {
337 ret = WD_RUNNING;
338 }
339 }
340 }
341
342
343 if (p->devs[index].runstatus & WD_STAT_SVCD)
344 ret |= WD_SERVICED;
345
346 return ret;
347}
348
349static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
350{
351 struct cpwd *p = dev_id;
352
353
354
355
356 spin_lock_irq(&p->lock);
357
358 cpwd_stoptimer(p, WD0_ID);
359 p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
360
361 spin_unlock_irq(&p->lock);
362
363 return IRQ_HANDLED;
364}
365
366static int cpwd_open(struct inode *inode, struct file *f)
367{
368 struct cpwd *p = cpwd_device;
369
370 lock_kernel();
371 switch (iminor(inode)) {
372 case WD0_MINOR:
373 case WD1_MINOR:
374 case WD2_MINOR:
375 break;
376
377 default:
378 unlock_kernel();
379 return -ENODEV;
380 }
381
382
383 if (!p->initialized) {
384 if (request_irq(p->irq, &cpwd_interrupt,
385 IRQF_SHARED, DRIVER_NAME, p)) {
386 printk(KERN_ERR PFX "Cannot register IRQ %d\n",
387 p->irq);
388 unlock_kernel();
389 return -EBUSY;
390 }
391 p->initialized = true;
392 }
393
394 unlock_kernel();
395
396 return nonseekable_open(inode, f);
397}
398
399static int cpwd_release(struct inode *inode, struct file *file)
400{
401 return 0;
402}
403
404static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
405{
406 static struct watchdog_info info = {
407 .options = WDIOF_SETTIMEOUT,
408 .firmware_version = 1,
409 .identity = DRIVER_NAME,
410 };
411 void __user *argp = (void __user *)arg;
412 struct inode *inode = file->f_path.dentry->d_inode;
413 int index = iminor(inode) - WD0_MINOR;
414 struct cpwd *p = cpwd_device;
415 int setopt = 0;
416
417 switch (cmd) {
418
419 case WDIOC_GETSUPPORT:
420 if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
421 return -EFAULT;
422 break;
423
424 case WDIOC_GETSTATUS:
425 case WDIOC_GETBOOTSTATUS:
426 if (put_user(0, (int __user *)argp))
427 return -EFAULT;
428 break;
429
430 case WDIOC_KEEPALIVE:
431 cpwd_pingtimer(p, index);
432 break;
433
434 case WDIOC_SETOPTIONS:
435 if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
436 return -EFAULT;
437
438 if (setopt & WDIOS_DISABLECARD) {
439 if (p->enabled)
440 return -EINVAL;
441 cpwd_stoptimer(p, index);
442 } else if (setopt & WDIOS_ENABLECARD) {
443 cpwd_starttimer(p, index);
444 } else {
445 return -EINVAL;
446 }
447 break;
448
449
450 case WIOCGSTAT:
451 setopt = cpwd_getstatus(p, index);
452 if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
453 return -EFAULT;
454 break;
455
456 case WIOCSTART:
457 cpwd_starttimer(p, index);
458 break;
459
460 case WIOCSTOP:
461 if (p->enabled)
462 return -EINVAL;
463
464 cpwd_stoptimer(p, index);
465 break;
466
467 default:
468 return -EINVAL;
469 }
470
471 return 0;
472}
473
474static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
475 unsigned long arg)
476{
477 int rval = -ENOIOCTLCMD;
478
479 switch (cmd) {
480
481 case WIOCSTART:
482 case WIOCSTOP:
483 case WIOCGSTAT:
484 lock_kernel();
485 rval = cpwd_ioctl(file, cmd, arg);
486 unlock_kernel();
487 break;
488
489
490 default:
491 break;
492 }
493
494 return rval;
495}
496
497static ssize_t cpwd_write(struct file *file, const char __user *buf,
498 size_t count, loff_t *ppos)
499{
500 struct inode *inode = file->f_path.dentry->d_inode;
501 struct cpwd *p = cpwd_device;
502 int index = iminor(inode);
503
504 if (count) {
505 cpwd_pingtimer(p, index);
506 return 1;
507 }
508
509 return 0;
510}
511
512static ssize_t cpwd_read(struct file *file, char __user *buffer,
513 size_t count, loff_t *ppos)
514{
515 return -EINVAL;
516}
517
518static const struct file_operations cpwd_fops = {
519 .owner = THIS_MODULE,
520 .unlocked_ioctl = cpwd_ioctl,
521 .compat_ioctl = cpwd_compat_ioctl,
522 .open = cpwd_open,
523 .write = cpwd_write,
524 .read = cpwd_read,
525 .release = cpwd_release,
526};
527
528static int __devinit cpwd_probe(struct of_device *op,
529 const struct of_device_id *match)
530{
531 struct device_node *options;
532 const char *str_prop;
533 const void *prop_val;
534 int i, err = -EINVAL;
535 struct cpwd *p;
536
537 if (cpwd_device)
538 return -EINVAL;
539
540 p = kzalloc(sizeof(*p), GFP_KERNEL);
541 err = -ENOMEM;
542 if (!p) {
543 printk(KERN_ERR PFX "Unable to allocate struct cpwd.\n");
544 goto out;
545 }
546
547 p->irq = op->irqs[0];
548
549 spin_lock_init(&p->lock);
550
551 p->regs = of_ioremap(&op->resource[0], 0,
552 4 * WD_TIMER_REGSZ, DRIVER_NAME);
553 if (!p->regs) {
554 printk(KERN_ERR PFX "Unable to map registers.\n");
555 goto out_free;
556 }
557
558 options = of_find_node_by_path("/options");
559 err = -ENODEV;
560 if (!options) {
561 printk(KERN_ERR PFX "Unable to find /options node.\n");
562 goto out_iounmap;
563 }
564
565 prop_val = of_get_property(options, "watchdog-enable?", NULL);
566 p->enabled = (prop_val ? true : false);
567
568 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
569 p->reboot = (prop_val ? true : false);
570
571 str_prop = of_get_property(options, "watchdog-timeout", NULL);
572 if (str_prop)
573 p->timeout = simple_strtoul(str_prop, NULL, 10);
574
575
576
577
578
579 str_prop = of_get_property(op->node, "model", NULL);
580 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
581
582 if (!p->enabled)
583 cpwd_toggleintr(p, -1, WD_INTR_OFF);
584
585 for (i = 0; i < WD_NUMDEVS; i++) {
586 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
587 static int *parms[] = { &wd0_timeout,
588 &wd1_timeout,
589 &wd2_timeout };
590 struct miscdevice *mp = &p->devs[i].misc;
591
592 mp->minor = WD0_MINOR + i;
593 mp->name = cpwd_names[i];
594 mp->fops = &cpwd_fops;
595
596 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
597 p->devs[i].intr_mask = (WD0_INTR_MASK << i);
598 p->devs[i].runstatus &= ~WD_STAT_BSTOP;
599 p->devs[i].runstatus |= WD_STAT_INIT;
600 p->devs[i].timeout = p->timeout;
601 if (*parms[i])
602 p->devs[i].timeout = *parms[i];
603
604 err = misc_register(&p->devs[i].misc);
605 if (err) {
606 printk(KERN_ERR "Could not register misc device for "
607 "dev %d\n", i);
608 goto out_unregister;
609 }
610 }
611
612 if (p->broken) {
613 init_timer(&cpwd_timer);
614 cpwd_timer.function = cpwd_brokentimer;
615 cpwd_timer.data = (unsigned long) p;
616 cpwd_timer.expires = WD_BTIMEOUT;
617
618 printk(KERN_INFO PFX "PLD defect workaround enabled for "
619 "model " WD_BADMODEL ".\n");
620 }
621
622 dev_set_drvdata(&op->dev, p);
623 cpwd_device = p;
624 err = 0;
625
626out:
627 return err;
628
629out_unregister:
630 for (i--; i >= 0; i--)
631 misc_deregister(&p->devs[i].misc);
632
633out_iounmap:
634 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
635
636out_free:
637 kfree(p);
638 goto out;
639}
640
641static int __devexit cpwd_remove(struct of_device *op)
642{
643 struct cpwd *p = dev_get_drvdata(&op->dev);
644 int i;
645
646 for (i = 0; i < 4; i++) {
647 misc_deregister(&p->devs[i].misc);
648
649 if (!p->enabled) {
650 cpwd_stoptimer(p, i);
651 if (p->devs[i].runstatus & WD_STAT_BSTOP)
652 cpwd_resetbrokentimer(p, i);
653 }
654 }
655
656 if (p->broken)
657 del_timer_sync(&cpwd_timer);
658
659 if (p->initialized)
660 free_irq(p->irq, p);
661
662 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
663 kfree(p);
664
665 cpwd_device = NULL;
666
667 return 0;
668}
669
670static const struct of_device_id cpwd_match[] = {
671 {
672 .name = "watchdog",
673 },
674 {},
675};
676MODULE_DEVICE_TABLE(of, cpwd_match);
677
678static struct of_platform_driver cpwd_driver = {
679 .name = DRIVER_NAME,
680 .match_table = cpwd_match,
681 .probe = cpwd_probe,
682 .remove = __devexit_p(cpwd_remove),
683};
684
685static int __init cpwd_init(void)
686{
687 return of_register_driver(&cpwd_driver, &of_bus_type);
688}
689
690static void __exit cpwd_exit(void)
691{
692 of_unregister_driver(&cpwd_driver);
693}
694
695module_init(cpwd_init);
696module_exit(cpwd_exit);
697