linux/drivers/watchdog/intel_scu_watchdog.c
<<
>>
Prefs
   1/*
   2 *      Intel_SCU 0.2:  An Intel SCU IOH Based Watchdog Device
   3 *                      for Intel part #(s):
   4 *                              - AF82MP20 PCH
   5 *
   6 *      Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
   7 *
   8 *      This program is free software; you can redistribute it and/or
   9 *      modify it under the terms of version 2 of the GNU General
  10 *      Public License as published by the Free Software Foundation.
  11 *
  12 *      This program is distributed in the hope that it will be
  13 *      useful, but WITHOUT ANY WARRANTY; without even the implied
  14 *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
  15 *      PURPOSE.  See the GNU General Public License for more details.
  16 *      You should have received a copy of the GNU General Public
  17 *      License along with this program; if not, write to the Free
  18 *      Software Foundation, Inc., 59 Temple Place - Suite 330,
  19 *      Boston, MA  02111-1307, USA.
  20 *      The full GNU General Public License is included in this
  21 *      distribution in the file called COPYING.
  22 *
  23 */
  24
  25#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  26
  27#include <linux/compiler.h>
  28#include <linux/module.h>
  29#include <linux/kernel.h>
  30#include <linux/moduleparam.h>
  31#include <linux/types.h>
  32#include <linux/miscdevice.h>
  33#include <linux/watchdog.h>
  34#include <linux/fs.h>
  35#include <linux/notifier.h>
  36#include <linux/reboot.h>
  37#include <linux/init.h>
  38#include <linux/jiffies.h>
  39#include <linux/uaccess.h>
  40#include <linux/slab.h>
  41#include <linux/io.h>
  42#include <linux/interrupt.h>
  43#include <linux/delay.h>
  44#include <linux/sched.h>
  45#include <linux/signal.h>
  46#include <linux/sfi.h>
  47#include <asm/irq.h>
  48#include <linux/atomic.h>
  49#include <asm/intel_scu_ipc.h>
  50#include <asm/apb_timer.h>
  51#include <asm/mrst.h>
  52
  53#include "intel_scu_watchdog.h"
  54
  55/* Bounds number of times we will retry loading time count */
  56/* This retry is a work around for a silicon bug.          */
  57#define MAX_RETRY 16
  58
  59#define IPC_SET_WATCHDOG_TIMER  0xF8
  60
  61static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
  62module_param(timer_margin, int, 0);
  63MODULE_PARM_DESC(timer_margin,
  64                "Watchdog timer margin"
  65                "Time between interrupt and resetting the system"
  66                "The range is from 1 to 160"
  67                "This is the time for all keep alives to arrive");
  68
  69static int timer_set = DEFAULT_TIME;
  70module_param(timer_set, int, 0);
  71MODULE_PARM_DESC(timer_set,
  72                "Default Watchdog timer setting"
  73                "Complete cycle time"
  74                "The range is from 1 to 170"
  75                "This is the time for all keep alives to arrive");
  76
  77/* After watchdog device is closed, check force_boot. If:
  78 * force_boot == 0, then force boot on next watchdog interrupt after close,
  79 * force_boot == 1, then force boot immediately when device is closed.
  80 */
  81static int force_boot;
  82module_param(force_boot, int, 0);
  83MODULE_PARM_DESC(force_boot,
  84                "A value of 1 means that the driver will reboot"
  85                "the system immediately if the /dev/watchdog device is closed"
  86                "A value of 0 means that when /dev/watchdog device is closed"
  87                "the watchdog timer will be refreshed for one more interval"
  88                "of length: timer_set. At the end of this interval, the"
  89                "watchdog timer will reset the system."
  90                );
  91
  92/* there is only one device in the system now; this can be made into
  93 * an array in the future if we have more than one device */
  94
  95static struct intel_scu_watchdog_dev watchdog_device;
  96
  97/* Forces restart, if force_reboot is set */
  98static void watchdog_fire(void)
  99{
 100        if (force_boot) {
 101                pr_crit("Initiating system reboot\n");
 102                emergency_restart();
 103                pr_crit("Reboot didn't ?????\n");
 104        }
 105
 106        else {
 107                pr_crit("Immediate Reboot Disabled\n");
 108                pr_crit("System will reset when watchdog timer times out!\n");
 109        }
 110}
 111
 112static int check_timer_margin(int new_margin)
 113{
 114        if ((new_margin < MIN_TIME_CYCLE) ||
 115            (new_margin > MAX_TIME - timer_set)) {
 116                pr_debug("value of new_margin %d is out of the range %d to %d\n",
 117                         new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
 118                return -EINVAL;
 119        }
 120        return 0;
 121}
 122
 123/*
 124 * IPC operations
 125 */
 126static int watchdog_set_ipc(int soft_threshold, int threshold)
 127{
 128        u32     *ipc_wbuf;
 129        u8       cbuf[16] = { '\0' };
 130        int      ipc_ret = 0;
 131
 132        ipc_wbuf = (u32 *)&cbuf;
 133        ipc_wbuf[0] = soft_threshold;
 134        ipc_wbuf[1] = threshold;
 135
 136        ipc_ret = intel_scu_ipc_command(
 137                        IPC_SET_WATCHDOG_TIMER,
 138                        0,
 139                        ipc_wbuf,
 140                        2,
 141                        NULL,
 142                        0);
 143
 144        if (ipc_ret != 0)
 145                pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
 146
 147        return ipc_ret;
 148};
 149
 150/*
 151 *      Intel_SCU operations
 152 */
 153
 154/* timer interrupt handler */
 155static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
 156{
 157        int int_status;
 158        int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
 159
 160        pr_debug("irq, int_status: %x\n", int_status);
 161
 162        if (int_status != 0)
 163                return IRQ_NONE;
 164
 165        /* has the timer been started? If not, then this is spurious */
 166        if (watchdog_device.timer_started == 0) {
 167                pr_debug("spurious interrupt received\n");
 168                return IRQ_HANDLED;
 169        }
 170
 171        /* temporarily disable the timer */
 172        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 173
 174        /* set the timer to the threshold */
 175        iowrite32(watchdog_device.threshold,
 176                  watchdog_device.timer_load_count_addr);
 177
 178        /* allow the timer to run */
 179        iowrite32(0x00000003, watchdog_device.timer_control_addr);
 180
 181        return IRQ_HANDLED;
 182}
 183
 184static int intel_scu_keepalive(void)
 185{
 186
 187        /* read eoi register - clears interrupt */
 188        ioread32(watchdog_device.timer_clear_interrupt_addr);
 189
 190        /* temporarily disable the timer */
 191        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 192
 193        /* set the timer to the soft_threshold */
 194        iowrite32(watchdog_device.soft_threshold,
 195                  watchdog_device.timer_load_count_addr);
 196
 197        /* allow the timer to run */
 198        iowrite32(0x00000003, watchdog_device.timer_control_addr);
 199
 200        return 0;
 201}
 202
 203static int intel_scu_stop(void)
 204{
 205        iowrite32(0, watchdog_device.timer_control_addr);
 206        return 0;
 207}
 208
 209static int intel_scu_set_heartbeat(u32 t)
 210{
 211        int                      ipc_ret;
 212        int                      retry_count;
 213        u32                      soft_value;
 214        u32                      hw_pre_value;
 215        u32                      hw_value;
 216
 217        watchdog_device.timer_set = t;
 218        watchdog_device.threshold =
 219                timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
 220        watchdog_device.soft_threshold =
 221                (watchdog_device.timer_set - timer_margin)
 222                * watchdog_device.timer_tbl_ptr->freq_hz;
 223
 224        pr_debug("set_heartbeat: timer freq is %d\n",
 225                 watchdog_device.timer_tbl_ptr->freq_hz);
 226        pr_debug("set_heartbeat: timer_set is %x (hex)\n",
 227                 watchdog_device.timer_set);
 228        pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin);
 229        pr_debug("set_heartbeat: threshold is %x (hex)\n",
 230                 watchdog_device.threshold);
 231        pr_debug("set_heartbeat: soft_threshold is %x (hex)\n",
 232                 watchdog_device.soft_threshold);
 233
 234        /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
 235        /* watchdog timing come out right. */
 236        watchdog_device.threshold =
 237                watchdog_device.threshold / FREQ_ADJUSTMENT;
 238        watchdog_device.soft_threshold =
 239                watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
 240
 241        /* temporarily disable the timer */
 242        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 243
 244        /* send the threshold and soft_threshold via IPC to the processor */
 245        ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
 246                                   watchdog_device.threshold);
 247
 248        if (ipc_ret != 0) {
 249                /* Make sure the watchdog timer is stopped */
 250                intel_scu_stop();
 251                return ipc_ret;
 252        }
 253
 254        /* Soft Threshold set loop. Early versions of silicon did */
 255        /* not always set this count correctly.  This loop checks */
 256        /* the value and retries if it was not set correctly.     */
 257
 258        retry_count = 0;
 259        soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
 260        do {
 261
 262                /* Make sure timer is stopped */
 263                intel_scu_stop();
 264
 265                if (MAX_RETRY < retry_count++) {
 266                        /* Unable to set timer value */
 267                        pr_err("Unable to set timer\n");
 268                        return -ENODEV;
 269                }
 270
 271                /* set the timer to the soft threshold */
 272                iowrite32(watchdog_device.soft_threshold,
 273                        watchdog_device.timer_load_count_addr);
 274
 275                /* read count value before starting timer */
 276                hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
 277                hw_pre_value = hw_pre_value & 0xFFFF0000;
 278
 279                /* Start the timer */
 280                iowrite32(0x00000003, watchdog_device.timer_control_addr);
 281
 282                /* read the value the time loaded into its count reg */
 283                hw_value = ioread32(watchdog_device.timer_load_count_addr);
 284                hw_value = hw_value & 0xFFFF0000;
 285
 286
 287        } while (soft_value != hw_value);
 288
 289        watchdog_device.timer_started = 1;
 290
 291        return 0;
 292}
 293
 294/*
 295 * /dev/watchdog handling
 296 */
 297
 298static int intel_scu_open(struct inode *inode, struct file *file)
 299{
 300
 301        /* Set flag to indicate that watchdog device is open */
 302        if (test_and_set_bit(0, &watchdog_device.driver_open))
 303                return -EBUSY;
 304
 305        /* Check for reopen of driver. Reopens are not allowed */
 306        if (watchdog_device.driver_closed)
 307                return -EPERM;
 308
 309        return nonseekable_open(inode, file);
 310}
 311
 312static int intel_scu_release(struct inode *inode, struct file *file)
 313{
 314        /*
 315         * This watchdog should not be closed, after the timer
 316         * is started with the WDIPC_SETTIMEOUT ioctl
 317         * If force_boot is set watchdog_fire() will cause an
 318         * immediate reset. If force_boot is not set, the watchdog
 319         * timer is refreshed for one more interval. At the end
 320         * of that interval, the watchdog timer will reset the system.
 321         */
 322
 323        if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
 324                pr_debug("intel_scu_release, without open\n");
 325                return -ENOTTY;
 326        }
 327
 328        if (!watchdog_device.timer_started) {
 329                /* Just close, since timer has not been started */
 330                pr_debug("closed, without starting timer\n");
 331                return 0;
 332        }
 333
 334        pr_crit("Unexpected close of /dev/watchdog!\n");
 335
 336        /* Since the timer was started, prevent future reopens */
 337        watchdog_device.driver_closed = 1;
 338
 339        /* Refresh the timer for one more interval */
 340        intel_scu_keepalive();
 341
 342        /* Reboot system (if force_boot is set) */
 343        watchdog_fire();
 344
 345        /* We should only reach this point if force_boot is not set */
 346        return 0;
 347}
 348
 349static ssize_t intel_scu_write(struct file *file,
 350                              char const *data,
 351                              size_t len,
 352                              loff_t *ppos)
 353{
 354
 355        if (watchdog_device.timer_started)
 356                /* Watchdog already started, keep it alive */
 357                intel_scu_keepalive();
 358        else
 359                /* Start watchdog with timer value set by init */
 360                intel_scu_set_heartbeat(watchdog_device.timer_set);
 361
 362        return len;
 363}
 364
 365static long intel_scu_ioctl(struct file *file,
 366                           unsigned int cmd,
 367                           unsigned long arg)
 368{
 369        void __user *argp = (void __user *)arg;
 370        u32 __user *p = argp;
 371        u32 new_margin;
 372
 373
 374        static const struct watchdog_info ident = {
 375                .options =          WDIOF_SETTIMEOUT
 376                                    | WDIOF_KEEPALIVEPING,
 377                .firmware_version = 0,  /* @todo Get from SCU via
 378                                                 ipc_get_scu_fw_version()? */
 379                .identity =         "Intel_SCU IOH Watchdog"  /* len < 32 */
 380        };
 381
 382        switch (cmd) {
 383        case WDIOC_GETSUPPORT:
 384                return copy_to_user(argp,
 385                                    &ident,
 386                                    sizeof(ident)) ? -EFAULT : 0;
 387        case WDIOC_GETSTATUS:
 388        case WDIOC_GETBOOTSTATUS:
 389                return put_user(0, p);
 390        case WDIOC_KEEPALIVE:
 391                intel_scu_keepalive();
 392
 393                return 0;
 394        case WDIOC_SETTIMEOUT:
 395                if (get_user(new_margin, p))
 396                        return -EFAULT;
 397
 398                if (check_timer_margin(new_margin))
 399                        return -EINVAL;
 400
 401                if (intel_scu_set_heartbeat(new_margin))
 402                        return -EINVAL;
 403                return 0;
 404        case WDIOC_GETTIMEOUT:
 405                return put_user(watchdog_device.soft_threshold, p);
 406
 407        default:
 408                return -ENOTTY;
 409        }
 410}
 411
 412/*
 413 *      Notifier for system down
 414 */
 415static int intel_scu_notify_sys(struct notifier_block *this,
 416                               unsigned long code,
 417                               void *another_unused)
 418{
 419        if (code == SYS_DOWN || code == SYS_HALT)
 420                /* Turn off the watchdog timer. */
 421                intel_scu_stop();
 422        return NOTIFY_DONE;
 423}
 424
 425/*
 426 *      Kernel Interfaces
 427 */
 428static const struct file_operations intel_scu_fops = {
 429        .owner          = THIS_MODULE,
 430        .llseek         = no_llseek,
 431        .write          = intel_scu_write,
 432        .unlocked_ioctl = intel_scu_ioctl,
 433        .open           = intel_scu_open,
 434        .release        = intel_scu_release,
 435};
 436
 437static int __init intel_scu_watchdog_init(void)
 438{
 439        int ret;
 440        u32 __iomem *tmp_addr;
 441
 442        /*
 443         * We don't really need to check this as the SFI timer get will fail
 444         * but if we do so we can exit with a clearer reason and no noise.
 445         *
 446         * If it isn't an intel MID device then it doesn't have this watchdog
 447         */
 448        if (!mrst_identify_cpu())
 449                return -ENODEV;
 450
 451        /* Check boot parameters to verify that their initial values */
 452        /* are in range. */
 453        /* Check value of timer_set boot parameter */
 454        if ((timer_set < MIN_TIME_CYCLE) ||
 455            (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
 456                pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n",
 457                       timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
 458                return -EINVAL;
 459        }
 460
 461        /* Check value of timer_margin boot parameter */
 462        if (check_timer_margin(timer_margin))
 463                return -EINVAL;
 464
 465        watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
 466
 467        if (watchdog_device.timer_tbl_ptr == NULL) {
 468                pr_debug("timer is not available\n");
 469                return -ENODEV;
 470        }
 471        /* make sure the timer exists */
 472        if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
 473                pr_debug("timer %d does not have valid physical memory\n",
 474                         sfi_mtimer_num);
 475                return -ENODEV;
 476        }
 477
 478        if (watchdog_device.timer_tbl_ptr->irq == 0) {
 479                pr_debug("timer %d invalid irq\n", sfi_mtimer_num);
 480                return -ENODEV;
 481        }
 482
 483        tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
 484                        20);
 485
 486        if (tmp_addr == NULL) {
 487                pr_debug("timer unable to ioremap\n");
 488                return -ENOMEM;
 489        }
 490
 491        watchdog_device.timer_load_count_addr = tmp_addr++;
 492        watchdog_device.timer_current_value_addr = tmp_addr++;
 493        watchdog_device.timer_control_addr = tmp_addr++;
 494        watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
 495        watchdog_device.timer_interrupt_status_addr = tmp_addr++;
 496
 497        /* Set the default time values in device structure */
 498
 499        watchdog_device.timer_set = timer_set;
 500        watchdog_device.threshold =
 501                timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
 502        watchdog_device.soft_threshold =
 503                (watchdog_device.timer_set - timer_margin)
 504                * watchdog_device.timer_tbl_ptr->freq_hz;
 505
 506
 507        watchdog_device.intel_scu_notifier.notifier_call =
 508                intel_scu_notify_sys;
 509
 510        ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
 511        if (ret) {
 512                pr_err("cannot register notifier %d)\n", ret);
 513                goto register_reboot_error;
 514        }
 515
 516        watchdog_device.miscdev.minor = WATCHDOG_MINOR;
 517        watchdog_device.miscdev.name = "watchdog";
 518        watchdog_device.miscdev.fops = &intel_scu_fops;
 519
 520        ret = misc_register(&watchdog_device.miscdev);
 521        if (ret) {
 522                pr_err("cannot register miscdev %d err =%d\n",
 523                       WATCHDOG_MINOR, ret);
 524                goto misc_register_error;
 525        }
 526
 527        ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
 528                watchdog_timer_interrupt,
 529                IRQF_SHARED, "watchdog",
 530                &watchdog_device.timer_load_count_addr);
 531        if (ret) {
 532                pr_err("error requesting irq %d\n", ret);
 533                goto request_irq_error;
 534        }
 535        /* Make sure timer is disabled before returning */
 536        intel_scu_stop();
 537        return 0;
 538
 539/* error cleanup */
 540
 541request_irq_error:
 542        misc_deregister(&watchdog_device.miscdev);
 543misc_register_error:
 544        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 545register_reboot_error:
 546        intel_scu_stop();
 547        iounmap(watchdog_device.timer_load_count_addr);
 548        return ret;
 549}
 550
 551static void __exit intel_scu_watchdog_exit(void)
 552{
 553
 554        misc_deregister(&watchdog_device.miscdev);
 555        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 556        /* disable the timer */
 557        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 558        iounmap(watchdog_device.timer_load_count_addr);
 559}
 560
 561late_initcall(intel_scu_watchdog_init);
 562module_exit(intel_scu_watchdog_exit);
 563
 564MODULE_AUTHOR("Intel Corporation");
 565MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
 566MODULE_LICENSE("GPL");
 567MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 568MODULE_VERSION(WDT_VER);
 569
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.