1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29#include <linux/fs.h>
30#include <linux/init.h>
31#include <linux/kernel.h>
32#include <linux/miscdevice.h>
33#include <linux/module.h>
34#include <linux/notifier.h>
35#include <linux/reboot.h>
36#include <linux/types.h>
37#include <linux/watchdog.h>
38#include <linux/uaccess.h>
39
40#include <asm/rtas.h>
41
42#define WDRTAS_MAGIC_CHAR 42
43#define WDRTAS_SUPPORTED_MASK (WDIOF_SETTIMEOUT | \
44 WDIOF_MAGICCLOSE)
45
46MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
47MODULE_DESCRIPTION("RTAS watchdog driver");
48MODULE_LICENSE("GPL");
49MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
50MODULE_ALIAS_MISCDEV(TEMP_MINOR);
51
52#ifdef CONFIG_WATCHDOG_NOWAYOUT
53static int wdrtas_nowayout = 1;
54#else
55static int wdrtas_nowayout = 0;
56#endif
57
58static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
59static char wdrtas_expect_close;
60
61static int wdrtas_interval;
62
63#define WDRTAS_THERMAL_SENSOR 3
64static int wdrtas_token_get_sensor_state;
65#define WDRTAS_SURVEILLANCE_IND 9000
66static int wdrtas_token_set_indicator;
67#define WDRTAS_SP_SPI 28
68static int wdrtas_token_get_sp;
69static int wdrtas_token_event_scan;
70
71#define WDRTAS_DEFAULT_INTERVAL 300
72
73#define WDRTAS_LOGBUFFER_LEN 128
74static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90static int wdrtas_set_interval(int interval)
91{
92 long result;
93 static int print_msg = 10;
94
95
96 interval = (interval + 59) / 60;
97
98 result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
99 WDRTAS_SURVEILLANCE_IND, 0, interval);
100 if (result < 0 && print_msg) {
101 printk(KERN_ERR "wdrtas: setting the watchdog to %i "
102 "timeout failed: %li\n", interval, result);
103 print_msg--;
104 }
105
106 return result;
107}
108
109#define WDRTAS_SP_SPI_LEN 4
110
111
112
113
114
115
116
117
118
119
120
121static int wdrtas_get_interval(int fallback_value)
122{
123 long result;
124 char value[WDRTAS_SP_SPI_LEN];
125
126 spin_lock(&rtas_data_buf_lock);
127 memset(rtas_data_buf, 0, WDRTAS_SP_SPI_LEN);
128 result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
129 WDRTAS_SP_SPI, __pa(rtas_data_buf),
130 WDRTAS_SP_SPI_LEN);
131
132 memcpy(value, rtas_data_buf, WDRTAS_SP_SPI_LEN);
133 spin_unlock(&rtas_data_buf_lock);
134
135 if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
136 printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog "
137 "timeout (%li). Continuing\n", result);
138 return fallback_value;
139 }
140
141
142 return ((int)value[2]) * 60;
143}
144
145
146
147
148
149
150
151static void wdrtas_timer_start(void)
152{
153 wdrtas_set_interval(wdrtas_interval);
154}
155
156
157
158
159
160
161
162static void wdrtas_timer_stop(void)
163{
164 wdrtas_set_interval(0);
165}
166
167
168
169
170
171
172
173static void wdrtas_log_scanned_event(void)
174{
175 int i;
176
177 for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
178 printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = "
179 "%02x %02x %02x %02x %02x %02x %02x %02x "
180 "%02x %02x %02x %02x %02x %02x %02x %02x\n",
181 (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
182 wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
183 wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
184 wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
185 wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
186 wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
187 wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
188 wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
189 wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
190}
191
192
193
194
195
196
197
198
199static void wdrtas_timer_keepalive(void)
200{
201 long result;
202
203 do {
204 result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
205 RTAS_EVENT_SCAN_ALL_EVENTS, 0,
206 (void *)__pa(wdrtas_logbuffer),
207 WDRTAS_LOGBUFFER_LEN);
208 if (result < 0)
209 printk(KERN_ERR "wdrtas: event-scan failed: %li\n",
210 result);
211 if (result == 0)
212 wdrtas_log_scanned_event();
213 } while (result == 0);
214}
215
216
217
218
219
220
221
222
223
224static int wdrtas_get_temperature(void)
225{
226 long result;
227 int temperature = 0;
228
229 result = rtas_call(wdrtas_token_get_sensor_state, 2, 2,
230 (void *)__pa(&temperature),
231 WDRTAS_THERMAL_SENSOR, 0);
232
233 if (result < 0)
234 printk(KERN_WARNING "wdrtas: reading the thermal sensor "
235 "faild: %li\n", result);
236 else
237 temperature = ((temperature * 9) / 5) + 32;
238
239 return temperature;
240}
241
242
243
244
245
246
247
248static int wdrtas_get_status(void)
249{
250 return 0;
251}
252
253
254
255
256
257
258
259static int wdrtas_get_boot_status(void)
260{
261 return 0;
262}
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279static ssize_t wdrtas_write(struct file *file, const char __user *buf,
280 size_t len, loff_t *ppos)
281{
282 int i;
283 char c;
284
285 if (!len)
286 goto out;
287
288 if (!wdrtas_nowayout) {
289 wdrtas_expect_close = 0;
290
291 for (i = 0; i < len; i++) {
292 if (get_user(c, buf + i))
293 return -EFAULT;
294
295 if (c == 'V')
296 wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
297 }
298 }
299
300 wdrtas_timer_keepalive();
301
302out:
303 return len;
304}
305
306
307
308
309
310
311
312
313
314
315
316
317static long wdrtas_ioctl(struct file *file, unsigned int cmd,
318 unsigned long arg)
319{
320 int __user *argp = (void __user *)arg;
321 int i;
322 static struct watchdog_info wdinfo = {
323 .options = WDRTAS_SUPPORTED_MASK,
324 .firmware_version = 0,
325 .identity = "wdrtas",
326 };
327
328 switch (cmd) {
329 case WDIOC_GETSUPPORT:
330 if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
331 return -EFAULT;
332 return 0;
333
334 case WDIOC_GETSTATUS:
335 i = wdrtas_get_status();
336 return put_user(i, argp);
337
338 case WDIOC_GETBOOTSTATUS:
339 i = wdrtas_get_boot_status();
340 return put_user(i, argp);
341
342 case WDIOC_GETTEMP:
343 if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
344 return -EOPNOTSUPP;
345
346 i = wdrtas_get_temperature();
347 return put_user(i, argp);
348
349 case WDIOC_SETOPTIONS:
350 if (get_user(i, argp))
351 return -EFAULT;
352 if (i & WDIOS_DISABLECARD)
353 wdrtas_timer_stop();
354 if (i & WDIOS_ENABLECARD) {
355 wdrtas_timer_keepalive();
356 wdrtas_timer_start();
357 }
358
359
360
361 return 0;
362
363 case WDIOC_KEEPALIVE:
364 wdrtas_timer_keepalive();
365 return 0;
366
367 case WDIOC_SETTIMEOUT:
368 if (get_user(i, argp))
369 return -EFAULT;
370
371 if (wdrtas_set_interval(i))
372 return -EINVAL;
373
374 wdrtas_timer_keepalive();
375
376 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
377 wdrtas_interval = i;
378 else
379 wdrtas_interval = wdrtas_get_interval(i);
380
381
382 case WDIOC_GETTIMEOUT:
383 return put_user(wdrtas_interval, argp);
384
385 default:
386 return -ENOTTY;
387 }
388}
389
390
391
392
393
394
395
396
397
398
399
400static int wdrtas_open(struct inode *inode, struct file *file)
401{
402
403 if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
404 atomic_dec(&wdrtas_miscdev_open);
405 return -EBUSY;
406 }
407
408 wdrtas_timer_start();
409 wdrtas_timer_keepalive();
410
411 return nonseekable_open(inode, file);
412}
413
414
415
416
417
418
419
420
421
422
423static int wdrtas_close(struct inode *inode, struct file *file)
424{
425
426 if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
427 wdrtas_timer_stop();
428 else {
429 printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog "
430 "not stopped.\n");
431 wdrtas_timer_keepalive();
432 }
433
434 wdrtas_expect_close = 0;
435 atomic_dec(&wdrtas_miscdev_open);
436 return 0;
437}
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
453 size_t count, loff_t *ppos)
454{
455 int temperature = 0;
456
457 temperature = wdrtas_get_temperature();
458 if (temperature < 0)
459 return temperature;
460
461 if (copy_to_user(buf, &temperature, 1))
462 return -EFAULT;
463
464 return 1;
465}
466
467
468
469
470
471
472
473
474
475
476static int wdrtas_temp_open(struct inode *inode, struct file *file)
477{
478 return nonseekable_open(inode, file);
479}
480
481
482
483
484
485
486
487
488
489
490static int wdrtas_temp_close(struct inode *inode, struct file *file)
491{
492 return 0;
493}
494
495
496
497
498
499
500
501
502
503
504
505static int wdrtas_reboot(struct notifier_block *this,
506 unsigned long code, void *ptr)
507{
508 if (code == SYS_DOWN || code == SYS_HALT)
509 wdrtas_timer_stop();
510
511 return NOTIFY_DONE;
512}
513
514
515
516static const struct file_operations wdrtas_fops = {
517 .owner = THIS_MODULE,
518 .llseek = no_llseek,
519 .write = wdrtas_write,
520 .unlocked_ioctl = wdrtas_ioctl,
521 .open = wdrtas_open,
522 .release = wdrtas_close,
523};
524
525static struct miscdevice wdrtas_miscdev = {
526 .minor = WATCHDOG_MINOR,
527 .name = "watchdog",
528 .fops = &wdrtas_fops,
529};
530
531static const struct file_operations wdrtas_temp_fops = {
532 .owner = THIS_MODULE,
533 .llseek = no_llseek,
534 .read = wdrtas_temp_read,
535 .open = wdrtas_temp_open,
536 .release = wdrtas_temp_close,
537};
538
539static struct miscdevice wdrtas_tempdev = {
540 .minor = TEMP_MINOR,
541 .name = "temperature",
542 .fops = &wdrtas_temp_fops,
543};
544
545static struct notifier_block wdrtas_notifier = {
546 .notifier_call = wdrtas_reboot,
547};
548
549
550
551
552
553
554
555
556
557
558static int wdrtas_get_tokens(void)
559{
560 wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
561 if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
562 printk(KERN_WARNING "wdrtas: couldn't get token for "
563 "get-sensor-state. Trying to continue without "
564 "temperature support.\n");
565 }
566
567 wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
568 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
569 printk(KERN_WARNING "wdrtas: couldn't get token for "
570 "ibm,get-system-parameter. Trying to continue with "
571 "a default timeout value of %i seconds.\n",
572 WDRTAS_DEFAULT_INTERVAL);
573 }
574
575 wdrtas_token_set_indicator = rtas_token("set-indicator");
576 if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
577 printk(KERN_ERR "wdrtas: couldn't get token for "
578 "set-indicator. Terminating watchdog code.\n");
579 return -EIO;
580 }
581
582 wdrtas_token_event_scan = rtas_token("event-scan");
583 if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
584 printk(KERN_ERR "wdrtas: couldn't get token for event-scan. "
585 "Terminating watchdog code.\n");
586 return -EIO;
587 }
588
589 return 0;
590}
591
592
593
594
595
596
597
598static void wdrtas_unregister_devs(void)
599{
600 misc_deregister(&wdrtas_miscdev);
601 if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
602 misc_deregister(&wdrtas_tempdev);
603}
604
605
606
607
608
609
610
611
612
613static int wdrtas_register_devs(void)
614{
615 int result;
616
617 result = misc_register(&wdrtas_miscdev);
618 if (result) {
619 printk(KERN_ERR "wdrtas: couldn't register watchdog misc "
620 "device. Terminating watchdog code.\n");
621 return result;
622 }
623
624 if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
625 result = misc_register(&wdrtas_tempdev);
626 if (result) {
627 printk(KERN_WARNING "wdrtas: couldn't register "
628 "watchdog temperature misc device. Continuing "
629 "without temperature support.\n");
630 wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
631 }
632 }
633
634 return 0;
635}
636
637
638
639
640
641
642
643
644static int __init wdrtas_init(void)
645{
646 if (wdrtas_get_tokens())
647 return -ENODEV;
648
649 if (wdrtas_register_devs())
650 return -ENODEV;
651
652 if (register_reboot_notifier(&wdrtas_notifier)) {
653 printk(KERN_ERR "wdrtas: could not register reboot notifier. "
654 "Terminating watchdog code.\n");
655 wdrtas_unregister_devs();
656 return -ENODEV;
657 }
658
659 if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
660 wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
661 else
662 wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
663
664 return 0;
665}
666
667
668
669
670
671
672static void __exit wdrtas_exit(void)
673{
674 if (!wdrtas_nowayout)
675 wdrtas_timer_stop();
676
677 wdrtas_unregister_devs();
678
679 unregister_reboot_notifier(&wdrtas_notifier);
680}
681
682module_init(wdrtas_init);
683module_exit(wdrtas_exit);
684