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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  30
  31#include <linux/fs.h>
  32#include <linux/init.h>
  33#include <linux/kernel.h>
  34#include <linux/miscdevice.h>
  35#include <linux/module.h>
  36#include <linux/notifier.h>
  37#include <linux/reboot.h>
  38#include <linux/types.h>
  39#include <linux/watchdog.h>
  40#include <linux/uaccess.h>
  41
  42#include <asm/rtas.h>
  43
  44#define WDRTAS_MAGIC_CHAR               42
  45#define WDRTAS_SUPPORTED_MASK           (WDIOF_SETTIMEOUT | \
  46                                         WDIOF_MAGICCLOSE)
  47
  48MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
  49MODULE_DESCRIPTION("RTAS watchdog driver");
  50MODULE_LICENSE("GPL");
  51
  52static bool 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                pr_err("setting the watchdog to %i timeout failed: %li\n",
  97                       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                pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n",
 132                        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_timer_keepalive - resets watchdog timer to keep system alive
 164 *
 165 * wdrtas_timer_keepalive restarts the watchdog timer by calling the
 166 * RTAS function event-scan and repeats these calls as long as there are
 167 * events available. All events will be dumped.
 168 */
 169static void wdrtas_timer_keepalive(void)
 170{
 171        long result;
 172
 173        do {
 174                result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
 175                                   RTAS_EVENT_SCAN_ALL_EVENTS, 0,
 176                                   (void *)__pa(wdrtas_logbuffer),
 177                                   WDRTAS_LOGBUFFER_LEN);
 178                if (result < 0)
 179                        pr_err("event-scan failed: %li\n", result);
 180                if (result == 0)
 181                        print_hex_dump(KERN_INFO, "dumping event, data: ",
 182                                DUMP_PREFIX_OFFSET, 16, 1,
 183                                wdrtas_logbuffer, WDRTAS_LOGBUFFER_LEN, false);
 184        } while (result == 0);
 185}
 186
 187/**
 188 * wdrtas_get_temperature - returns current temperature
 189 *
 190 * returns temperature or <0 on failures
 191 *
 192 * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
 193 * uses the RTAS call get-sensor-state, token 3 to do so
 194 */
 195static int wdrtas_get_temperature(void)
 196{
 197        int result;
 198        int temperature = 0;
 199
 200        result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
 201
 202        if (result < 0)
 203                pr_warn("reading the thermal sensor failed: %i\n", result);
 204        else
 205                temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
 206
 207        return temperature;
 208}
 209
 210/**
 211 * wdrtas_get_status - returns the status of the watchdog
 212 *
 213 * returns a bitmask of defines WDIOF_... as defined in
 214 * include/linux/watchdog.h
 215 */
 216static int wdrtas_get_status(void)
 217{
 218        return 0; /* TODO */
 219}
 220
 221/**
 222 * wdrtas_get_boot_status - returns the reason for the last boot
 223 *
 224 * returns a bitmask of defines WDIOF_... as defined in
 225 * include/linux/watchdog.h, indicating why the watchdog rebooted the system
 226 */
 227static int wdrtas_get_boot_status(void)
 228{
 229        return 0; /* TODO */
 230}
 231
 232/*** watchdog API and operations stuff */
 233
 234/* wdrtas_write - called when watchdog device is written to
 235 * @file: file structure
 236 * @buf: user buffer with data
 237 * @len: amount to data written
 238 * @ppos: position in file
 239 *
 240 * returns the number of successfully processed characters, which is always
 241 * the number of bytes passed to this function
 242 *
 243 * wdrtas_write processes all the data given to it and looks for the magic
 244 * character 'V'. This character allows the watchdog device to be closed
 245 * properly.
 246 */
 247static ssize_t wdrtas_write(struct file *file, const char __user *buf,
 248             size_t len, loff_t *ppos)
 249{
 250        int i;
 251        char c;
 252
 253        if (!len)
 254                goto out;
 255
 256        if (!wdrtas_nowayout) {
 257                wdrtas_expect_close = 0;
 258                /* look for 'V' */
 259                for (i = 0; i < len; i++) {
 260                        if (get_user(c, buf + i))
 261                                return -EFAULT;
 262                        /* allow to close device */
 263                        if (c == 'V')
 264                                wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
 265                }
 266        }
 267
 268        wdrtas_timer_keepalive();
 269
 270out:
 271        return len;
 272}
 273
 274/**
 275 * wdrtas_ioctl - ioctl function for the watchdog device
 276 * @file: file structure
 277 * @cmd: command for ioctl
 278 * @arg: argument pointer
 279 *
 280 * returns 0 on success, <0 on failure
 281 *
 282 * wdrtas_ioctl implements the watchdog API ioctls
 283 */
 284
 285static long wdrtas_ioctl(struct file *file, unsigned int cmd,
 286                                                        unsigned long arg)
 287{
 288        int __user *argp = (void __user *)arg;
 289        int i;
 290        static const struct watchdog_info wdinfo = {
 291                .options = WDRTAS_SUPPORTED_MASK,
 292                .firmware_version = 0,
 293                .identity = "wdrtas",
 294        };
 295
 296        switch (cmd) {
 297        case WDIOC_GETSUPPORT:
 298                if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
 299                        return -EFAULT;
 300                return 0;
 301
 302        case WDIOC_GETSTATUS:
 303                i = wdrtas_get_status();
 304                return put_user(i, argp);
 305
 306        case WDIOC_GETBOOTSTATUS:
 307                i = wdrtas_get_boot_status();
 308                return put_user(i, argp);
 309
 310        case WDIOC_GETTEMP:
 311                if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
 312                        return -EOPNOTSUPP;
 313
 314                i = wdrtas_get_temperature();
 315                return put_user(i, argp);
 316
 317        case WDIOC_SETOPTIONS:
 318                if (get_user(i, argp))
 319                        return -EFAULT;
 320                if (i & WDIOS_DISABLECARD)
 321                        wdrtas_timer_stop();
 322                if (i & WDIOS_ENABLECARD) {
 323                        wdrtas_timer_keepalive();
 324                        wdrtas_timer_start();
 325                }
 326                /* not implemented. Done by H8
 327                if (i & WDIOS_TEMPPANIC) {
 328                } */
 329                return 0;
 330
 331        case WDIOC_KEEPALIVE:
 332                wdrtas_timer_keepalive();
 333                return 0;
 334
 335        case WDIOC_SETTIMEOUT:
 336                if (get_user(i, argp))
 337                        return -EFAULT;
 338
 339                if (wdrtas_set_interval(i))
 340                        return -EINVAL;
 341
 342                wdrtas_timer_keepalive();
 343
 344                if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 345                        wdrtas_interval = i;
 346                else
 347                        wdrtas_interval = wdrtas_get_interval(i);
 348                /* fallthrough */
 349
 350        case WDIOC_GETTIMEOUT:
 351                return put_user(wdrtas_interval, argp);
 352
 353        default:
 354                return -ENOTTY;
 355        }
 356}
 357
 358/**
 359 * wdrtas_open - open function of watchdog device
 360 * @inode: inode structure
 361 * @file: file structure
 362 *
 363 * returns 0 on success, -EBUSY if the file has been opened already, <0 on
 364 * other failures
 365 *
 366 * function called when watchdog device is opened
 367 */
 368static int wdrtas_open(struct inode *inode, struct file *file)
 369{
 370        /* only open once */
 371        if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
 372                atomic_dec(&wdrtas_miscdev_open);
 373                return -EBUSY;
 374        }
 375
 376        wdrtas_timer_start();
 377        wdrtas_timer_keepalive();
 378
 379        return nonseekable_open(inode, file);
 380}
 381
 382/**
 383 * wdrtas_close - close function of watchdog device
 384 * @inode: inode structure
 385 * @file: file structure
 386 *
 387 * returns 0 on success
 388 *
 389 * close function. Always succeeds
 390 */
 391static int wdrtas_close(struct inode *inode, struct file *file)
 392{
 393        /* only stop watchdog, if this was announced using 'V' before */
 394        if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
 395                wdrtas_timer_stop();
 396        else {
 397                pr_warn("got unexpected close. Watchdog not stopped.\n");
 398                wdrtas_timer_keepalive();
 399        }
 400
 401        wdrtas_expect_close = 0;
 402        atomic_dec(&wdrtas_miscdev_open);
 403        return 0;
 404}
 405
 406/**
 407 * wdrtas_temp_read - gives back the temperature in fahrenheit
 408 * @file: file structure
 409 * @buf: user buffer
 410 * @count: number of bytes to be read
 411 * @ppos: position in file
 412 *
 413 * returns always 1 or -EFAULT in case of user space copy failures, <0 on
 414 * other failures
 415 *
 416 * wdrtas_temp_read gives the temperature to the users by copying this
 417 * value as one byte into the user space buffer. The unit is Fahrenheit...
 418 */
 419static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
 420                 size_t count, loff_t *ppos)
 421{
 422        int temperature = 0;
 423
 424        temperature = wdrtas_get_temperature();
 425        if (temperature < 0)
 426                return temperature;
 427
 428        if (copy_to_user(buf, &temperature, 1))
 429                return -EFAULT;
 430
 431        return 1;
 432}
 433
 434/**
 435 * wdrtas_temp_open - open function of temperature device
 436 * @inode: inode structure
 437 * @file: file structure
 438 *
 439 * returns 0 on success, <0 on failure
 440 *
 441 * function called when temperature device is opened
 442 */
 443static int wdrtas_temp_open(struct inode *inode, struct file *file)
 444{
 445        return nonseekable_open(inode, file);
 446}
 447
 448/**
 449 * wdrtas_temp_close - close function of temperature device
 450 * @inode: inode structure
 451 * @file: file structure
 452 *
 453 * returns 0 on success
 454 *
 455 * close function. Always succeeds
 456 */
 457static int wdrtas_temp_close(struct inode *inode, struct file *file)
 458{
 459        return 0;
 460}
 461
 462/**
 463 * wdrtas_reboot - reboot notifier function
 464 * @nb: notifier block structure
 465 * @code: reboot code
 466 * @ptr: unused
 467 *
 468 * returns NOTIFY_DONE
 469 *
 470 * wdrtas_reboot stops the watchdog in case of a reboot
 471 */
 472static int wdrtas_reboot(struct notifier_block *this,
 473                                        unsigned long code, void *ptr)
 474{
 475        if (code == SYS_DOWN || code == SYS_HALT)
 476                wdrtas_timer_stop();
 477
 478        return NOTIFY_DONE;
 479}
 480
 481/*** initialization stuff */
 482
 483static const struct file_operations wdrtas_fops = {
 484        .owner          = THIS_MODULE,
 485        .llseek         = no_llseek,
 486        .write          = wdrtas_write,
 487        .unlocked_ioctl = wdrtas_ioctl,
 488        .open           = wdrtas_open,
 489        .release        = wdrtas_close,
 490};
 491
 492static struct miscdevice wdrtas_miscdev = {
 493        .minor =        WATCHDOG_MINOR,
 494        .name =         "watchdog",
 495        .fops =         &wdrtas_fops,
 496};
 497
 498static const struct file_operations wdrtas_temp_fops = {
 499        .owner          = THIS_MODULE,
 500        .llseek         = no_llseek,
 501        .read           = wdrtas_temp_read,
 502        .open           = wdrtas_temp_open,
 503        .release        = wdrtas_temp_close,
 504};
 505
 506static struct miscdevice wdrtas_tempdev = {
 507        .minor =        TEMP_MINOR,
 508        .name =         "temperature",
 509        .fops =         &wdrtas_temp_fops,
 510};
 511
 512static struct notifier_block wdrtas_notifier = {
 513        .notifier_call =        wdrtas_reboot,
 514};
 515
 516/**
 517 * wdrtas_get_tokens - reads in RTAS tokens
 518 *
 519 * returns 0 on success, <0 on failure
 520 *
 521 * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
 522 * this watchdog driver. It tolerates, if "get-sensor-state" and
 523 * "ibm,get-system-parameter" are not available.
 524 */
 525static int wdrtas_get_tokens(void)
 526{
 527        wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
 528        if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
 529                pr_warn("couldn't get token for get-sensor-state. Trying to continue without temperature support.\n");
 530        }
 531
 532        wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
 533        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
 534                pr_warn("couldn't get token for ibm,get-system-parameter. Trying to continue with a default timeout value of %i seconds.\n",
 535                        WDRTAS_DEFAULT_INTERVAL);
 536        }
 537
 538        wdrtas_token_set_indicator = rtas_token("set-indicator");
 539        if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
 540                pr_err("couldn't get token for set-indicator. Terminating watchdog code.\n");
 541                return -EIO;
 542        }
 543
 544        wdrtas_token_event_scan = rtas_token("event-scan");
 545        if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
 546                pr_err("couldn't get token for event-scan. Terminating watchdog code.\n");
 547                return -EIO;
 548        }
 549
 550        return 0;
 551}
 552
 553/**
 554 * wdrtas_unregister_devs - unregisters the misc dev handlers
 555 *
 556 * wdrtas_register_devs unregisters the watchdog and temperature watchdog
 557 * misc devs
 558 */
 559static void wdrtas_unregister_devs(void)
 560{
 561        misc_deregister(&wdrtas_miscdev);
 562        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
 563                misc_deregister(&wdrtas_tempdev);
 564}
 565
 566/**
 567 * wdrtas_register_devs - registers the misc dev handlers
 568 *
 569 * returns 0 on success, <0 on failure
 570 *
 571 * wdrtas_register_devs registers the watchdog and temperature watchdog
 572 * misc devs
 573 */
 574static int wdrtas_register_devs(void)
 575{
 576        int result;
 577
 578        result = misc_register(&wdrtas_miscdev);
 579        if (result) {
 580                pr_err("couldn't register watchdog misc device. Terminating watchdog code.\n");
 581                return result;
 582        }
 583
 584        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
 585                result = misc_register(&wdrtas_tempdev);
 586                if (result) {
 587                        pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n");
 588                        wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
 589                }
 590        }
 591
 592        return 0;
 593}
 594
 595/**
 596 * wdrtas_init - init function of the watchdog driver
 597 *
 598 * returns 0 on success, <0 on failure
 599 *
 600 * registers the file handlers and the reboot notifier
 601 */
 602static int __init wdrtas_init(void)
 603{
 604        if (wdrtas_get_tokens())
 605                return -ENODEV;
 606
 607        if (wdrtas_register_devs())
 608                return -ENODEV;
 609
 610        if (register_reboot_notifier(&wdrtas_notifier)) {
 611                pr_err("could not register reboot notifier. Terminating watchdog code.\n");
 612                wdrtas_unregister_devs();
 613                return -ENODEV;
 614        }
 615
 616        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 617                wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
 618        else
 619                wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
 620
 621        return 0;
 622}
 623
 624/**
 625 * wdrtas_exit - exit function of the watchdog driver
 626 *
 627 * unregisters the file handlers and the reboot notifier
 628 */
 629static void __exit wdrtas_exit(void)
 630{
 631        if (!wdrtas_nowayout)
 632                wdrtas_timer_stop();
 633
 634        wdrtas_unregister_devs();
 635
 636        unregister_reboot_notifier(&wdrtas_notifier);
 637}
 638
 639module_init(wdrtas_init);
 640module_exit(wdrtas_exit);
 641