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/intel-mid.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_value;
 215
 216        watchdog_device.timer_set = t;
 217        watchdog_device.threshold =
 218                timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
 219        watchdog_device.soft_threshold =
 220                (watchdog_device.timer_set - timer_margin)
 221                * watchdog_device.timer_tbl_ptr->freq_hz;
 222
 223        pr_debug("set_heartbeat: timer freq is %d\n",
 224                 watchdog_device.timer_tbl_ptr->freq_hz);
 225        pr_debug("set_heartbeat: timer_set is %x (hex)\n",
 226                 watchdog_device.timer_set);
 227        pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin);
 228        pr_debug("set_heartbeat: threshold is %x (hex)\n",
 229                 watchdog_device.threshold);
 230        pr_debug("set_heartbeat: soft_threshold is %x (hex)\n",
 231                 watchdog_device.soft_threshold);
 232
 233        /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
 234        /* watchdog timing come out right. */
 235        watchdog_device.threshold =
 236                watchdog_device.threshold / FREQ_ADJUSTMENT;
 237        watchdog_device.soft_threshold =
 238                watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
 239
 240        /* temporarily disable the timer */
 241        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 242
 243        /* send the threshold and soft_threshold via IPC to the processor */
 244        ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
 245                                   watchdog_device.threshold);
 246
 247        if (ipc_ret != 0) {
 248                /* Make sure the watchdog timer is stopped */
 249                intel_scu_stop();
 250                return ipc_ret;
 251        }
 252
 253        /* Soft Threshold set loop. Early versions of silicon did */
 254        /* not always set this count correctly.  This loop checks */
 255        /* the value and retries if it was not set correctly.     */
 256
 257        retry_count = 0;
 258        soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
 259        do {
 260
 261                /* Make sure timer is stopped */
 262                intel_scu_stop();
 263
 264                if (MAX_RETRY < retry_count++) {
 265                        /* Unable to set timer value */
 266                        pr_err("Unable to set timer\n");
 267                        return -ENODEV;
 268                }
 269
 270                /* set the timer to the soft threshold */
 271                iowrite32(watchdog_device.soft_threshold,
 272                        watchdog_device.timer_load_count_addr);
 273
 274                /* read count value before starting timer */
 275                ioread32(watchdog_device.timer_load_count_addr);
 276
 277                /* Start the timer */
 278                iowrite32(0x00000003, watchdog_device.timer_control_addr);
 279
 280                /* read the value the time loaded into its count reg */
 281                hw_value = ioread32(watchdog_device.timer_load_count_addr);
 282                hw_value = hw_value & 0xFFFF0000;
 283
 284
 285        } while (soft_value != hw_value);
 286
 287        watchdog_device.timer_started = 1;
 288
 289        return 0;
 290}
 291
 292/*
 293 * /dev/watchdog handling
 294 */
 295
 296static int intel_scu_open(struct inode *inode, struct file *file)
 297{
 298
 299        /* Set flag to indicate that watchdog device is open */
 300        if (test_and_set_bit(0, &watchdog_device.driver_open))
 301                return -EBUSY;
 302
 303        /* Check for reopen of driver. Reopens are not allowed */
 304        if (watchdog_device.driver_closed)
 305                return -EPERM;
 306
 307        return nonseekable_open(inode, file);
 308}
 309
 310static int intel_scu_release(struct inode *inode, struct file *file)
 311{
 312        /*
 313         * This watchdog should not be closed, after the timer
 314         * is started with the WDIPC_SETTIMEOUT ioctl
 315         * If force_boot is set watchdog_fire() will cause an
 316         * immediate reset. If force_boot is not set, the watchdog
 317         * timer is refreshed for one more interval. At the end
 318         * of that interval, the watchdog timer will reset the system.
 319         */
 320
 321        if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
 322                pr_debug("intel_scu_release, without open\n");
 323                return -ENOTTY;
 324        }
 325
 326        if (!watchdog_device.timer_started) {
 327                /* Just close, since timer has not been started */
 328                pr_debug("closed, without starting timer\n");
 329                return 0;
 330        }
 331
 332        pr_crit("Unexpected close of /dev/watchdog!\n");
 333
 334        /* Since the timer was started, prevent future reopens */
 335        watchdog_device.driver_closed = 1;
 336
 337        /* Refresh the timer for one more interval */
 338        intel_scu_keepalive();
 339
 340        /* Reboot system (if force_boot is set) */
 341        watchdog_fire();
 342
 343        /* We should only reach this point if force_boot is not set */
 344        return 0;
 345}
 346
 347static ssize_t intel_scu_write(struct file *file,
 348                              char const *data,
 349                              size_t len,
 350                              loff_t *ppos)
 351{
 352
 353        if (watchdog_device.timer_started)
 354                /* Watchdog already started, keep it alive */
 355                intel_scu_keepalive();
 356        else
 357                /* Start watchdog with timer value set by init */
 358                intel_scu_set_heartbeat(watchdog_device.timer_set);
 359
 360        return len;
 361}
 362
 363static long intel_scu_ioctl(struct file *file,
 364                           unsigned int cmd,
 365                           unsigned long arg)
 366{
 367        void __user *argp = (void __user *)arg;
 368        u32 __user *p = argp;
 369        u32 new_margin;
 370
 371
 372        static const struct watchdog_info ident = {
 373                .options =          WDIOF_SETTIMEOUT
 374                                    | WDIOF_KEEPALIVEPING,
 375                .firmware_version = 0,  /* @todo Get from SCU via
 376                                                 ipc_get_scu_fw_version()? */
 377                .identity =         "Intel_SCU IOH Watchdog"  /* len < 32 */
 378        };
 379
 380        switch (cmd) {
 381        case WDIOC_GETSUPPORT:
 382                return copy_to_user(argp,
 383                                    &ident,
 384                                    sizeof(ident)) ? -EFAULT : 0;
 385        case WDIOC_GETSTATUS:
 386        case WDIOC_GETBOOTSTATUS:
 387                return put_user(0, p);
 388        case WDIOC_KEEPALIVE:
 389                intel_scu_keepalive();
 390
 391                return 0;
 392        case WDIOC_SETTIMEOUT:
 393                if (get_user(new_margin, p))
 394                        return -EFAULT;
 395
 396                if (check_timer_margin(new_margin))
 397                        return -EINVAL;
 398
 399                if (intel_scu_set_heartbeat(new_margin))
 400                        return -EINVAL;
 401                return 0;
 402        case WDIOC_GETTIMEOUT:
 403                return put_user(watchdog_device.soft_threshold, p);
 404
 405        default:
 406                return -ENOTTY;
 407        }
 408}
 409
 410/*
 411 *      Notifier for system down
 412 */
 413static int intel_scu_notify_sys(struct notifier_block *this,
 414                               unsigned long code,
 415                               void *another_unused)
 416{
 417        if (code == SYS_DOWN || code == SYS_HALT)
 418                /* Turn off the watchdog timer. */
 419                intel_scu_stop();
 420        return NOTIFY_DONE;
 421}
 422
 423/*
 424 *      Kernel Interfaces
 425 */
 426static const struct file_operations intel_scu_fops = {
 427        .owner          = THIS_MODULE,
 428        .llseek         = no_llseek,
 429        .write          = intel_scu_write,
 430        .unlocked_ioctl = intel_scu_ioctl,
 431        .open           = intel_scu_open,
 432        .release        = intel_scu_release,
 433};
 434
 435static int __init intel_scu_watchdog_init(void)
 436{
 437        int ret;
 438        u32 __iomem *tmp_addr;
 439
 440        /*
 441         * We don't really need to check this as the SFI timer get will fail
 442         * but if we do so we can exit with a clearer reason and no noise.
 443         *
 444         * If it isn't an intel MID device then it doesn't have this watchdog
 445         */
 446        if (!intel_mid_identify_cpu())
 447                return -ENODEV;
 448
 449        /* Check boot parameters to verify that their initial values */
 450        /* are in range. */
 451        /* Check value of timer_set boot parameter */
 452        if ((timer_set < MIN_TIME_CYCLE) ||
 453            (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
 454                pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n",
 455                       timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
 456                return -EINVAL;
 457        }
 458
 459        /* Check value of timer_margin boot parameter */
 460        if (check_timer_margin(timer_margin))
 461                return -EINVAL;
 462
 463        watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
 464
 465        if (watchdog_device.timer_tbl_ptr == NULL) {
 466                pr_debug("timer is not available\n");
 467                return -ENODEV;
 468        }
 469        /* make sure the timer exists */
 470        if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
 471                pr_debug("timer %d does not have valid physical memory\n",
 472                         sfi_mtimer_num);
 473                return -ENODEV;
 474        }
 475
 476        if (watchdog_device.timer_tbl_ptr->irq == 0) {
 477                pr_debug("timer %d invalid irq\n", sfi_mtimer_num);
 478                return -ENODEV;
 479        }
 480
 481        tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
 482                        20);
 483
 484        if (tmp_addr == NULL) {
 485                pr_debug("timer unable to ioremap\n");
 486                return -ENOMEM;
 487        }
 488
 489        watchdog_device.timer_load_count_addr = tmp_addr++;
 490        watchdog_device.timer_current_value_addr = tmp_addr++;
 491        watchdog_device.timer_control_addr = tmp_addr++;
 492        watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
 493        watchdog_device.timer_interrupt_status_addr = tmp_addr++;
 494
 495        /* Set the default time values in device structure */
 496
 497        watchdog_device.timer_set = timer_set;
 498        watchdog_device.threshold =
 499                timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
 500        watchdog_device.soft_threshold =
 501                (watchdog_device.timer_set - timer_margin)
 502                * watchdog_device.timer_tbl_ptr->freq_hz;
 503
 504
 505        watchdog_device.intel_scu_notifier.notifier_call =
 506                intel_scu_notify_sys;
 507
 508        ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
 509        if (ret) {
 510                pr_err("cannot register notifier %d)\n", ret);
 511                goto register_reboot_error;
 512        }
 513
 514        watchdog_device.miscdev.minor = WATCHDOG_MINOR;
 515        watchdog_device.miscdev.name = "watchdog";
 516        watchdog_device.miscdev.fops = &intel_scu_fops;
 517
 518        ret = misc_register(&watchdog_device.miscdev);
 519        if (ret) {
 520                pr_err("cannot register miscdev %d err =%d\n",
 521                       WATCHDOG_MINOR, ret);
 522                goto misc_register_error;
 523        }
 524
 525        ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
 526                watchdog_timer_interrupt,
 527                IRQF_SHARED, "watchdog",
 528                &watchdog_device.timer_load_count_addr);
 529        if (ret) {
 530                pr_err("error requesting irq %d\n", ret);
 531                goto request_irq_error;
 532        }
 533        /* Make sure timer is disabled before returning */
 534        intel_scu_stop();
 535        return 0;
 536
 537/* error cleanup */
 538
 539request_irq_error:
 540        misc_deregister(&watchdog_device.miscdev);
 541misc_register_error:
 542        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 543register_reboot_error:
 544        intel_scu_stop();
 545        iounmap(watchdog_device.timer_load_count_addr);
 546        return ret;
 547}
 548
 549static void __exit intel_scu_watchdog_exit(void)
 550{
 551
 552        misc_deregister(&watchdog_device.miscdev);
 553        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
 554        /* disable the timer */
 555        iowrite32(0x00000002, watchdog_device.timer_control_addr);
 556        iounmap(watchdog_device.timer_load_count_addr);
 557}
 558
 559late_initcall(intel_scu_watchdog_init);
 560module_exit(intel_scu_watchdog_exit);
 561
 562MODULE_AUTHOR("Intel Corporation");
 563MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
 564MODULE_LICENSE("GPL");
 565MODULE_VERSION(WDT_VER);
 566