linux/drivers/watchdog/wdrtas.c
<<
>>
Prefs
   1/*
   2 * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as
   3 * RTAS calls are available
   4 */
   5
   6/*
   7 * RTAS watchdog driver
   8 *
   9 * (C) Copyright IBM Corp. 2005
  10 * device driver to exploit watchdog RTAS functions
  11 *
  12 * Authors : Utz Bacher <utz.bacher@de.ibm.com>
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation; either version 2, or (at your option)
  17 * any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  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/*** watchdog access functions */
  78
  79/**
  80 * wdrtas_set_interval - sets the watchdog interval
  81 * @interval: new interval
  82 *
  83 * returns 0 on success, <0 on failures
  84 *
  85 * wdrtas_set_interval sets the watchdog keepalive interval by calling the
  86 * RTAS function set-indicator (surveillance). The unit of interval is
  87 * seconds.
  88 */
  89
  90static int wdrtas_set_interval(int interval)
  91{
  92        long result;
  93        static int print_msg = 10;
  94
  95        /* rtas uses minutes */
  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/**
 110 * wdrtas_get_interval - returns the current watchdog interval
 111 * @fallback_value: value (in seconds) to use, if the RTAS call fails
 112 *
 113 * returns the interval
 114 *
 115 * wdrtas_get_interval returns the current watchdog keepalive interval
 116 * as reported by the RTAS function ibm,get-system-parameter. The unit
 117 * of the return value is seconds.
 118 */
 119static int wdrtas_get_interval(int fallback_value)
 120{
 121        long result;
 122        char value[4];
 123
 124        result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
 125                           WDRTAS_SP_SPI, (void *)__pa(&value), 4);
 126        if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
 127                printk(KERN_WARNING "wdrtas: could not get sp_spi watchdog "
 128                       "timeout (%li). Continuing\n", result);
 129                return fallback_value;
 130        }
 131
 132        /* rtas uses minutes */
 133        return ((int)value[2]) * 60;
 134}
 135
 136/**
 137 * wdrtas_timer_start - starts watchdog
 138 *
 139 * wdrtas_timer_start starts the watchdog by calling the RTAS function
 140 * set-interval (surveillance)
 141 */
 142static void wdrtas_timer_start(void)
 143{
 144        wdrtas_set_interval(wdrtas_interval);
 145}
 146
 147/**
 148 * wdrtas_timer_stop - stops watchdog
 149 *
 150 * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
 151 * set-interval (surveillance)
 152 */
 153static void wdrtas_timer_stop(void)
 154{
 155        wdrtas_set_interval(0);
 156}
 157
 158/**
 159 * wdrtas_log_scanned_event - logs an event we received during keepalive
 160 *
 161 * wdrtas_log_scanned_event prints a message to the log buffer dumping
 162 * the results of the last event-scan call
 163 */
 164static void wdrtas_log_scanned_event(void)
 165{
 166        int i;
 167
 168        for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
 169                printk(KERN_INFO "wdrtas: dumping event (line %i/%i), data = "
 170                       "%02x %02x %02x %02x  %02x %02x %02x %02x   "
 171                       "%02x %02x %02x %02x  %02x %02x %02x %02x\n",
 172                       (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
 173                       wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
 174                       wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
 175                       wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
 176                       wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
 177                       wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
 178                       wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
 179                       wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
 180                       wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
 181}
 182
 183/**
 184 * wdrtas_timer_keepalive - resets watchdog timer to keep system alive
 185 *
 186 * wdrtas_timer_keepalive restarts the watchdog timer by calling the
 187 * RTAS function event-scan and repeats these calls as long as there are
 188 * events available. All events will be dumped.
 189 */
 190static void wdrtas_timer_keepalive(void)
 191{
 192        long result;
 193
 194        do {
 195                result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
 196                                   RTAS_EVENT_SCAN_ALL_EVENTS, 0,
 197                                   (void *)__pa(wdrtas_logbuffer),
 198                                   WDRTAS_LOGBUFFER_LEN);
 199                if (result < 0)
 200                        printk(KERN_ERR "wdrtas: event-scan failed: %li\n",
 201                               result);
 202                if (result == 0)
 203                        wdrtas_log_scanned_event();
 204        } while (result == 0);
 205}
 206
 207/**
 208 * wdrtas_get_temperature - returns current temperature
 209 *
 210 * returns temperature or <0 on failures
 211 *
 212 * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
 213 * uses the RTAS call get-sensor-state, token 3 to do so
 214 */
 215static int wdrtas_get_temperature(void)
 216{
 217        long result;
 218        int temperature = 0;
 219
 220        result = rtas_call(wdrtas_token_get_sensor_state, 2, 2,
 221                           (void *)__pa(&temperature),
 222                           WDRTAS_THERMAL_SENSOR, 0);
 223
 224        if (result < 0)
 225                printk(KERN_WARNING "wdrtas: reading the thermal sensor "
 226                       "faild: %li\n", result);
 227        else
 228                temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
 229
 230        return temperature;
 231}
 232
 233/**
 234 * wdrtas_get_status - returns the status of the watchdog
 235 *
 236 * returns a bitmask of defines WDIOF_... as defined in
 237 * include/linux/watchdog.h
 238 */
 239static int wdrtas_get_status(void)
 240{
 241        return 0; /* TODO */
 242}
 243
 244/**
 245 * wdrtas_get_boot_status - returns the reason for the last boot
 246 *
 247 * returns a bitmask of defines WDIOF_... as defined in
 248 * include/linux/watchdog.h, indicating why the watchdog rebooted the system
 249 */
 250static int wdrtas_get_boot_status(void)
 251{
 252        return 0; /* TODO */
 253}
 254
 255/*** watchdog API and operations stuff */
 256
 257/* wdrtas_write - called when watchdog device is written to
 258 * @file: file structure
 259 * @buf: user buffer with data
 260 * @len: amount to data written
 261 * @ppos: position in file
 262 *
 263 * returns the number of successfully processed characters, which is always
 264 * the number of bytes passed to this function
 265 *
 266 * wdrtas_write processes all the data given to it and looks for the magic
 267 * character 'V'. This character allows the watchdog device to be closed
 268 * properly.
 269 */
 270static ssize_t wdrtas_write(struct file *file, const char __user *buf,
 271             size_t len, loff_t *ppos)
 272{
 273        int i;
 274        char c;
 275
 276        if (!len)
 277                goto out;
 278
 279        if (!wdrtas_nowayout) {
 280                wdrtas_expect_close = 0;
 281                /* look for 'V' */
 282                for (i = 0; i < len; i++) {
 283                        if (get_user(c, buf + i))
 284                                return -EFAULT;
 285                        /* allow to close device */
 286                        if (c == 'V')
 287                                wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
 288                }
 289        }
 290
 291        wdrtas_timer_keepalive();
 292
 293out:
 294        return len;
 295}
 296
 297/**
 298 * wdrtas_ioctl - ioctl function for the watchdog device
 299 * @file: file structure
 300 * @cmd: command for ioctl
 301 * @arg: argument pointer
 302 *
 303 * returns 0 on success, <0 on failure
 304 *
 305 * wdrtas_ioctl implements the watchdog API ioctls
 306 */
 307
 308static long wdrtas_ioctl(struct file *file, unsigned int cmd,
 309                                                        unsigned long arg)
 310{
 311        int __user *argp = (void __user *)arg;
 312        int i;
 313        static struct watchdog_info wdinfo = {
 314                .options = WDRTAS_SUPPORTED_MASK,
 315                .firmware_version = 0,
 316                .identity = "wdrtas",
 317        };
 318
 319        switch (cmd) {
 320        case WDIOC_GETSUPPORT:
 321                if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
 322                        return -EFAULT;
 323                return 0;
 324
 325        case WDIOC_GETSTATUS:
 326                i = wdrtas_get_status();
 327                return put_user(i, argp);
 328
 329        case WDIOC_GETBOOTSTATUS:
 330                i = wdrtas_get_boot_status();
 331                return put_user(i, argp);
 332
 333        case WDIOC_GETTEMP:
 334                if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
 335                        return -EOPNOTSUPP;
 336
 337                i = wdrtas_get_temperature();
 338                return put_user(i, argp);
 339
 340        case WDIOC_SETOPTIONS:
 341                if (get_user(i, argp))
 342                        return -EFAULT;
 343                if (i & WDIOS_DISABLECARD)
 344                        wdrtas_timer_stop();
 345                if (i & WDIOS_ENABLECARD) {
 346                        wdrtas_timer_keepalive();
 347                        wdrtas_timer_start();
 348                }
 349                /* not implemented. Done by H8
 350                if (i & WDIOS_TEMPPANIC) {
 351                } */
 352                return 0;
 353
 354        case WDIOC_KEEPALIVE:
 355                wdrtas_timer_keepalive();
 356                return 0;
 357
 358        case WDIOC_SETTIMEOUT:
 359                if (get_user(i, argp))
 360                        return -EFAULT;
 361
 362                if (wdrtas_set_interval(i))
 363                        return -EINVAL;
 364
 365                wdrtas_timer_keepalive();
 366
 367                if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 368                        wdrtas_interval = i;
 369                else
 370                        wdrtas_interval = wdrtas_get_interval(i);
 371                /* fallthrough */
 372
 373        case WDIOC_GETTIMEOUT:
 374                return put_user(wdrtas_interval, argp);
 375
 376        default:
 377                return -ENOTTY;
 378        }
 379}
 380
 381/**
 382 * wdrtas_open - open function of watchdog device
 383 * @inode: inode structure
 384 * @file: file structure
 385 *
 386 * returns 0 on success, -EBUSY if the file has been opened already, <0 on
 387 * other failures
 388 *
 389 * function called when watchdog device is opened
 390 */
 391static int wdrtas_open(struct inode *inode, struct file *file)
 392{
 393        /* only open once */
 394        if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
 395                atomic_dec(&wdrtas_miscdev_open);
 396                return -EBUSY;
 397        }
 398
 399        wdrtas_timer_start();
 400        wdrtas_timer_keepalive();
 401
 402        return nonseekable_open(inode, file);
 403}
 404
 405/**
 406 * wdrtas_close - close function of watchdog device
 407 * @inode: inode structure
 408 * @file: file structure
 409 *
 410 * returns 0 on success
 411 *
 412 * close function. Always succeeds
 413 */
 414static int wdrtas_close(struct inode *inode, struct file *file)
 415{
 416        /* only stop watchdog, if this was announced using 'V' before */
 417        if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
 418                wdrtas_timer_stop();
 419        else {
 420                printk(KERN_WARNING "wdrtas: got unexpected close. Watchdog "
 421                       "not stopped.\n");
 422                wdrtas_timer_keepalive();
 423        }
 424
 425        wdrtas_expect_close = 0;
 426        atomic_dec(&wdrtas_miscdev_open);
 427        return 0;
 428}
 429
 430/**
 431 * wdrtas_temp_read - gives back the temperature in fahrenheit
 432 * @file: file structure
 433 * @buf: user buffer
 434 * @count: number of bytes to be read
 435 * @ppos: position in file
 436 *
 437 * returns always 1 or -EFAULT in case of user space copy failures, <0 on
 438 * other failures
 439 *
 440 * wdrtas_temp_read gives the temperature to the users by copying this
 441 * value as one byte into the user space buffer. The unit is Fahrenheit...
 442 */
 443static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
 444                 size_t count, loff_t *ppos)
 445{
 446        int temperature = 0;
 447
 448        temperature = wdrtas_get_temperature();
 449        if (temperature < 0)
 450                return temperature;
 451
 452        if (copy_to_user(buf, &temperature, 1))
 453                return -EFAULT;
 454
 455        return 1;
 456}
 457
 458/**
 459 * wdrtas_temp_open - open function of temperature device
 460 * @inode: inode structure
 461 * @file: file structure
 462 *
 463 * returns 0 on success, <0 on failure
 464 *
 465 * function called when temperature device is opened
 466 */
 467static int wdrtas_temp_open(struct inode *inode, struct file *file)
 468{
 469        return nonseekable_open(inode, file);
 470}
 471
 472/**
 473 * wdrtas_temp_close - close function of temperature device
 474 * @inode: inode structure
 475 * @file: file structure
 476 *
 477 * returns 0 on success
 478 *
 479 * close function. Always succeeds
 480 */
 481static int wdrtas_temp_close(struct inode *inode, struct file *file)
 482{
 483        return 0;
 484}
 485
 486/**
 487 * wdrtas_reboot - reboot notifier function
 488 * @nb: notifier block structure
 489 * @code: reboot code
 490 * @ptr: unused
 491 *
 492 * returns NOTIFY_DONE
 493 *
 494 * wdrtas_reboot stops the watchdog in case of a reboot
 495 */
 496static int wdrtas_reboot(struct notifier_block *this,
 497                                        unsigned long code, void *ptr)
 498{
 499        if (code == SYS_DOWN || code == SYS_HALT)
 500                wdrtas_timer_stop();
 501
 502        return NOTIFY_DONE;
 503}
 504
 505/*** initialization stuff */
 506
 507static const struct file_operations wdrtas_fops = {
 508        .owner          = THIS_MODULE,
 509        .llseek         = no_llseek,
 510        .write          = wdrtas_write,
 511        .unlocked_ioctl = wdrtas_ioctl,
 512        .open           = wdrtas_open,
 513        .release        = wdrtas_close,
 514};
 515
 516static struct miscdevice wdrtas_miscdev = {
 517        .minor =        WATCHDOG_MINOR,
 518        .name =         "watchdog",
 519        .fops =         &wdrtas_fops,
 520};
 521
 522static const struct file_operations wdrtas_temp_fops = {
 523        .owner          = THIS_MODULE,
 524        .llseek         = no_llseek,
 525        .read           = wdrtas_temp_read,
 526        .open           = wdrtas_temp_open,
 527        .release        = wdrtas_temp_close,
 528};
 529
 530static struct miscdevice wdrtas_tempdev = {
 531        .minor =        TEMP_MINOR,
 532        .name =         "temperature",
 533        .fops =         &wdrtas_temp_fops,
 534};
 535
 536static struct notifier_block wdrtas_notifier = {
 537        .notifier_call =        wdrtas_reboot,
 538};
 539
 540/**
 541 * wdrtas_get_tokens - reads in RTAS tokens
 542 *
 543 * returns 0 on succes, <0 on failure
 544 *
 545 * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
 546 * this watchdog driver. It tolerates, if "get-sensor-state" and
 547 * "ibm,get-system-parameter" are not available.
 548 */
 549static int wdrtas_get_tokens(void)
 550{
 551        wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
 552        if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
 553                printk(KERN_WARNING "wdrtas: couldn't get token for "
 554                       "get-sensor-state. Trying to continue without "
 555                       "temperature support.\n");
 556        }
 557
 558        wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
 559        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
 560                printk(KERN_WARNING "wdrtas: couldn't get token for "
 561                       "ibm,get-system-parameter. Trying to continue with "
 562                       "a default timeout value of %i seconds.\n",
 563                       WDRTAS_DEFAULT_INTERVAL);
 564        }
 565
 566        wdrtas_token_set_indicator = rtas_token("set-indicator");
 567        if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
 568                printk(KERN_ERR "wdrtas: couldn't get token for "
 569                       "set-indicator. Terminating watchdog code.\n");
 570                return -EIO;
 571        }
 572
 573        wdrtas_token_event_scan = rtas_token("event-scan");
 574        if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
 575                printk(KERN_ERR "wdrtas: couldn't get token for event-scan. "
 576                       "Terminating watchdog code.\n");
 577                return -EIO;
 578        }
 579
 580        return 0;
 581}
 582
 583/**
 584 * wdrtas_unregister_devs - unregisters the misc dev handlers
 585 *
 586 * wdrtas_register_devs unregisters the watchdog and temperature watchdog
 587 * misc devs
 588 */
 589static void wdrtas_unregister_devs(void)
 590{
 591        misc_deregister(&wdrtas_miscdev);
 592        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
 593                misc_deregister(&wdrtas_tempdev);
 594}
 595
 596/**
 597 * wdrtas_register_devs - registers the misc dev handlers
 598 *
 599 * returns 0 on succes, <0 on failure
 600 *
 601 * wdrtas_register_devs registers the watchdog and temperature watchdog
 602 * misc devs
 603 */
 604static int wdrtas_register_devs(void)
 605{
 606        int result;
 607
 608        result = misc_register(&wdrtas_miscdev);
 609        if (result) {
 610                printk(KERN_ERR "wdrtas: couldn't register watchdog misc "
 611                       "device. Terminating watchdog code.\n");
 612                return result;
 613        }
 614
 615        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
 616                result = misc_register(&wdrtas_tempdev);
 617                if (result) {
 618                        printk(KERN_WARNING "wdrtas: couldn't register "
 619                               "watchdog temperature misc device. Continuing "
 620                               "without temperature support.\n");
 621                        wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
 622                }
 623        }
 624
 625        return 0;
 626}
 627
 628/**
 629 * wdrtas_init - init function of the watchdog driver
 630 *
 631 * returns 0 on succes, <0 on failure
 632 *
 633 * registers the file handlers and the reboot notifier
 634 */
 635static int __init wdrtas_init(void)
 636{
 637        if (wdrtas_get_tokens())
 638                return -ENODEV;
 639
 640        if (wdrtas_register_devs())
 641                return -ENODEV;
 642
 643        if (register_reboot_notifier(&wdrtas_notifier)) {
 644                printk(KERN_ERR "wdrtas: could not register reboot notifier. "
 645                       "Terminating watchdog code.\n");
 646                wdrtas_unregister_devs();
 647                return -ENODEV;
 648        }
 649
 650        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 651                wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
 652        else
 653                wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
 654
 655        return 0;
 656}
 657
 658/**
 659 * wdrtas_exit - exit function of the watchdog driver
 660 *
 661 * unregisters the file handlers and the reboot notifier
 662 */
 663static void __exit wdrtas_exit(void)
 664{
 665        if (!wdrtas_nowayout)
 666                wdrtas_timer_stop();
 667
 668        wdrtas_unregister_devs();
 669
 670        unregister_reboot_notifier(&wdrtas_notifier);
 671}
 672
 673module_init(wdrtas_init);
 674module_exit(wdrtas_exit);
 675