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