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#define WDRTAS_SP_SPI_LEN 4
 110
 111/**
 112 * wdrtas_get_interval - returns the current watchdog interval
 113 * @fallback_value: value (in seconds) to use, if the RTAS call fails
 114 *
 115 * returns the interval
 116 *
 117 * wdrtas_get_interval returns the current watchdog keepalive interval
 118 * as reported by the RTAS function ibm,get-system-parameter. The unit
 119 * of the return value is seconds.
 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        /* rtas uses minutes */
 142        return ((int)value[2]) * 60;
 143}
 144
 145/**
 146 * wdrtas_timer_start - starts watchdog
 147 *
 148 * wdrtas_timer_start starts the watchdog by calling the RTAS function
 149 * set-interval (surveillance)
 150 */
 151static void wdrtas_timer_start(void)
 152{
 153        wdrtas_set_interval(wdrtas_interval);
 154}
 155
 156/**
 157 * wdrtas_timer_stop - stops watchdog
 158 *
 159 * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
 160 * set-interval (surveillance)
 161 */
 162static void wdrtas_timer_stop(void)
 163{
 164        wdrtas_set_interval(0);
 165}
 166
 167/**
 168 * wdrtas_log_scanned_event - logs an event we received during keepalive
 169 *
 170 * wdrtas_log_scanned_event prints a message to the log buffer dumping
 171 * the results of the last event-scan call
 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 * wdrtas_timer_keepalive - resets watchdog timer to keep system alive
 194 *
 195 * wdrtas_timer_keepalive restarts the watchdog timer by calling the
 196 * RTAS function event-scan and repeats these calls as long as there are
 197 * events available. All events will be dumped.
 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 * wdrtas_get_temperature - returns current temperature
 218 *
 219 * returns temperature or <0 on failures
 220 *
 221 * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
 222 * uses the RTAS call get-sensor-state, token 3 to do so
 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; /* fahrenheit */
 238
 239        return temperature;
 240}
 241
 242/**
 243 * wdrtas_get_status - returns the status of the watchdog
 244 *
 245 * returns a bitmask of defines WDIOF_... as defined in
 246 * include/linux/watchdog.h
 247 */
 248static int wdrtas_get_status(void)
 249{
 250        return 0; /* TODO */
 251}
 252
 253/**
 254 * wdrtas_get_boot_status - returns the reason for the last boot
 255 *
 256 * returns a bitmask of defines WDIOF_... as defined in
 257 * include/linux/watchdog.h, indicating why the watchdog rebooted the system
 258 */
 259static int wdrtas_get_boot_status(void)
 260{
 261        return 0; /* TODO */
 262}
 263
 264/*** watchdog API and operations stuff */
 265
 266/* wdrtas_write - called when watchdog device is written to
 267 * @file: file structure
 268 * @buf: user buffer with data
 269 * @len: amount to data written
 270 * @ppos: position in file
 271 *
 272 * returns the number of successfully processed characters, which is always
 273 * the number of bytes passed to this function
 274 *
 275 * wdrtas_write processes all the data given to it and looks for the magic
 276 * character 'V'. This character allows the watchdog device to be closed
 277 * properly.
 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                /* look for 'V' */
 291                for (i = 0; i < len; i++) {
 292                        if (get_user(c, buf + i))
 293                                return -EFAULT;
 294                        /* allow to close device */
 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 * wdrtas_ioctl - ioctl function for the watchdog device
 308 * @file: file structure
 309 * @cmd: command for ioctl
 310 * @arg: argument pointer
 311 *
 312 * returns 0 on success, <0 on failure
 313 *
 314 * wdrtas_ioctl implements the watchdog API ioctls
 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                /* not implemented. Done by H8
 359                if (i & WDIOS_TEMPPANIC) {
 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                /* fallthrough */
 381
 382        case WDIOC_GETTIMEOUT:
 383                return put_user(wdrtas_interval, argp);
 384
 385        default:
 386                return -ENOTTY;
 387        }
 388}
 389
 390/**
 391 * wdrtas_open - open function of watchdog device
 392 * @inode: inode structure
 393 * @file: file structure
 394 *
 395 * returns 0 on success, -EBUSY if the file has been opened already, <0 on
 396 * other failures
 397 *
 398 * function called when watchdog device is opened
 399 */
 400static int wdrtas_open(struct inode *inode, struct file *file)
 401{
 402        /* only open once */
 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 * wdrtas_close - close function of watchdog device
 416 * @inode: inode structure
 417 * @file: file structure
 418 *
 419 * returns 0 on success
 420 *
 421 * close function. Always succeeds
 422 */
 423static int wdrtas_close(struct inode *inode, struct file *file)
 424{
 425        /* only stop watchdog, if this was announced using 'V' before */
 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 * wdrtas_temp_read - gives back the temperature in fahrenheit
 441 * @file: file structure
 442 * @buf: user buffer
 443 * @count: number of bytes to be read
 444 * @ppos: position in file
 445 *
 446 * returns always 1 or -EFAULT in case of user space copy failures, <0 on
 447 * other failures
 448 *
 449 * wdrtas_temp_read gives the temperature to the users by copying this
 450 * value as one byte into the user space buffer. The unit is Fahrenheit...
 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 * wdrtas_temp_open - open function of temperature device
 469 * @inode: inode structure
 470 * @file: file structure
 471 *
 472 * returns 0 on success, <0 on failure
 473 *
 474 * function called when temperature device is opened
 475 */
 476static int wdrtas_temp_open(struct inode *inode, struct file *file)
 477{
 478        return nonseekable_open(inode, file);
 479}
 480
 481/**
 482 * wdrtas_temp_close - close function of temperature device
 483 * @inode: inode structure
 484 * @file: file structure
 485 *
 486 * returns 0 on success
 487 *
 488 * close function. Always succeeds
 489 */
 490static int wdrtas_temp_close(struct inode *inode, struct file *file)
 491{
 492        return 0;
 493}
 494
 495/**
 496 * wdrtas_reboot - reboot notifier function
 497 * @nb: notifier block structure
 498 * @code: reboot code
 499 * @ptr: unused
 500 *
 501 * returns NOTIFY_DONE
 502 *
 503 * wdrtas_reboot stops the watchdog in case of a reboot
 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/*** initialization stuff */
 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 * wdrtas_get_tokens - reads in RTAS tokens
 551 *
 552 * returns 0 on succes, <0 on failure
 553 *
 554 * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
 555 * this watchdog driver. It tolerates, if "get-sensor-state" and
 556 * "ibm,get-system-parameter" are not available.
 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 * wdrtas_unregister_devs - unregisters the misc dev handlers
 594 *
 595 * wdrtas_register_devs unregisters the watchdog and temperature watchdog
 596 * misc devs
 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 * wdrtas_register_devs - registers the misc dev handlers
 607 *
 608 * returns 0 on succes, <0 on failure
 609 *
 610 * wdrtas_register_devs registers the watchdog and temperature watchdog
 611 * misc devs
 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 * wdrtas_init - init function of the watchdog driver
 639 *
 640 * returns 0 on succes, <0 on failure
 641 *
 642 * registers the file handlers and the reboot notifier
 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 * wdrtas_exit - exit function of the watchdog driver
 669 *
 670 * unregisters the file handlers and the reboot notifier
 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