1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/fs.h>
23#include <linux/errno.h>
24#include <linux/major.h>
25#include <linux/miscdevice.h>
26#include <linux/interrupt.h>
27#include <linux/ioport.h>
28#include <linux/timer.h>
29#include <linux/slab.h>
30#include <linux/mutex.h>
31#include <linux/io.h>
32#include <linux/of.h>
33#include <linux/of_device.h>
34#include <linux/uaccess.h>
35
36#include <asm/irq.h>
37#include <asm/watchdog.h>
38
39#define DRIVER_NAME "cpwd"
40
41#define WD_OBPNAME "watchdog"
42#define WD_BADMODEL "SUNW,501-5336"
43#define WD_BTIMEOUT (jiffies + (HZ * 1000))
44#define WD_BLIMIT 0xFFFF
45
46#define WD0_MINOR 212
47#define WD1_MINOR 213
48#define WD2_MINOR 214
49
50
51#define WD0_ID 0
52#define WD1_ID 1
53#define WD2_ID 2
54#define WD_NUMDEVS 3
55
56#define WD_INTR_OFF 0
57#define WD_INTR_ON 1
58
59#define WD_STAT_INIT 0x01
60#define WD_STAT_BSTOP 0x02
61#define WD_STAT_SVCD 0x04
62
63
64
65#define WD0_INTR_MASK 0x01
66#define WD1_INTR_MASK 0x02
67#define WD2_INTR_MASK 0x04
68
69#define WD_S_RUNNING 0x01
70#define WD_S_EXPIRED 0x02
71
72struct cpwd {
73 void __iomem *regs;
74 spinlock_t lock;
75
76 unsigned int irq;
77
78 unsigned long timeout;
79 bool enabled;
80 bool reboot;
81 bool broken;
82 bool initialized;
83
84 struct {
85 struct miscdevice misc;
86 void __iomem *regs;
87 u8 intr_mask;
88 u8 runstatus;
89 u16 timeout;
90 } devs[WD_NUMDEVS];
91};
92
93static DEFINE_MUTEX(cpwd_mutex);
94static struct cpwd *cpwd_device;
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
145#define WD_TIMER_REGSZ 16
146#define WD0_OFF 0
147#define WD1_OFF (WD_TIMER_REGSZ * 1)
148#define WD2_OFF (WD_TIMER_REGSZ * 2)
149#define PLD_OFF (WD_TIMER_REGSZ * 3)
150
151#define WD_DCNTR 0x00
152#define WD_LIMIT 0x04
153#define WD_STATUS 0x08
154
155#define PLD_IMASK (PLD_OFF + 0x00)
156#define PLD_STATUS (PLD_OFF + 0x04)
157
158static struct timer_list cpwd_timer;
159
160static int wd0_timeout;
161static int wd1_timeout;
162static int wd2_timeout;
163
164module_param(wd0_timeout, int, 0);
165MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
166module_param(wd1_timeout, int, 0);
167MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
168module_param(wd2_timeout, int, 0);
169MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
170
171MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
172MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
173MODULE_LICENSE("GPL");
174MODULE_SUPPORTED_DEVICE("watchdog");
175
176static void cpwd_writew(u16 val, void __iomem *addr)
177{
178 writew(cpu_to_le16(val), addr);
179}
180static u16 cpwd_readw(void __iomem *addr)
181{
182 u16 val = readw(addr);
183
184 return le16_to_cpu(val);
185}
186
187static void cpwd_writeb(u8 val, void __iomem *addr)
188{
189 writeb(val, addr);
190}
191
192static u8 cpwd_readb(void __iomem *addr)
193{
194 return readb(addr);
195}
196
197
198
199
200
201
202
203
204static void cpwd_toggleintr(struct cpwd *p, int index, int enable)
205{
206 unsigned char curregs = cpwd_readb(p->regs + PLD_IMASK);
207 unsigned char setregs =
208 (index == -1) ?
209 (WD0_INTR_MASK | WD1_INTR_MASK | WD2_INTR_MASK) :
210 (p->devs[index].intr_mask);
211
212 if (enable == WD_INTR_ON)
213 curregs &= ~setregs;
214 else
215 curregs |= setregs;
216
217 cpwd_writeb(curregs, p->regs + PLD_IMASK);
218}
219
220
221
222
223static void cpwd_resetbrokentimer(struct cpwd *p, int index)
224{
225 cpwd_toggleintr(p, index, WD_INTR_ON);
226 cpwd_writew(WD_BLIMIT, p->devs[index].regs + WD_LIMIT);
227}
228
229
230
231
232
233
234static void cpwd_brokentimer(struct timer_list *unused)
235{
236 struct cpwd *p = cpwd_device;
237 int id, tripped = 0;
238
239
240
241
242 if (timer_pending(&cpwd_timer))
243 del_timer(&cpwd_timer);
244
245 for (id = 0; id < WD_NUMDEVS; id++) {
246 if (p->devs[id].runstatus & WD_STAT_BSTOP) {
247 ++tripped;
248 cpwd_resetbrokentimer(p, id);
249 }
250 }
251
252 if (tripped) {
253
254 cpwd_timer.expires = WD_BTIMEOUT;
255 add_timer(&cpwd_timer);
256 }
257}
258
259
260
261
262static void cpwd_pingtimer(struct cpwd *p, int index)
263{
264 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING)
265 cpwd_readw(p->devs[index].regs + WD_DCNTR);
266}
267
268
269
270
271
272static void cpwd_stoptimer(struct cpwd *p, int index)
273{
274 if (cpwd_readb(p->devs[index].regs + WD_STATUS) & WD_S_RUNNING) {
275 cpwd_toggleintr(p, index, WD_INTR_OFF);
276
277 if (p->broken) {
278 p->devs[index].runstatus |= WD_STAT_BSTOP;
279 cpwd_brokentimer(NULL);
280 }
281 }
282}
283
284
285
286
287
288
289
290
291static void cpwd_starttimer(struct cpwd *p, int index)
292{
293 if (p->broken)
294 p->devs[index].runstatus &= ~WD_STAT_BSTOP;
295
296 p->devs[index].runstatus &= ~WD_STAT_SVCD;
297
298 cpwd_writew(p->devs[index].timeout, p->devs[index].regs + WD_LIMIT);
299 cpwd_toggleintr(p, index, WD_INTR_ON);
300}
301
302static int cpwd_getstatus(struct cpwd *p, int index)
303{
304 unsigned char stat = cpwd_readb(p->devs[index].regs + WD_STATUS);
305 unsigned char intr = cpwd_readb(p->devs[index].regs + PLD_IMASK);
306 unsigned char ret = WD_STOPPED;
307
308
309 if (!stat)
310 return ret;
311
312
313 else if (WD_S_EXPIRED & stat) {
314 ret = WD_EXPIRED;
315 } else if (WD_S_RUNNING & stat) {
316 if (intr & p->devs[index].intr_mask) {
317 ret = WD_FREERUN;
318 } else {
319
320
321
322
323
324
325
326
327
328
329
330 if (p->broken &&
331 (p->devs[index].runstatus & WD_STAT_BSTOP)) {
332 if (p->devs[index].runstatus & WD_STAT_SVCD) {
333 ret = WD_EXPIRED;
334 } else {
335
336
337 ret = WD_FREERUN;
338 }
339 } else {
340 ret = WD_RUNNING;
341 }
342 }
343 }
344
345
346 if (p->devs[index].runstatus & WD_STAT_SVCD)
347 ret |= WD_SERVICED;
348
349 return ret;
350}
351
352static irqreturn_t cpwd_interrupt(int irq, void *dev_id)
353{
354 struct cpwd *p = dev_id;
355
356
357
358
359 spin_lock_irq(&p->lock);
360
361 cpwd_stoptimer(p, WD0_ID);
362 p->devs[WD0_ID].runstatus |= WD_STAT_SVCD;
363
364 spin_unlock_irq(&p->lock);
365
366 return IRQ_HANDLED;
367}
368
369static int cpwd_open(struct inode *inode, struct file *f)
370{
371 struct cpwd *p = cpwd_device;
372
373 mutex_lock(&cpwd_mutex);
374 switch (iminor(inode)) {
375 case WD0_MINOR:
376 case WD1_MINOR:
377 case WD2_MINOR:
378 break;
379
380 default:
381 mutex_unlock(&cpwd_mutex);
382 return -ENODEV;
383 }
384
385
386 if (!p->initialized) {
387 if (request_irq(p->irq, &cpwd_interrupt,
388 IRQF_SHARED, DRIVER_NAME, p)) {
389 pr_err("Cannot register IRQ %d\n", 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 stream_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_inode(file);
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_inode(file);
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 cpwd_probe(struct platform_device *op)
532{
533 struct device_node *options;
534 const char *str_prop;
535 const void *prop_val;
536 int i, err = -EINVAL;
537 struct cpwd *p;
538
539 if (cpwd_device)
540 return -EINVAL;
541
542 p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
543 if (!p)
544 return -ENOMEM;
545
546 p->irq = op->archdata.irqs[0];
547
548 spin_lock_init(&p->lock);
549
550 p->regs = of_ioremap(&op->resource[0], 0,
551 4 * WD_TIMER_REGSZ, DRIVER_NAME);
552 if (!p->regs) {
553 pr_err("Unable to map registers\n");
554 return -ENOMEM;
555 }
556
557 options = of_find_node_by_path("/options");
558 if (!options) {
559 err = -ENODEV;
560 pr_err("Unable to find /options node\n");
561 goto out_iounmap;
562 }
563
564 prop_val = of_get_property(options, "watchdog-enable?", NULL);
565 p->enabled = (prop_val ? true : false);
566
567 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
568 p->reboot = (prop_val ? true : false);
569
570 str_prop = of_get_property(options, "watchdog-timeout", NULL);
571 if (str_prop)
572 p->timeout = simple_strtoul(str_prop, NULL, 10);
573
574 of_node_put(options);
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 timer_setup(&cpwd_timer, cpwd_brokentimer, 0);
615 cpwd_timer.expires = WD_BTIMEOUT;
616
617 pr_info("PLD defect workaround enabled for model %s\n",
618 WD_BADMODEL);
619 }
620
621 platform_set_drvdata(op, p);
622 cpwd_device = p;
623 return 0;
624
625out_unregister:
626 for (i--; i >= 0; i--)
627 misc_deregister(&p->devs[i].misc);
628
629out_iounmap:
630 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
631
632 return err;
633}
634
635static int cpwd_remove(struct platform_device *op)
636{
637 struct cpwd *p = platform_get_drvdata(op);
638 int i;
639
640 for (i = 0; i < WD_NUMDEVS; i++) {
641 misc_deregister(&p->devs[i].misc);
642
643 if (!p->enabled) {
644 cpwd_stoptimer(p, i);
645 if (p->devs[i].runstatus & WD_STAT_BSTOP)
646 cpwd_resetbrokentimer(p, i);
647 }
648 }
649
650 if (p->broken)
651 del_timer_sync(&cpwd_timer);
652
653 if (p->initialized)
654 free_irq(p->irq, p);
655
656 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
657
658 cpwd_device = NULL;
659
660 return 0;
661}
662
663static const struct of_device_id cpwd_match[] = {
664 {
665 .name = "watchdog",
666 },
667 {},
668};
669MODULE_DEVICE_TABLE(of, cpwd_match);
670
671static struct platform_driver cpwd_driver = {
672 .driver = {
673 .name = DRIVER_NAME,
674 .of_match_table = cpwd_match,
675 },
676 .probe = cpwd_probe,
677 .remove = cpwd_remove,
678};
679
680module_platform_driver(cpwd_driver);
681