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