WATCHDOG: Enable intel_scu_watchdog driver, otherwise the firmware would
authorFei Yang <fei.yang@intel.com>
Tue, 25 Oct 2011 00:12:16 +0000 (17:12 -0700)
committermgross <mark.gross@intel.com>
Wed, 9 Nov 2011 21:16:02 +0000 (13:16 -0800)
reset the cpu approximately every 75s. This is a copy from UMG K35 kernel
tree, up to the following commit

commit 8ec47127745d548a591db2308f58a5ceeb4599c9
Author: mgross <mark.gross@intel.com>
Date:   Tue Oct 4 12:24:20 2011 -0700

    watchdog: detect spining in kernel and treat them as softlock ups

    BZ: 10041

    Updated watch dog to detect soft lock ups and provide WARN data to help
    debug soft lock ups in drivers that don't wedge the system so much that
    the watch dog stops getting kicked.

    The works by computing the amount of system time between the SCU warning
    interupt and the SOFT_LOCK_TIMEOUT which is 10 seconds.  If the system
    time on any cpu is within 10ms of the duration of the sample time (about
    10 sec in jiffies) then we assume that CPU has a driver wedge and needs
    to be reset.  It calls panic to get the trace data logged to the emmc
    panic area.

    Change-Id: I2b4a5bff99bae86f7431f28d161b57973774ea0a
    Reviewed-on: http://android.intel.com:8080/20482
Reviewed-by: Allyn, Mark A <mark.a.allyn@intel.com>
Tested-by: Allyn, Mark A <mark.a.allyn@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Reviewed-by: Tang, Richard <richard.tang@intel.com>
Reviewed-by: Mangalampalli, Jayant <jayant.mangalampalli@intel.com>
Change-Id: Ib91d869741681ffb0190a2b57056192705b03687
Signed-off-by: Fei Yang <fei.yang@intel.com>
drivers/watchdog/Kconfig
drivers/watchdog/intel_scu_watchdog.c
drivers/watchdog/intel_scu_watchdog.h

index 21d816e..d7f0917 100644 (file)
@@ -535,7 +535,7 @@ config I6300ESB_WDT
 
 config INTEL_SCU_WATCHDOG
        bool "Intel SCU Watchdog for Mobile Platforms"
-       depends on X86_MRST
+       depends on X86_MRST || X86_MDFLD
        ---help---
          Hardware driver for the watchdog time built into the Intel SCU
          for Intel Mobile Platforms.
index ba43860..147e286 100644 (file)
@@ -22,6 +22,8 @@
  *
  */
 
+#define DEBUG  1
+
 #include <linux/compiler.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/reboot.h>
 #include <linux/init.h>
 #include <linux/jiffies.h>
+#include <linux/poll.h>
+#include <linux/wait.h>
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
-#include <linux/delay.h>
 #include <linux/sched.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
 #include <linux/signal.h>
 #include <linux/sfi.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/wakelock.h>
 #include <asm/irq.h>
 #include <asm/atomic.h>
+
+/* See arch/x86/kernel/ipc_mrst.c */
 #include <asm/intel_scu_ipc.h>
 #include <asm/apb_timer.h>
-#include <asm/mrst.h>
 
 #include "intel_scu_watchdog.h"
 
 
 #define IPC_SET_WATCHDOG_TIMER 0xF8
 
+static DECLARE_WAIT_QUEUE_HEAD(read_wq);
+
+/* The read function (intel_scu_read) waits for the warning_flag to */
+/* be set by the watchdog interrupt handler. */
+/* When warning_flag is set intel_scu_read wakes up the user level */
+/* process, which is responsible for refreshing the watchdog timer */
+static int warning_flag;
+
+static bool disable_kernel_watchdog;
+
+/*
+ * heartbeats: cpu last kstat.system times
+ * beattime : jeffies at the sample time of heartbeats.
+ * SOFT_LOCK_TIME : some time out in sec after warning interrupt.
+ * dump_softloc_debug : called on SOFT_LOCK_TIME time out after scu
+ *                     interrupt to log data to logbuffer and emmc-panic code,
+ *                     SOFT_LOCK_TIME needs to be < SCU warn to reset time
+ *                     which is currently thats 15 sec.
+ *
+ * The soft lock works be taking a snapshot of kstat_cpu(i).cpustat.system at
+ * the time of the warning interrupt for each cpu.  Then at SOFT_LOCK_TIME the
+ * amount of time spend in system is computed and if its within 10 ms of the
+ * total SOFT_LOCK_TIME on any cpu it will dump the stack on that cpu and then
+ * calls panic.
+ *
+ */
+static cputime64_t heartbeats[NR_CPUS];
+static cputime64_t beattime;
+#define SOFT_LOCK_TIME 10
+static void dump_softlock_debug(unsigned long data);
+DEFINE_TIMER(softlock_timer, dump_softlock_debug, 0, 0);
+
+/**
+ * Please note that we are using a config CONFIG_DISABLE_SCU_WATCHDOG
+ * because this boot parameter should only be settable in a development
+ * environment and that customer devices should not have this capability
+ */
+#if defined(CONFIG_DISABLE_SCU_WATCHDOG)
+module_param(disable_kernel_watchdog, bool, S_IRUGO);
+MODULE_PARM_DESC(disable_kernel_watchdog,
+               "Disable kernel watchdog"
+               "Set to 0, watchdog started at boot"
+               "and left running; Set to 1; watchdog"
+               "is not started until user space"
+               "watchdog daemon is started; also if the"
+               "timer is started by the iafw firmware, it"
+               "will be disabled upon initialization of this"
+               "driver if disable_kernel_watchdog is set");
+#endif
+
 static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
-module_param(timer_margin, int, 0);
+module_param(timer_margin, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(timer_margin,
                "Watchdog timer margin"
                "Time between interrupt and resetting the system"
@@ -65,23 +124,23 @@ MODULE_PARM_DESC(timer_margin,
                "This is the time for all keep alives to arrive");
 
 static int timer_set = DEFAULT_TIME;
-module_param(timer_set, int, 0);
+module_param(timer_set, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(timer_set,
                "Default Watchdog timer setting"
                "Complete cycle time"
                "The range is from 1 to 170"
                "This is the time for all keep alives to arrive");
 
-/* After watchdog device is closed, check force_boot. If:
- * force_boot == 0, then force boot on next watchdog interrupt after close,
- * force_boot == 1, then force boot immediately when device is closed.
+/* After watchdog device is closed, check force_reset. If:
+ * force_reset is false, then force boot after time expires after close,
+ * force_reset is true, then force boot immediately when device is closed.
  */
-static int force_boot;
-module_param(force_boot, int, 0);
-MODULE_PARM_DESC(force_boot,
-               "A value of 1 means that the driver will reboot"
+static bool force_reset = true;
+module_param(force_reset, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(force_reset,
+               "A true means that the driver will reboot"
                "the system immediately if the /dev/watchdog device is closed"
-               "A value of 0 means that when /dev/watchdog device is closed"
+               "A false means that when /dev/watchdog device is closed"
                "the watchdog timer will be refreshed for one more interval"
                "of length: timer_set. At the end of this interval, the"
                "watchdog timer will reset the system."
@@ -92,10 +151,12 @@ MODULE_PARM_DESC(force_boot,
 
 static struct intel_scu_watchdog_dev watchdog_device;
 
+static struct wake_lock watchdog_wake_lock;
+
 /* Forces restart, if force_reboot is set */
 static void watchdog_fire(void)
 {
-       if (force_boot) {
+       if (force_reset) {
                printk(KERN_CRIT PFX "Initiating system reboot.\n");
                emergency_restart();
                printk(KERN_CRIT PFX "Reboot didn't ?????\n");
@@ -112,13 +173,53 @@ static int check_timer_margin(int new_margin)
 {
        if ((new_margin < MIN_TIME_CYCLE) ||
            (new_margin > MAX_TIME - timer_set)) {
-               pr_debug("Watchdog timer: value of new_margin %d is out of the range %d to %d\n",
+               pr_debug("Watchdog timer: Value of new_margin %d is "
+                         "out of the range %d to %d\n",
                          new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
-               return -EINVAL;
+                       return -EINVAL;
        }
        return 0;
 }
 
+/* time is about to run out and the scu will reset soon.  quickly
+ * dump debug data to logbuffer and emmc via calling panic before lights
+ * go out.
+ */
+static void smp_dumpstack(void *info)
+{
+       dump_stack();
+}
+
+static void dump_softlock_debug(unsigned long data)
+{
+       int i, reboot;
+       cputime64_t system[NR_CPUS], num_jifs;
+
+       num_jifs = jiffies - beattime;
+       for_each_possible_cpu(i) {
+               system[i] = cputime64_sub(kstat_cpu(i).cpustat.system,
+                               heartbeats[i]);
+       }
+
+       reboot = 0;
+
+       for_each_possible_cpu(i) {
+               if ((num_jifs - cputime_to_jiffies(system[i])) <
+                                               msecs_to_jiffies(10)) {
+                       WARN(1, "cpu %d wedged\n", i);
+                       smp_call_function_single(i, smp_dumpstack, NULL, 1);
+                       reboot = 1;
+               }
+       }
+
+       if (reboot) {
+               panic_timeout = 10;
+               panic("Soft lock on CPUs\n");
+       }
+
+}
+
+
 /*
  * IPC operations
  */
@@ -141,7 +242,8 @@ static int watchdog_set_ipc(int soft_threshold, int threshold)
                        0);
 
        if (ipc_ret != 0)
-               pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
+               printk(KERN_CRIT PFX "Error Setting SCU Watchdog Timer: %x\n",
+                       ipc_ret);
 
        return ipc_ret;
 };
@@ -150,39 +252,13 @@ static int watchdog_set_ipc(int soft_threshold, int threshold)
  *      Intel_SCU operations
  */
 
-/* timer interrupt handler */
-static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
-{
-       int int_status;
-       int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
-
-       pr_debug("Watchdog timer: irq, int_status: %x\n", int_status);
-
-       if (int_status != 0)
-               return IRQ_NONE;
-
-       /* has the timer been started? If not, then this is spurious */
-       if (watchdog_device.timer_started == 0) {
-               pr_debug("Watchdog timer: spurious interrupt received\n");
-               return IRQ_HANDLED;
-       }
-
-       /* temporarily disable the timer */
-       iowrite32(0x00000002, watchdog_device.timer_control_addr);
-
-       /* set the timer to the threshold */
-       iowrite32(watchdog_device.threshold,
-                 watchdog_device.timer_load_count_addr);
-
-       /* allow the timer to run */
-       iowrite32(0x00000003, watchdog_device.timer_control_addr);
-
-       return IRQ_HANDLED;
-}
 
 static int intel_scu_keepalive(void)
 {
 
+       pr_debug("Watchdog timer: keepalive: soft_threshold %x\n",
+               watchdog_device.soft_threshold);
+
        /* read eoi register - clears interrupt */
        ioread32(watchdog_device.timer_clear_interrupt_addr);
 
@@ -205,13 +281,70 @@ static int intel_scu_stop(void)
        return 0;
 }
 
+/* tasklet function for interrupt; keep interupt itself simple */
+static void watchdog_interrupt_tasklet_body(unsigned long data)
+{
+       int i, int_status;
+
+       pr_debug("Watchdog: interrupt tasklet body start\n");
+
+       if (!disable_kernel_watchdog) {
+               pr_debug("Watchdog: interrupt tasklet body disable clear\n");
+               if (!test_bit(0, &watchdog_device.driver_open)) {
+                       /* if not yet open; keep alive here in kernel */
+                       int_status = intel_scu_keepalive();
+                       if (int_status) {
+                               pr_debug("Watchdog timer: set do keepalive"
+                                       " from interrupt\n");
+                       }
+               return;
+               }
+       } else {
+               pr_debug("Watchdog: interrupt tasklet body disable set\n");
+               /* disable the timer */
+               iowrite32(0x00000002, watchdog_device.timer_control_addr);
+               intel_scu_stop();
+               return;
+       }
+
+       /* wake up read to send data to user (reminder for keep alive */
+       warning_flag = 1;
+
+       /*start timer for softlock detection */
+       beattime = jiffies;
+       for_each_possible_cpu(i) {
+               heartbeats[i] = kstat_cpu(i).cpustat.system;
+       }
+       mod_timer(&softlock_timer, jiffies + SOFT_LOCK_TIME * HZ);
+
+       /* Wake up the daemon */
+       wake_up_interruptible(&read_wq);
+
+       /*
+        * Hold a timeout wakelock so user space watchdogd has a chance
+        * to run after waking up from s3
+        */
+       wake_lock_timeout(&watchdog_wake_lock, 5 * HZ);
+}
+
+/* timer interrupt handler */
+static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
+{
+
+       /* has the timer been started? If not, then this is spurious */
+       if (watchdog_device.timer_started == 0) {
+               pr_debug("Watchdog timer: spurious interrupt received\n");
+               return IRQ_HANDLED;
+       }
+
+       tasklet_schedule(&watchdog_device.interrupt_tasklet);
+
+       return IRQ_HANDLED;
+}
+
 static int intel_scu_set_heartbeat(u32 t)
 {
        int                      ipc_ret;
-       int                      retry_count;
-       u32                      soft_value;
-       u32                      hw_pre_value;
-       u32                      hw_value;
 
        watchdog_device.timer_set = t;
        watchdog_device.threshold =
@@ -231,12 +364,6 @@ static int intel_scu_set_heartbeat(u32 t)
        pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
                watchdog_device.soft_threshold);
 
-       /* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
-       /* watchdog timing come out right. */
-       watchdog_device.threshold =
-               watchdog_device.threshold / FREQ_ADJUSTMENT;
-       watchdog_device.soft_threshold =
-               watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
 
        /* temporarily disable the timer */
        iowrite32(0x00000002, watchdog_device.timer_control_addr);
@@ -251,40 +378,15 @@ static int intel_scu_set_heartbeat(u32 t)
                return ipc_ret;
        }
 
-       /* Soft Threshold set loop. Early versions of silicon did */
-       /* not always set this count correctly.  This loop checks */
-       /* the value and retries if it was not set correctly.     */
-
-       retry_count = 0;
-       soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
-       do {
-
-               /* Make sure timer is stopped */
-               intel_scu_stop();
-
-               if (MAX_RETRY < retry_count++) {
-                       /* Unable to set timer value */
-                       pr_err("Watchdog timer: Unable to set timer\n");
-                       return -ENODEV;
-               }
-
-               /* set the timer to the soft threshold */
-               iowrite32(watchdog_device.soft_threshold,
-                       watchdog_device.timer_load_count_addr);
-
-               /* read count value before starting timer */
-               hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
-               hw_pre_value = hw_pre_value & 0xFFFF0000;
-
-               /* Start the timer */
-               iowrite32(0x00000003, watchdog_device.timer_control_addr);
-
-               /* read the value the time loaded into its count reg */
-               hw_value = ioread32(watchdog_device.timer_load_count_addr);
-               hw_value = hw_value & 0xFFFF0000;
+       /* Make sure timer is stopped */
+       intel_scu_stop();
 
+       /* set the timer to the soft threshold */
+       iowrite32(watchdog_device.soft_threshold,
+               watchdog_device.timer_load_count_addr);
 
-       } while (soft_value != hw_value);
+       /* Start the timer */
+       iowrite32(0x00000003, watchdog_device.timer_control_addr);
 
        watchdog_device.timer_started = 1;
 
@@ -314,20 +416,20 @@ static int intel_scu_release(struct inode *inode, struct file *file)
        /*
         * This watchdog should not be closed, after the timer
         * is started with the WDIPC_SETTIMEOUT ioctl
-        * If force_boot is set watchdog_fire() will cause an
-        * immediate reset. If force_boot is not set, the watchdog
+        * If force_reset is set watchdog_fire() will cause an
+        * immediate reset. If force_reset is not set, the watchdog
         * timer is refreshed for one more interval. At the end
         * of that interval, the watchdog timer will reset the system.
         */
 
-       if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
+       if (!test_bit(0, &watchdog_device.driver_open)) {
                pr_debug("Watchdog timer: intel_scu_release, without open\n");
                return -ENOTTY;
        }
 
        if (!watchdog_device.timer_started) {
                /* Just close, since timer has not been started */
-               pr_debug("Watchdog timer: closed, without starting timer\n");
+               pr_debug("Watchdog timer: Closed, without starting timer\n");
                return 0;
        }
 
@@ -340,10 +442,10 @@ static int intel_scu_release(struct inode *inode, struct file *file)
        /* Refresh the timer for one more interval */
        intel_scu_keepalive();
 
-       /* Reboot system (if force_boot is set) */
+       /* Reboot system (if force_reset is set) */
        watchdog_fire();
 
-       /* We should only reach this point if force_boot is not set */
+       /* We should only reach this point if force_reset is not set */
        return 0;
 }
 
@@ -353,16 +455,63 @@ static ssize_t intel_scu_write(struct file *file,
                              loff_t *ppos)
 {
 
-       if (watchdog_device.timer_started)
+       if (watchdog_device.timer_started) {
                /* Watchdog already started, keep it alive */
                intel_scu_keepalive();
-       else
+               wake_unlock(&watchdog_wake_lock);
+       } else
                /* Start watchdog with timer value set by init */
                intel_scu_set_heartbeat(watchdog_device.timer_set);
 
        return len;
 }
 
+static ssize_t intel_scu_read(struct file *file,
+                            char __user *user_data,
+                            size_t len,
+                            loff_t *user_ppos)
+{
+       int result;
+       u8 buf = 0;
+
+       /* we wait for the next interrupt; if more than one */
+       /* interrupt has occurred since the last read, we */
+       /* dont care. The data is not critical. We will do */
+       /* a copy to user each time we get and interrupt */
+       /* It is up to the Watchdog daemon to be ready to */
+       /* do the read (which signifies that the driver is */
+       /* awaiting a keep alive and that a limited time */
+       /* is available for the keep alive before the system */
+       /* is rebooted by the timer */
+       /* if (wait_event_interruptible(read_wq, warning_flag != 0))
+               return -ERESTARTSYS; */
+
+       warning_flag = 0;
+
+       /* Please note that the content of the data is irrelevent */
+       /* All that matters is that the read is available to the user */
+       result = copy_to_user(user_data, (void *)&buf, 1);
+
+       if (result != 0)
+               return -EFAULT;
+       else
+               return 1;
+
+}
+
+static unsigned int intel_scu_poll(struct file *file, poll_table *wait)
+{
+       unsigned int mask = 0;
+       poll_wait(file, &read_wq, wait);
+
+       if (warning_flag == 1)
+               mask |= POLLIN | POLLRDNORM;
+       else
+               mask = 0;
+
+       return mask;
+}
+
 static long intel_scu_ioctl(struct file *file,
                           unsigned int cmd,
                           unsigned long arg)
@@ -430,8 +579,10 @@ static const struct file_operations intel_scu_fops = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
        .write          = intel_scu_write,
+       .read           = intel_scu_read,
        .unlocked_ioctl = intel_scu_ioctl,
        .open           = intel_scu_open,
+       .poll           = intel_scu_poll,
        .release        = intel_scu_release,
 };
 
@@ -440,21 +591,13 @@ static int __init intel_scu_watchdog_init(void)
        int ret;
        u32 __iomem *tmp_addr;
 
-       /*
-        * We don't really need to check this as the SFI timer get will fail
-        * but if we do so we can exit with a clearer reason and no noise.
-        *
-        * If it isn't an intel MID device then it doesn't have this watchdog
-        */
-       if (!mrst_identify_cpu())
-               return -ENODEV;
 
        /* Check boot parameters to verify that their initial values */
        /* are in range. */
        /* Check value of timer_set boot parameter */
        if ((timer_set < MIN_TIME_CYCLE) ||
            (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
-               pr_err("Watchdog timer: value of timer_set %x (hex) "
+               pr_err("Watchdog timer: Value of timer_set %x (hex) "
                  "is out of range from %x to %x (hex)\n",
                  timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
                return -EINVAL;
@@ -467,19 +610,20 @@ static int __init intel_scu_watchdog_init(void)
        watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
 
        if (watchdog_device.timer_tbl_ptr == NULL) {
-               pr_debug("Watchdog timer - Intel SCU watchdog: timer is not available\n");
+               pr_debug("Watchdog timer - Intel SCU watchdog: Timer is"
+                       " not available\n");
                return -ENODEV;
        }
        /* make sure the timer exists */
        if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
-               pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does not have valid physical memory\n",
-                                                               sfi_mtimer_num);
+               pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does"
+                 " not have valid physical memory\n", sfi_mtimer_num);
                return -ENODEV;
        }
 
        if (watchdog_device.timer_tbl_ptr->irq == 0) {
                pr_debug("Watchdog timer: timer %d invalid irq\n",
-                                                       sfi_mtimer_num);
+                 sfi_mtimer_num);
                return -ENODEV;
        }
 
@@ -499,60 +643,88 @@ static int __init intel_scu_watchdog_init(void)
 
        /* Set the default time values in device structure */
 
-       watchdog_device.timer_set = timer_set;
-       watchdog_device.threshold =
-               timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
-       watchdog_device.soft_threshold =
-               (watchdog_device.timer_set - timer_margin)
-               * watchdog_device.timer_tbl_ptr->freq_hz;
-
-
        watchdog_device.intel_scu_notifier.notifier_call =
                intel_scu_notify_sys;
 
        ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
        if (ret) {
-               pr_err("Watchdog timer: cannot register notifier %d)\n", ret);
+               printk(KERN_ERR PFX
+                       "Watchdog timer: cannot register notifier %d)\n", ret);
                goto register_reboot_error;
        }
 
-       watchdog_device.miscdev.minor = WATCHDOG_MINOR;
-       watchdog_device.miscdev.name = "watchdog";
-       watchdog_device.miscdev.fops = &intel_scu_fops;
-
-       ret = misc_register(&watchdog_device.miscdev);
-       if (ret) {
-               pr_err("Watchdog timer: cannot register miscdev %d err =%d\n",
-                                                       WATCHDOG_MINOR, ret);
-               goto misc_register_error;
+       if (!disable_kernel_watchdog) {
+               watchdog_device.miscdev.minor = WATCHDOG_MINOR;
+               watchdog_device.miscdev.name = "watchdog";
+               watchdog_device.miscdev.fops = &intel_scu_fops;
+
+               ret = misc_register(&watchdog_device.miscdev);
+               if (ret) {
+                       printk(KERN_ERR PFX
+                              "Watchdog timer: cannot register miscdev %d err =%d\n",
+                              WATCHDOG_MINOR,
+                              ret);
+                       goto misc_register_error;
+               }
        }
 
+       wake_lock_init(&watchdog_wake_lock, WAKE_LOCK_SUSPEND,
+                       "intel_scu_watchdog");
+
        ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
                watchdog_timer_interrupt,
-               IRQF_SHARED, "watchdog",
+               IRQF_SHARED|IRQF_NO_SUSPEND, "watchdog",
                &watchdog_device.timer_load_count_addr);
        if (ret) {
-               pr_err("Watchdog timer: error requesting irq %d\n", ret);
+               printk(KERN_ERR "Watchdog timer: error requesting irq\n");
+               printk(KERN_ERR "Watchdog timer: error value returned is %d\n",
+                       ret);
                goto request_irq_error;
        }
-       /* Make sure timer is disabled before returning */
-       intel_scu_stop();
+
+       /* set up the tasklet for handling interrupt duties */
+       tasklet_init(&watchdog_device.interrupt_tasklet,
+               watchdog_interrupt_tasklet_body, (unsigned long)0);
+
+       /* set and start timer */
+       if (!disable_kernel_watchdog) {
+               ret = intel_scu_set_heartbeat(timer_set);
+
+               if (ret)
+                       pr_debug("cannot set watchdog during init\n");
+
+       } else {
+               iowrite32(0x00000002, watchdog_device.timer_control_addr);
+               intel_scu_stop();
+       }
+
+       init_timer(&softlock_timer);
+
        return 0;
 
 /* error cleanup */
 
 request_irq_error:
+
        misc_deregister(&watchdog_device.miscdev);
+
 misc_register_error:
+
+       pr_debug("Watchdog timer: misc_register_error\n");
        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
+
 register_reboot_error:
+
        intel_scu_stop();
+
        iounmap(watchdog_device.timer_load_count_addr);
+
        return ret;
 }
 
 static void __exit intel_scu_watchdog_exit(void)
 {
+       del_timer_sync(&softlock_timer);
 
        misc_deregister(&watchdog_device.miscdev);
        unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
@@ -561,7 +733,12 @@ static void __exit intel_scu_watchdog_exit(void)
        iounmap(watchdog_device.timer_load_count_addr);
 }
 
-late_initcall(intel_scu_watchdog_init);
+#ifdef MODULE
+module_init(intel_scu_watchdog_init);
+#else
+rootfs_initcall(intel_scu_watchdog_init);
+#endif
+
 module_exit(intel_scu_watchdog_exit);
 
 MODULE_AUTHOR("Intel Corporation");
@@ -569,3 +746,4 @@ MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
 MODULE_VERSION(WDT_VER);
+
index d2b074a..44bf587 100644 (file)
 /* minimum time between interrupts */
 #define MIN_TIME_CYCLE 1
 
-/* Time from warning to reboot is 2 seconds */
-#define DEFAULT_SOFT_TO_HARD_MARGIN 2
+#define DEFAULT_SOFT_TO_HARD_MARGIN 15
 
 #define MAX_TIME 170
 
-#define DEFAULT_TIME 5
+#define DEFAULT_TIME 75
 
 #define MAX_SOFT_TO_HARD_MARGIN (MAX_TIME-MIN_TIME_CYCLE)
 
@@ -58,6 +57,7 @@ struct intel_scu_watchdog_dev {
        struct sfi_timer_table_entry *timer_tbl_ptr;
        struct notifier_block intel_scu_notifier;
        struct miscdevice miscdev;
+       struct tasklet_struct interrupt_tasklet;
 };
 
 extern int sfi_mtimer_num;