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(struct timer_list *unused)
234{
235 struct cpwd *p = cpwd_device;
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(NULL);
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 = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
542 if (!p)
543 return -ENOMEM;
544
545 p->irq = op->archdata.irqs[0];
546
547 spin_lock_init(&p->lock);
548
549 p->regs = of_ioremap(&op->resource[0], 0,
550 4 * WD_TIMER_REGSZ, DRIVER_NAME);
551 if (!p->regs) {
552 pr_err("Unable to map registers\n");
553 return -ENOMEM;
554 }
555
556 options = of_find_node_by_path("/options");
557 if (!options) {
558 err = -ENODEV;
559 pr_err("Unable to find /options node\n");
560 goto out_iounmap;
561 }
562
563 prop_val = of_get_property(options, "watchdog-enable?", NULL);
564 p->enabled = (prop_val ? true : false);
565
566 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
567 p->reboot = (prop_val ? true : false);
568
569 str_prop = of_get_property(options, "watchdog-timeout", NULL);
570 if (str_prop)
571 p->timeout = simple_strtoul(str_prop, NULL, 10);
572
573
574
575
576
577 str_prop = of_get_property(op->dev.of_node, "model", NULL);
578 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
579
580 if (!p->enabled)
581 cpwd_toggleintr(p, -1, WD_INTR_OFF);
582
583 for (i = 0; i < WD_NUMDEVS; i++) {
584 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
585 static int *parms[] = { &wd0_timeout,
586 &wd1_timeout,
587 &wd2_timeout };
588 struct miscdevice *mp = &p->devs[i].misc;
589
590 mp->minor = WD0_MINOR + i;
591 mp->name = cpwd_names[i];
592 mp->fops = &cpwd_fops;
593
594 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
595 p->devs[i].intr_mask = (WD0_INTR_MASK << i);
596 p->devs[i].runstatus &= ~WD_STAT_BSTOP;
597 p->devs[i].runstatus |= WD_STAT_INIT;
598 p->devs[i].timeout = p->timeout;
599 if (*parms[i])
600 p->devs[i].timeout = *parms[i];
601
602 err = misc_register(&p->devs[i].misc);
603 if (err) {
604 pr_err("Could not register misc device for dev %d\n",
605 i);
606 goto out_unregister;
607 }
608 }
609
610 if (p->broken) {
611 timer_setup(&cpwd_timer, cpwd_brokentimer, 0);
612 cpwd_timer.expires = WD_BTIMEOUT;
613
614 pr_info("PLD defect workaround enabled for model %s\n",
615 WD_BADMODEL);
616 }
617
618 platform_set_drvdata(op, p);
619 cpwd_device = p;
620 return 0;
621
622out_unregister:
623 for (i--; i >= 0; i--)
624 misc_deregister(&p->devs[i].misc);
625
626out_iounmap:
627 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
628
629 return err;
630}
631
632static int cpwd_remove(struct platform_device *op)
633{
634 struct cpwd *p = platform_get_drvdata(op);
635 int i;
636
637 for (i = 0; i < WD_NUMDEVS; i++) {
638 misc_deregister(&p->devs[i].misc);
639
640 if (!p->enabled) {
641 cpwd_stoptimer(p, i);
642 if (p->devs[i].runstatus & WD_STAT_BSTOP)
643 cpwd_resetbrokentimer(p, i);
644 }
645 }
646
647 if (p->broken)
648 del_timer_sync(&cpwd_timer);
649
650 if (p->initialized)
651 free_irq(p->irq, p);
652
653 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
654
655 cpwd_device = NULL;
656
657 return 0;
658}
659
660static const struct of_device_id cpwd_match[] = {
661 {
662 .name = "watchdog",
663 },
664 {},
665};
666MODULE_DEVICE_TABLE(of, cpwd_match);
667
668static struct platform_driver cpwd_driver = {
669 .driver = {
670 .name = DRIVER_NAME,
671 .of_match_table = cpwd_match,
672 },
673 .probe = cpwd_probe,
674 .remove = cpwd_remove,
675};
676
677module_platform_driver(cpwd_driver);
678