1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/fs.h>
22#include <linux/errno.h>
23#include <linux/major.h>
24#include <linux/miscdevice.h>
25#include <linux/interrupt.h>
26#include <linux/ioport.h>
27#include <linux/timer.h>
28#include <linux/slab.h>
29#include <linux/mutex.h>
30#include <linux/io.h>
31#include <linux/of.h>
32#include <linux/of_device.h>
33#include <linux/uaccess.h>
34
35#include <asm/irq.h>
36#include <asm/watchdog.h>
37
38#define DRIVER_NAME "cpwd"
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 pr_err("Cannot register IRQ %d\n", p->irq);
389 mutex_unlock(&cpwd_mutex);
390 return -EBUSY;
391 }
392 p->initialized = true;
393 }
394
395 mutex_unlock(&cpwd_mutex);
396
397 return nonseekable_open(inode, f);
398}
399
400static int cpwd_release(struct inode *inode, struct file *file)
401{
402 return 0;
403}
404
405static long cpwd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
406{
407 static const struct watchdog_info info = {
408 .options = WDIOF_SETTIMEOUT,
409 .firmware_version = 1,
410 .identity = DRIVER_NAME,
411 };
412 void __user *argp = (void __user *)arg;
413 struct inode *inode = file_inode(file);
414 int index = iminor(inode) - WD0_MINOR;
415 struct cpwd *p = cpwd_device;
416 int setopt = 0;
417
418 switch (cmd) {
419
420 case WDIOC_GETSUPPORT:
421 if (copy_to_user(argp, &info, sizeof(struct watchdog_info)))
422 return -EFAULT;
423 break;
424
425 case WDIOC_GETSTATUS:
426 case WDIOC_GETBOOTSTATUS:
427 if (put_user(0, (int __user *)argp))
428 return -EFAULT;
429 break;
430
431 case WDIOC_KEEPALIVE:
432 cpwd_pingtimer(p, index);
433 break;
434
435 case WDIOC_SETOPTIONS:
436 if (copy_from_user(&setopt, argp, sizeof(unsigned int)))
437 return -EFAULT;
438
439 if (setopt & WDIOS_DISABLECARD) {
440 if (p->enabled)
441 return -EINVAL;
442 cpwd_stoptimer(p, index);
443 } else if (setopt & WDIOS_ENABLECARD) {
444 cpwd_starttimer(p, index);
445 } else {
446 return -EINVAL;
447 }
448 break;
449
450
451 case WIOCGSTAT:
452 setopt = cpwd_getstatus(p, index);
453 if (copy_to_user(argp, &setopt, sizeof(unsigned int)))
454 return -EFAULT;
455 break;
456
457 case WIOCSTART:
458 cpwd_starttimer(p, index);
459 break;
460
461 case WIOCSTOP:
462 if (p->enabled)
463 return -EINVAL;
464
465 cpwd_stoptimer(p, index);
466 break;
467
468 default:
469 return -EINVAL;
470 }
471
472 return 0;
473}
474
475static long cpwd_compat_ioctl(struct file *file, unsigned int cmd,
476 unsigned long arg)
477{
478 int rval = -ENOIOCTLCMD;
479
480 switch (cmd) {
481
482 case WIOCSTART:
483 case WIOCSTOP:
484 case WIOCGSTAT:
485 mutex_lock(&cpwd_mutex);
486 rval = cpwd_ioctl(file, cmd, arg);
487 mutex_unlock(&cpwd_mutex);
488 break;
489
490
491 default:
492 break;
493 }
494
495 return rval;
496}
497
498static ssize_t cpwd_write(struct file *file, const char __user *buf,
499 size_t count, loff_t *ppos)
500{
501 struct inode *inode = file_inode(file);
502 struct cpwd *p = cpwd_device;
503 int index = iminor(inode);
504
505 if (count) {
506 cpwd_pingtimer(p, index);
507 return 1;
508 }
509
510 return 0;
511}
512
513static ssize_t cpwd_read(struct file *file, char __user *buffer,
514 size_t count, loff_t *ppos)
515{
516 return -EINVAL;
517}
518
519static const struct file_operations cpwd_fops = {
520 .owner = THIS_MODULE,
521 .unlocked_ioctl = cpwd_ioctl,
522 .compat_ioctl = cpwd_compat_ioctl,
523 .open = cpwd_open,
524 .write = cpwd_write,
525 .read = cpwd_read,
526 .release = cpwd_release,
527 .llseek = no_llseek,
528};
529
530static int cpwd_probe(struct platform_device *op)
531{
532 struct device_node *options;
533 const char *str_prop;
534 const void *prop_val;
535 int i, err = -EINVAL;
536 struct cpwd *p;
537
538 if (cpwd_device)
539 return -EINVAL;
540
541 p = kzalloc(sizeof(*p), GFP_KERNEL);
542 err = -ENOMEM;
543 if (!p) {
544 pr_err("Unable to allocate struct cpwd\n");
545 goto out;
546 }
547
548 p->irq = op->archdata.irqs[0];
549
550 spin_lock_init(&p->lock);
551
552 p->regs = of_ioremap(&op->resource[0], 0,
553 4 * WD_TIMER_REGSZ, DRIVER_NAME);
554 if (!p->regs) {
555 pr_err("Unable to map registers\n");
556 goto out_free;
557 }
558
559 options = of_find_node_by_path("/options");
560 err = -ENODEV;
561 if (!options) {
562 pr_err("Unable to find /options node\n");
563 goto out_iounmap;
564 }
565
566 prop_val = of_get_property(options, "watchdog-enable?", NULL);
567 p->enabled = (prop_val ? true : false);
568
569 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
570 p->reboot = (prop_val ? true : false);
571
572 str_prop = of_get_property(options, "watchdog-timeout", NULL);
573 if (str_prop)
574 p->timeout = simple_strtoul(str_prop, NULL, 10);
575
576
577
578
579
580 str_prop = of_get_property(op->dev.of_node, "model", NULL);
581 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
582
583 if (!p->enabled)
584 cpwd_toggleintr(p, -1, WD_INTR_OFF);
585
586 for (i = 0; i < WD_NUMDEVS; i++) {
587 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
588 static int *parms[] = { &wd0_timeout,
589 &wd1_timeout,
590 &wd2_timeout };
591 struct miscdevice *mp = &p->devs[i].misc;
592
593 mp->minor = WD0_MINOR + i;
594 mp->name = cpwd_names[i];
595 mp->fops = &cpwd_fops;
596
597 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
598 p->devs[i].intr_mask = (WD0_INTR_MASK << i);
599 p->devs[i].runstatus &= ~WD_STAT_BSTOP;
600 p->devs[i].runstatus |= WD_STAT_INIT;
601 p->devs[i].timeout = p->timeout;
602 if (*parms[i])
603 p->devs[i].timeout = *parms[i];
604
605 err = misc_register(&p->devs[i].misc);
606 if (err) {
607 pr_err("Could not register misc device for dev %d\n",
608 i);
609 goto out_unregister;
610 }
611 }
612
613 if (p->broken) {
614 init_timer(&cpwd_timer);
615 cpwd_timer.function = cpwd_brokentimer;
616 cpwd_timer.data = (unsigned long) p;
617 cpwd_timer.expires = WD_BTIMEOUT;
618
619 pr_info("PLD defect workaround enabled for model %s\n",
620 WD_BADMODEL);
621 }
622
623 platform_set_drvdata(op, p);
624 cpwd_device = p;
625 err = 0;
626
627out:
628 return err;
629
630out_unregister:
631 for (i--; i >= 0; i--)
632 misc_deregister(&p->devs[i].misc);
633
634out_iounmap:
635 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
636
637out_free:
638 kfree(p);
639 goto out;
640}
641
642static int cpwd_remove(struct platform_device *op)
643{
644 struct cpwd *p = platform_get_drvdata(op);
645 int i;
646
647 for (i = 0; i < WD_NUMDEVS; i++) {
648 misc_deregister(&p->devs[i].misc);
649
650 if (!p->enabled) {
651 cpwd_stoptimer(p, i);
652 if (p->devs[i].runstatus & WD_STAT_BSTOP)
653 cpwd_resetbrokentimer(p, i);
654 }
655 }
656
657 if (p->broken)
658 del_timer_sync(&cpwd_timer);
659
660 if (p->initialized)
661 free_irq(p->irq, p);
662
663 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
664 kfree(p);
665
666 cpwd_device = NULL;
667
668 return 0;
669}
670
671static const struct of_device_id cpwd_match[] = {
672 {
673 .name = "watchdog",
674 },
675 {},
676};
677MODULE_DEVICE_TABLE(of, cpwd_match);
678
679static struct platform_driver cpwd_driver = {
680 .driver = {
681 .name = DRIVER_NAME,
682 .of_match_table = cpwd_match,
683 },
684 .probe = cpwd_probe,
685 .remove = cpwd_remove,
686};
687
688module_platform_driver(cpwd_driver);
689