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/compat.h>
30#include <linux/slab.h>
31#include <linux/mutex.h>
32#include <linux/io.h>
33#include <linux/of.h>
34#include <linux/of_device.h>
35#include <linux/uaccess.h>
36
37#include <asm/irq.h>
38#include <asm/watchdog.h>
39
40#define DRIVER_NAME "cpwd"
41
42#define WD_OBPNAME "watchdog"
43#define WD_BADMODEL "SUNW,501-5336"
44#define WD_BTIMEOUT (jiffies + (HZ * 1000))
45#define WD_BLIMIT 0xFFFF
46
47#define WD0_MINOR 212
48#define WD1_MINOR 213
49#define WD2_MINOR 214
50
51
52#define WD0_ID 0
53#define WD1_ID 1
54#define WD2_ID 2
55#define WD_NUMDEVS 3
56
57#define WD_INTR_OFF 0
58#define WD_INTR_ON 1
59
60#define WD_STAT_INIT 0x01
61#define WD_STAT_BSTOP 0x02
62#define WD_STAT_SVCD 0x04
63
64
65
66#define WD0_INTR_MASK 0x01
67#define WD1_INTR_MASK 0x02
68#define WD2_INTR_MASK 0x04
69
70#define WD_S_RUNNING 0x01
71#define WD_S_EXPIRED 0x02
72
73struct cpwd {
74 void __iomem *regs;
75 spinlock_t lock;
76
77 unsigned int irq;
78
79 unsigned long timeout;
80 bool enabled;
81 bool reboot;
82 bool broken;
83 bool initialized;
84
85 struct {
86 struct miscdevice misc;
87 void __iomem *regs;
88 u8 intr_mask;
89 u8 runstatus;
90 u16 timeout;
91 } devs[WD_NUMDEVS];
92};
93
94static DEFINE_MUTEX(cpwd_mutex);
95static struct cpwd *cpwd_device;
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
146#define WD_TIMER_REGSZ 16
147#define WD0_OFF 0
148#define WD1_OFF (WD_TIMER_REGSZ * 1)
149#define WD2_OFF (WD_TIMER_REGSZ * 2)
150#define PLD_OFF (WD_TIMER_REGSZ * 3)
151
152#define WD_DCNTR 0x00
153#define WD_LIMIT 0x04
154#define WD_STATUS 0x08
155
156#define PLD_IMASK (PLD_OFF + 0x00)
157#define PLD_STATUS (PLD_OFF + 0x04)
158
159static struct timer_list cpwd_timer;
160
161static int wd0_timeout;
162static int wd1_timeout;
163static int wd2_timeout;
164
165module_param(wd0_timeout, int, 0);
166MODULE_PARM_DESC(wd0_timeout, "Default watchdog0 timeout in 1/10secs");
167module_param(wd1_timeout, int, 0);
168MODULE_PARM_DESC(wd1_timeout, "Default watchdog1 timeout in 1/10secs");
169module_param(wd2_timeout, int, 0);
170MODULE_PARM_DESC(wd2_timeout, "Default watchdog2 timeout in 1/10secs");
171
172MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
173MODULE_DESCRIPTION("Hardware watchdog driver for Sun Microsystems CP1400/1500");
174MODULE_LICENSE("GPL");
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, unsigned long arg)
477{
478 return cpwd_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
479}
480
481static ssize_t cpwd_write(struct file *file, const char __user *buf,
482 size_t count, loff_t *ppos)
483{
484 struct inode *inode = file_inode(file);
485 struct cpwd *p = cpwd_device;
486 int index = iminor(inode);
487
488 if (count) {
489 cpwd_pingtimer(p, index);
490 return 1;
491 }
492
493 return 0;
494}
495
496static ssize_t cpwd_read(struct file *file, char __user *buffer,
497 size_t count, loff_t *ppos)
498{
499 return -EINVAL;
500}
501
502static const struct file_operations cpwd_fops = {
503 .owner = THIS_MODULE,
504 .unlocked_ioctl = cpwd_ioctl,
505 .compat_ioctl = cpwd_compat_ioctl,
506 .open = cpwd_open,
507 .write = cpwd_write,
508 .read = cpwd_read,
509 .release = cpwd_release,
510 .llseek = no_llseek,
511};
512
513static int cpwd_probe(struct platform_device *op)
514{
515 struct device_node *options;
516 const char *str_prop;
517 const void *prop_val;
518 int i, err = -EINVAL;
519 struct cpwd *p;
520
521 if (cpwd_device)
522 return -EINVAL;
523
524 p = devm_kzalloc(&op->dev, sizeof(*p), GFP_KERNEL);
525 if (!p)
526 return -ENOMEM;
527
528 p->irq = op->archdata.irqs[0];
529
530 spin_lock_init(&p->lock);
531
532 p->regs = of_ioremap(&op->resource[0], 0,
533 4 * WD_TIMER_REGSZ, DRIVER_NAME);
534 if (!p->regs) {
535 pr_err("Unable to map registers\n");
536 return -ENOMEM;
537 }
538
539 options = of_find_node_by_path("/options");
540 if (!options) {
541 err = -ENODEV;
542 pr_err("Unable to find /options node\n");
543 goto out_iounmap;
544 }
545
546 prop_val = of_get_property(options, "watchdog-enable?", NULL);
547 p->enabled = (prop_val ? true : false);
548
549 prop_val = of_get_property(options, "watchdog-reboot?", NULL);
550 p->reboot = (prop_val ? true : false);
551
552 str_prop = of_get_property(options, "watchdog-timeout", NULL);
553 if (str_prop)
554 p->timeout = simple_strtoul(str_prop, NULL, 10);
555
556 of_node_put(options);
557
558
559
560
561
562 str_prop = of_get_property(op->dev.of_node, "model", NULL);
563 p->broken = (str_prop && !strcmp(str_prop, WD_BADMODEL));
564
565 if (!p->enabled)
566 cpwd_toggleintr(p, -1, WD_INTR_OFF);
567
568 for (i = 0; i < WD_NUMDEVS; i++) {
569 static const char *cpwd_names[] = { "RIC", "XIR", "POR" };
570 static int *parms[] = { &wd0_timeout,
571 &wd1_timeout,
572 &wd2_timeout };
573 struct miscdevice *mp = &p->devs[i].misc;
574
575 mp->minor = WD0_MINOR + i;
576 mp->name = cpwd_names[i];
577 mp->fops = &cpwd_fops;
578
579 p->devs[i].regs = p->regs + (i * WD_TIMER_REGSZ);
580 p->devs[i].intr_mask = (WD0_INTR_MASK << i);
581 p->devs[i].runstatus &= ~WD_STAT_BSTOP;
582 p->devs[i].runstatus |= WD_STAT_INIT;
583 p->devs[i].timeout = p->timeout;
584 if (*parms[i])
585 p->devs[i].timeout = *parms[i];
586
587 err = misc_register(&p->devs[i].misc);
588 if (err) {
589 pr_err("Could not register misc device for dev %d\n",
590 i);
591 goto out_unregister;
592 }
593 }
594
595 if (p->broken) {
596 timer_setup(&cpwd_timer, cpwd_brokentimer, 0);
597 cpwd_timer.expires = WD_BTIMEOUT;
598
599 pr_info("PLD defect workaround enabled for model %s\n",
600 WD_BADMODEL);
601 }
602
603 platform_set_drvdata(op, p);
604 cpwd_device = p;
605 return 0;
606
607out_unregister:
608 for (i--; i >= 0; i--)
609 misc_deregister(&p->devs[i].misc);
610
611out_iounmap:
612 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
613
614 return err;
615}
616
617static int cpwd_remove(struct platform_device *op)
618{
619 struct cpwd *p = platform_get_drvdata(op);
620 int i;
621
622 for (i = 0; i < WD_NUMDEVS; i++) {
623 misc_deregister(&p->devs[i].misc);
624
625 if (!p->enabled) {
626 cpwd_stoptimer(p, i);
627 if (p->devs[i].runstatus & WD_STAT_BSTOP)
628 cpwd_resetbrokentimer(p, i);
629 }
630 }
631
632 if (p->broken)
633 del_timer_sync(&cpwd_timer);
634
635 if (p->initialized)
636 free_irq(p->irq, p);
637
638 of_iounmap(&op->resource[0], p->regs, 4 * WD_TIMER_REGSZ);
639
640 cpwd_device = NULL;
641
642 return 0;
643}
644
645static const struct of_device_id cpwd_match[] = {
646 {
647 .name = "watchdog",
648 },
649 {},
650};
651MODULE_DEVICE_TABLE(of, cpwd_match);
652
653static struct platform_driver cpwd_driver = {
654 .driver = {
655 .name = DRIVER_NAME,
656 .of_match_table = cpwd_match,
657 },
658 .probe = cpwd_probe,
659 .remove = cpwd_remove,
660};
661
662module_platform_driver(cpwd_driver);
663