*
*/
-#define DEBUG 1
+/* See Documentation/watchdog/intel-scu-watchdog.txt */
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/wakelock.h>
#include <asm/irq.h>
#include <asm/atomic.h>
-
-/* See arch/x86/kernel/ipc_mrst.c */
+#include <linux/intel_mid_pm.h>
#include <asm/intel_scu_ipc.h>
#include <asm/apb_timer.h>
#include "intel_scu_watchdog.h"
-/* Bounds number of times we will retry loading time count */
-/* This retry is a work around for a silicon bug. */
-#define MAX_RETRY 16
+/* Adjustment flags */
+/* from config file */
+/* #define CONFIG_DISABLE_SCU_WATCHDOG */
+/* local */
+#define CONFIG_INTEL_SCU_SOFT_LOCKUP
+#define CONFIG_DEBUG_WATCHDOG
+/* Defines */
#define IPC_SET_WATCHDOG_TIMER 0xF8
+#define IPC_SET_SUB_LOAD_THRES 0x00
+#define IPC_SET_SUB_DISABLE 0x01
+#define IPC_SET_SUB_KEEPALIVE 0x02
+
+#define WDIOC_SETTIMERTIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 11, int)
+#define WDIOC_GETTIMERTIMEOUT _IOW(WATCHDOG_IOCTL_BASE, 12, int)
+/* Statics */
+static struct intel_scu_watchdog_dev watchdog_device;
+static struct wake_lock watchdog_wake_lock;
static DECLARE_WAIT_QUEUE_HEAD(read_wq);
+static unsigned char osnib_reset = OSNIB_WRITE_VALUE;
/* 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 unsigned char osnib_reset = OSNIB_WRITE_VALUE;
+/* Module params */
static bool disable_kernel_watchdog;
-
+#ifdef CONFIG_DISABLE_SCU_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.
- *
- */
-#ifdef DEBUG
-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);
-#endif /* DEBUG */
-
-/**
* 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
+ * because this boot parameter should only be settable in a developement
*/
-#if defined(CONFIG_DISABLE_SCU_WATCHDOG)
module_param(disable_kernel_watchdog, bool, S_IRUGO);
MODULE_PARM_DESC(disable_kernel_watchdog,
"Disable kernel watchdog"
"driver if disable_kernel_watchdog is set");
#endif
-static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
-module_param(timer_margin, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(timer_margin,
- "Watchdog timer margin"
+static int pre_timeout = DEFAULT_PRETIMEOUT;
+module_param(pre_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pre_timeout,
+ "Watchdog pre timeout"
"Time between interrupt and resetting the system"
- "The range is from 1 to 160"
- "This is the time for all keep alives to arrive");
+ "The range is from 1 to 160");
-static int timer_set = DEFAULT_TIME;
-module_param(timer_set, int, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(timer_set,
+static int timeout = DEFAULT_TIMEOUT;
+module_param(timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(timeout,
"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_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 bool force_reset = true;
-module_param(force_reset, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(force_reset,
+static int timer_timeout = DEFAULT_TIMER_DURATION;
+module_param(timer_timeout, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(timer_timeout,
+ "Watchdog timer timeout"
+ "Time between timer interrupt and resetting the system");
+
+static bool reset_on_release = true;
+static bool kicking_active = true;
+#ifdef CONFIG_DEBUG_WATCHDOG
+module_param(reset_on_release, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(reset_on_release,
"A true means that the driver will reboot"
"the system immediately if the /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"
+ "of length: timeout. At the end of this interval, the"
"watchdog timer will reset the system."
);
-/* there is only one device in the system now; this can be made into
- * an array in the future if we have more than one device */
-
-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_reset) {
- printk(KERN_CRIT PFX "Initiating system reboot.\n");
- emergency_restart();
- printk(KERN_CRIT PFX "Reboot didn't ?????\n");
- }
-
- else {
- printk(KERN_CRIT PFX "Immediate Reboot Disabled\n");
- printk(KERN_CRIT PFX
- "System will reset when watchdog timer times out!\n");
- }
-}
+module_param(kicking_active, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(kicking_active,
+ "Deactivating the kicking will result in a cold reset"
+ "after a while"
+ );
+#endif
-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",
- new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
- return -EINVAL;
- }
- return 0;
-}
+#ifdef CONFIG_INTEL_SCU_SOFT_LOCKUP
+/*
+ * 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);
/* 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
static void dump_softlock_debug(unsigned long data)
{
-#ifdef DEBUG
int i, reboot;
cputime64_t system[NR_CPUS], num_jifs;
panic_timeout = 10;
panic("Soft lock on CPUs\n");
}
-#else /* DEBUG */
- return;
-#endif /* DEBUG */
-
}
+#endif /* CONFIG_INTEL_SCU_SOFT_LOCKUP */
+/* Check current timeouts */
+static int check_timeouts(void)
+{
+ if (timer_timeout+pre_timeout < timeout)
+ return 0;
-/*
- * IPC operations
- */
-static int watchdog_set_ipc(int soft_threshold, int threshold)
+ return -EINVAL;
+}
+
+/* Set the different timeouts needed by the SCU FW */
+static int watchdog_set_timeouts(int timer_threshold, int warning_pretimeout,
+ int reset_timeout)
{
u32 *ipc_wbuf;
- u8 cbuf[16] = { '\0' };
- int ipc_ret = 0;
+ u8 cbuf[16] = { '\0' };
+ int ret = 0;
+ u32 freq = watchdog_device.timer7_tbl_ptr->freq_hz;
ipc_wbuf = (u32 *)&cbuf;
- ipc_wbuf[0] = soft_threshold;
- ipc_wbuf[1] = threshold;
-
- ipc_ret = intel_scu_ipc_command(
- IPC_SET_WATCHDOG_TIMER,
- 0,
- ipc_wbuf,
- 2,
- NULL,
- 0);
-
- if (ipc_ret != 0)
- printk(KERN_CRIT PFX "Error Setting SCU Watchdog Timer: %x\n",
- ipc_ret);
-
- return ipc_ret;
-};
+ ipc_wbuf[0] = timer_threshold * freq;
+ ipc_wbuf[1] = warning_pretimeout * freq;
+ ipc_wbuf[2] = (reset_timeout - timer_threshold - warning_pretimeout)
+ * freq;
-/*
- * Intel_SCU operations
- */
+ pr_debug(PFX "Watchdog ipc_buff[0]%x\n", ipc_wbuf[0]);
+ pr_debug(PFX "Watchdog ipc_buff[1]%x\n", ipc_wbuf[1]);
+ pr_debug(PFX "Watchdog ipc_buff[2]%x\n", ipc_wbuf[2]);
+ ret = intel_scu_ipc_command(IPC_SET_WATCHDOG_TIMER,
+ IPC_SET_SUB_LOAD_THRES,
+ ipc_wbuf, 3, NULL, 0);
-static int intel_scu_keepalive(void)
-{
+ if (ret)
+ pr_crit(PFX "Error Setting SCU Watchdog Timer: %x\n", ret);
- pr_debug("Watchdog timer: keepalive: soft_threshold %x\n",
- watchdog_device.soft_threshold);
+ return ret;
+};
- /* read eoi register - clears interrupt */
- ioread32(watchdog_device.timer_clear_interrupt_addr);
+/* Keep alive */
+static int watchdog_keepalive(void)
+{
+int ret;
- /* temporarily disable the timer */
- iowrite32(0x00000002, watchdog_device.timer_control_addr);
+ pr_err(PFX "%s\n", __func__);
- /* set the timer to the soft_threshold */
- iowrite32(watchdog_device.soft_threshold,
- watchdog_device.timer_load_count_addr);
+ if (unlikely(!kicking_active)) {
+ /* Close our eyes */
+ pr_err(PFX "Transparent kicking\n");
+ return 0;
+ }
- /* allow the timer to run */
- iowrite32(0x00000003, watchdog_device.timer_control_addr);
+ /* Really kick it */
+ ret = intel_scu_ipc_command(IPC_SET_WATCHDOG_TIMER,
+ IPC_SET_SUB_KEEPALIVE, NULL, 0, NULL, 0);
+ if (ret)
+ pr_err(PFX "Error sending keepalive ipc: %x\n", ret);
- return 0;
+ return ret;
}
+/* stops the timer */
static int intel_scu_stop(void)
{
- iowrite32(0, watchdog_device.timer_control_addr);
- return 0;
+int ret;
+
+ pr_err(PFX "%s\n", __func__);
+
+ ret = intel_scu_ipc_command(IPC_SET_WATCHDOG_TIMER,
+ IPC_SET_SUB_DISABLE, NULL, 0, NULL, 0);
+ if (ret) {
+ pr_crit(PFX "Error sending disable ipc: %x\n", ret);
+ goto err;
+ }
+
+ watchdog_device.started = false;
+
+err:
+ return ret;
}
-/* tasklet function for interrupt; keep interupt itself simple */
+/* tasklet */
static void watchdog_interrupt_tasklet_body(unsigned long data)
{
- int i, int_status;
+int ret;
- pr_debug("Watchdog: interrupt tasklet body start\n");
+ pr_warn(PFX "interrupt tasklet body start\n");
if (disable_kernel_watchdog) {
- pr_debug("Watchdog: interrupt tasklet body disable set\n");
/* disable the timer */
- /* Set all thresholds to 0 to disable timeouts */
- watchdog_device.soft_threshold = 0;
- watchdog_device.threshold = 0;
-
- /* send the threshold and soft_threshold via IPC */
- int_status = watchdog_set_ipc(watchdog_device.soft_threshold,
- watchdog_device.threshold);
-
- if (int_status != 0) {
- /* Make sure the watchdog timer is stopped */
- pr_warn("can't set ipc to disable at start\n");
- intel_scu_stop();
- return;
- }
-
- iowrite32(0x00000002, watchdog_device.timer_control_addr);
- intel_scu_stop();
+ pr_warn(PFX "interrupt tasklet body disable set\n");
+ ret = intel_scu_stop();
+ if (ret)
+ pr_err(PFX "cannot disable the timer\n");
return;
}
/* wake up read to send data to user (reminder for keep alive */
warning_flag = 1;
-#ifdef DEBUG
- /*start timer for softlock detection */
- beattime = jiffies;
- for_each_possible_cpu(i) {
- heartbeats[i] = kstat_cpu(i).cpustat.system;
+#ifdef CONFIG_INTEL_SCU_SOFT_LOCKUP
+ {
+ int i;
+ /*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);
}
- mod_timer(&softlock_timer, jiffies + SOFT_LOCK_TIME * HZ);
-#endif /* DEBUG */
+#endif
/* Wake up the daemon */
wake_up_interruptible(&read_wq);
/* 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");
+ if (!watchdog_device.started) {
+ pr_warn(PFX "Spurious interrupt received\n");
return IRQ_HANDLED;
}
return IRQ_HANDLED;
}
-static int intel_scu_set_heartbeat(u32 t)
+/* warning interrupt handler */
+static irqreturn_t watchdog_warning_interrupt(int irq, void *dev_id)
{
- int ipc_ret;
-
- watchdog_device.timer_set = t;
- 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;
-
- pr_debug("Watchdog timer: set_heartbeat: timer freq is %d\n",
- watchdog_device.timer_tbl_ptr->freq_hz);
- pr_debug("Watchdog timer: set_heartbeat: timer_set is %x (hex)\n",
- watchdog_device.timer_set);
- pr_debug("Watchdog timer: set_hearbeat: timer_margin is %x (hex)\n",
- timer_margin);
- pr_debug("Watchdog timer: set_heartbeat: threshold is %x (hex)\n",
- watchdog_device.threshold);
- pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
- watchdog_device.soft_threshold);
-
-
- /* temporarily disable the timer */
- iowrite32(0x00000002, watchdog_device.timer_control_addr);
-
- /* send the threshold and soft_threshold via IPC to the processor */
- ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
- watchdog_device.threshold);
-
- if (ipc_ret != 0) {
- /* Make sure the watchdog timer is stopped */
- intel_scu_stop();
- return ipc_ret;
- }
+ pr_warn("[SHTDWN] %s, WATCHDOG TIMEOUT!\n", __func__);
- /* Make sure timer is stopped */
- intel_scu_stop();
+ /* Let's reset the platform after dumping some data */
+ panic("Kernel Watchdog");
- /* set the timer to the soft threshold */
- iowrite32(watchdog_device.soft_threshold,
- watchdog_device.timer_load_count_addr);
+ /* This code should not be reached */
+ return IRQ_HANDLED;
+}
- /* Start the timer */
- iowrite32(0x00000003, watchdog_device.timer_control_addr);
+/* Program and starts the timer */
+static int watchdog_config_and_start(u32 newtimeout, u32 newpretimeout)
+{
+int ret;
- watchdog_device.timer_started = 1;
+ timeout = newtimeout;
+ pre_timeout = newpretimeout;
+
+ pr_warn(PFX "Configuration: %dkHz, timeout=%ds, pre_timeout=%ds, timer=%ds\n",
+ watchdog_device.timer7_tbl_ptr->freq_hz / 1000, timeout,
+ pre_timeout, timer_timeout);
+
+ /* Configure the watchdog */
+ ret = watchdog_set_timeouts(timer_timeout, pre_timeout, timeout);
+ if (ret) {
+ pr_err(PFX "%s: Cannot configure the watchdog\n", __func__);
+
+ /* Make sure the watchdog timer is stopped */
+ intel_scu_stop();
+ return ret;
+ }
+
+ watchdog_device.started = true;
return 0;
}
-/*
- * /dev/watchdog handling
- */
-
+/* Open */
static int intel_scu_open(struct inode *inode, struct file *file)
{
- int result;
+int ret;
/* Set flag to indicate that watchdog device is open */
if (test_and_set_bit(0, &watchdog_device.driver_open))
return -EPERM;
/* Let shared OSNIB (sram) know we are open */
- result = intel_scu_ipc_write_osnib(
- &osnib_reset,
- OSNIB_WRITE_SIZE,
- OSNIB_WDOG_OFFSET,
- OSNIB_WRITE_MASK);
-
- if (result != 0) {
- pr_warn("cant write OSNIB\n");
+ /* To publish a proc and ioctl to do this and leave userland decide */
+ /* when it is sensible to do it (boot completed intent) */
+ ret = intel_scu_ipc_write_osnib(&osnib_reset, OSNIB_WRITE_SIZE,
+ OSNIB_WDOG_OFFSET, OSNIB_WRITE_MASK);
+
+ if (ret != 0) {
+ pr_err(PFX "cannot write OSNIB\n");
return -EINVAL;
}
return nonseekable_open(inode, file);
}
+/* Release */
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_reset is set watchdog_fire() will cause an
- * immediate reset. If force_reset is not set, the watchdog
+ * If reset_on_release is set this will cause an
+ * immediate reset. If reset_on_release 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_bit(0, &watchdog_device.driver_open)) {
- pr_debug("Watchdog timer: intel_scu_release, without open\n");
+ pr_err(PFX "intel_scu_release, without open\n");
return -ENOTTY;
}
- if (!watchdog_device.timer_started) {
+ if (!watchdog_device.started) {
/* Just close, since timer has not been started */
- pr_debug("Watchdog timer: Closed, without starting timer\n");
+ pr_err(PFX "Closed, without starting timer\n");
return 0;
}
- printk(KERN_CRIT PFX
- "Unexpected close of /dev/watchdog!\n");
+ pr_crit(PFX "Unexpected close of /dev/watchdog!\n");
/* Since the timer was started, prevent future reopens */
watchdog_device.driver_closed = 1;
/* Refresh the timer for one more interval */
- intel_scu_keepalive();
+ watchdog_keepalive();
- /* Reboot system (if force_reset is set) */
- watchdog_fire();
+ /* Reboot system if requested */
+ if (reset_on_release) {
+ pr_crit(PFX "Initiating system reboot.\n");
+ emergency_restart();
+ }
+
+ pr_crit(PFX "Immediate Reboot Disabled\n");
+ pr_crit(PFX "System will reset when watchdog timer expire!\n");
- /* We should only reach this point if force_reset is not set */
return 0;
}
-static ssize_t intel_scu_write(struct file *file,
- char const *data,
- size_t len,
+/* Write */
+static ssize_t intel_scu_write(struct file *file, char const *data, size_t len,
loff_t *ppos)
{
+ pr_debug(PFX "watchdog %s\n", __func__);
- if (watchdog_device.timer_started) {
+ if (watchdog_device.started) {
/* Watchdog already started, keep it alive */
- intel_scu_keepalive();
+ watchdog_keepalive();
wake_unlock(&watchdog_wake_lock);
- } else
+ } else {
/* Start watchdog with timer value set by init */
- intel_scu_set_heartbeat(watchdog_device.timer_set);
+ watchdog_config_and_start(timeout, pre_timeout);
+ }
return len;
}
-static ssize_t intel_scu_read(struct file *file,
- char __user *user_data,
- size_t len,
- loff_t *user_ppos)
+/* Read */
+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;
+int ret;
+const u8 *buf = "0";
/* we wait for the next interrupt; if more than one */
/* interrupt has occurred since the last read, we */
/* 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);
+ ret = copy_to_user(user_data, (const void *)buf, 1);
- if (result != 0)
+ if (ret)
return -EFAULT;
- else
- return 1;
+ return 1;
}
+/* Poll */
static unsigned int intel_scu_poll(struct file *file, poll_table *wait)
{
- unsigned int mask = 0;
+unsigned int mask;
+
poll_wait(file, &read_wq, wait);
+ mask = 0;
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)
+/* ioctl */
+static long intel_scu_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
- void __user *argp = (void __user *)arg;
- u32 __user *p = argp;
- u32 new_margin;
-
+void __user *argp = (void __user *)arg;
+u32 __user *p = argp;
+u32 val;
+int options;
static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT
- | WDIOF_KEEPALIVEPING,
- .firmware_version = 0, /* @todo Get from SCU via
- ipc_get_scu_fw_version()? */
- .identity = "Intel_SCU IOH Watchdog" /* len < 32 */
+ .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ /* @todo Get from SCU via ipc_get_scu_fw_version()? */
+ .firmware_version = 0,
+ /* len < 32 */
+ .identity = "Intel_SCU IOH Watchdog"
};
switch (cmd) {
case WDIOC_GETSUPPORT:
- return copy_to_user(argp,
- &ident,
+ return copy_to_user(argp, &ident,
sizeof(ident)) ? -EFAULT : 0;
case WDIOC_GETSTATUS:
case WDIOC_GETBOOTSTATUS:
return put_user(0, p);
case WDIOC_KEEPALIVE:
- intel_scu_keepalive();
+ pr_warn(PFX "%s: KeepAlive ioctl\n", __func__);
+ if (!watchdog_device.started)
+ return -EINVAL;
+ watchdog_keepalive();
return 0;
- case WDIOC_SETTIMEOUT:
- if (get_user(new_margin, p))
+ case WDIOC_SETTIMERTIMEOUT:
+ pr_warn(PFX "%s: SetTimerTimeout ioctl\n", __func__);
+
+ if (watchdog_device.started)
+ return -EBUSY;
+
+ /* Timeout to start scheduling the daemon */
+ if (get_user(val, p))
return -EFAULT;
- if (check_timer_margin(new_margin))
- return -EINVAL;
+ timer_timeout = val;
+ return 0;
+ case WDIOC_SETPRETIMEOUT:
+ pr_warn(PFX "%s: SetPreTimeout ioctl\n", __func__);
- if (intel_scu_set_heartbeat(new_margin))
- return -EINVAL;
+ if (watchdog_device.started)
+ return -EBUSY;
+
+ /* Timeout to warn */
+ if (get_user(val, p))
+ return -EFAULT;
+
+ pre_timeout = val;
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ pr_warn(PFX "%s: SetTimeout ioctl\n", __func__);
+
+ if (watchdog_device.started)
+ return -EBUSY;
+
+ if (get_user(val, p))
+ return -EFAULT;
+
+ timeout = val;
return 0;
case WDIOC_GETTIMEOUT:
- return put_user(watchdog_device.soft_threshold, p);
+ return put_user(timeout, p);
+ case WDIOC_SETOPTIONS:
+ if (get_user(options, p))
+ return -EFAULT;
+ if (options & WDIOS_DISABLECARD) {
+ pr_warn(PFX "%s: Stopping the watchdog\n", __func__);
+ intel_scu_stop();
+ return 0;
+ }
+
+ if (options & WDIOS_ENABLECARD) {
+ pr_warn(PFX "%s: Starting the watchdog\n", __func__);
+
+ if (watchdog_device.started)
+ return -EBUSY;
+
+ if (check_timeouts()) {
+ pr_warn(PFX "%s: Invalid thresholds\n",
+ __func__);
+ return -EINVAL;
+ }
+ if (watchdog_config_and_start(timeout, pre_timeout))
+ return -EINVAL;
+ return 0;
+ }
+ return 0;
default:
return -ENOTTY;
}
}
-/*
- * Notifier for system down
- */
-static int intel_scu_notify_sys(struct notifier_block *this,
- unsigned long code,
- void *another_unused)
+/* Reboot notifier */
+static int reboot_notifier(struct notifier_block *this,
+ unsigned long code,
+ void *another_unused)
{
- if (code == SYS_DOWN || code == SYS_HALT) {
+int ret;
+
+ if (code == SYS_RESTART || code == SYS_HALT || code == SYS_POWER_OFF) {
+ pr_warn(PFX "Reboot notifier\n");
+
/* Don't do instant reset on close */
- pr_debug("Watchdog timer - HALT or RESET notification\n");
- force_reset = false;
+ reset_on_release = false;
+
+ /* Kick once again */
+ ret = watchdog_keepalive();
+ if (ret)
+ pr_warn(PFX "%s: cannot keep timer alive\n", __func__);
}
return NOTIFY_DONE;
}
-/*
- * Kernel Interfaces
- */
+/* Kernel Interfaces */
static const struct file_operations intel_scu_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.release = intel_scu_release,
};
+/* Init code */
static int __init intel_scu_watchdog_init(void)
{
int ret;
- u32 __iomem *tmp_addr;
-
- /* 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) "
- "is out of range from %x to %x (hex)\n",
- timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
+ /* Check timeouts boot parameter */
+ if (check_timeouts()) {
+ pr_err(PFX "%s: Invalid timeouts\n", __func__);
return -EINVAL;
}
- /* Check value of timer_margin boot parameter */
- if (check_timer_margin(timer_margin))
- return -EINVAL;
-
- 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"
+ /* Acquire timer 7 */
+ watchdog_device.timer7_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
+ if (watchdog_device.timer7_tbl_ptr == NULL) {
+ pr_debug(PFX "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"
+ if (watchdog_device.timer7_tbl_ptr->phys_addr == 0) {
+ pr_debug(PFX "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",
+ if (watchdog_device.timer7_tbl_ptr->irq == 0) {
+ pr_debug(PFX "Watchdog timer: timer %d invalid irq\n",
sfi_mtimer_num);
return -ENODEV;
}
- tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
- 20);
-
- if (tmp_addr == NULL) {
- pr_debug("Watchdog timer: timer unable to ioremap\n");
- return -ENOMEM;
+ /* Acquire timer 6 */
+ watchdog_device.timer6_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-2);
+ if (watchdog_device.timer6_tbl_ptr == NULL) {
+ pr_debug(PFX "Watchdog timer - Intel SCU watchdog: Timer is"
+ " not available\n");
+ return -ENODEV;
+ }
+ if (watchdog_device.timer6_tbl_ptr->irq == 0) {
+ pr_debug(PFX "Watchdog timer: timer %d invalid irq\n",
+ sfi_mtimer_num);
+ return -ENODEV;
}
- watchdog_device.timer_load_count_addr = tmp_addr++;
- watchdog_device.timer_current_value_addr = tmp_addr++;
- watchdog_device.timer_control_addr = tmp_addr++;
- watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
- watchdog_device.timer_interrupt_status_addr = tmp_addr++;
-
- /* Set the default time values in device structure */
-
- watchdog_device.intel_scu_notifier.notifier_call =
- intel_scu_notify_sys;
-
- ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
+ /* Reboot notifier */
+ watchdog_device.reboot_notifier.notifier_call = reboot_notifier;
+ watchdog_device.reboot_notifier.priority = 1;
+ ret = register_reboot_notifier(&watchdog_device.reboot_notifier);
if (ret) {
- printk(KERN_ERR PFX
- "Watchdog timer: cannot register notifier %d)\n", ret);
- goto register_reboot_error;
+ pr_crit(PFX "cannot register reboot notifier %d\n", ret);
+ goto error_stop_timer;
}
+ /* Do not publish the watchdog device when disable (TO BE REMOVED) */
if (!disable_kernel_watchdog) {
watchdog_device.miscdev.minor = WATCHDOG_MINOR;
watchdog_device.miscdev.name = "watchdog";
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;
+ pr_crit(PFX "Cannot register miscdev %d err =%d\n",
+ WATCHDOG_MINOR, ret);
+ goto error_reboot_notifier;
}
}
wake_lock_init(&watchdog_wake_lock, WAKE_LOCK_SUSPEND,
"intel_scu_watchdog");
- ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
+ /* MSI #7 handler for timer interrupts */
+ ret = request_irq((unsigned int)watchdog_device.timer7_tbl_ptr->irq,
watchdog_timer_interrupt,
+ IRQF_SHARED|IRQF_NO_SUSPEND, "watchdog timer",
+ &watchdog_device);
+ if (ret) {
+ pr_err(PFX "error requesting irq %d\n",
+ watchdog_device.timer7_tbl_ptr->irq);
+ pr_err(PFX "error value returned is %d\n", ret);
+ goto error_misc_register;
+ }
+
+ /* MSI #6 handler to dump registers */
+ ret = request_irq((unsigned int)watchdog_device.timer6_tbl_ptr->irq,
+ watchdog_warning_interrupt,
IRQF_SHARED|IRQF_NO_SUSPEND, "watchdog",
- &watchdog_device.timer_load_count_addr);
+ &watchdog_device);
if (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;
+ pr_err(PFX "error requesting warning irq %d\n",
+ watchdog_device.timer6_tbl_ptr->irq);
+ pr_err(PFX "error value returned is %d\n", ret);
+ goto error_request_irq;
}
/* set up the tasklet for handling interrupt duties */
tasklet_init(&watchdog_device.interrupt_tasklet,
watchdog_interrupt_tasklet_body, (unsigned long)0);
-#ifdef DEBUG
+#ifdef CONFIG_INTEL_SCU_SOFT_LOCKUP
init_timer(&softlock_timer);
-#endif /* DEBUG */
+#endif
if (disable_kernel_watchdog) {
- pr_debug("disabling the timer\n");
-
- /* temporarily disable the timer */
- iowrite32(0x00000002, watchdog_device.timer_control_addr);
-
- /* Set all thresholds to 0 to disable timeouts */
- watchdog_device.soft_threshold = 0;
- watchdog_device.threshold = 0;
-
- /* send the threshold and soft_threshold via IPC */
- ret = watchdog_set_ipc(watchdog_device.soft_threshold,
- watchdog_device.threshold);
-
- if (ret != 0) {
- /* Make sure the watchdog timer is stopped */
- pr_warn("can't set ipc to disable at start\n");
- intel_scu_stop();
- return ret;
- }
+ pr_debug(PFX "disabling the timer\n");
/* Make sure timer is stopped */
- intel_scu_stop();
-
+ ret = intel_scu_stop();
+ if (ret != 0)
+ pr_debug(PFX "cant disable timer\n");
}
- return 0;
-
-/* error cleanup */
-request_irq_error:
+ watchdog_device.started = false;
- misc_deregister(&watchdog_device.miscdev);
+ return 0;
-misc_register_error:
+error_request_irq:
+ free_irq(watchdog_device.timer7_tbl_ptr->irq, NULL);
- pr_debug("Watchdog timer: misc_register_error\n");
- unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
+error_misc_register:
+ misc_deregister(&watchdog_device.miscdev);
-register_reboot_error:
+error_reboot_notifier:
+ unregister_reboot_notifier(&watchdog_device.reboot_notifier);
+error_stop_timer:
intel_scu_stop();
- iounmap(watchdog_device.timer_load_count_addr);
-
return ret;
}
static void __exit intel_scu_watchdog_exit(void)
{
-#ifdef DEBUG
+ int ret = 0;
+#ifdef CONFIG_INTEL_SCU_SOFT_LOCKUP
del_timer_sync(&softlock_timer);
-#endif /* DEBUG */
+#endif
+
+ ret = intel_scu_stop();
+ if (ret != 0)
+ pr_err(PFX "cant disable timer\n");
misc_deregister(&watchdog_device.miscdev);
- unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
- /* disable the timer */
- iowrite32(0x00000002, watchdog_device.timer_control_addr);
- iounmap(watchdog_device.timer_load_count_addr);
+ unregister_reboot_notifier(&watchdog_device.reboot_notifier);
}
#ifdef MODULE
module_exit(intel_scu_watchdog_exit);
MODULE_AUTHOR("Intel Corporation");
+MODULE_AUTHOR("mark.a.allyn@intel.com");
+MODULE_AUTHOR("yannx.puech@intel.com");
MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
MODULE_VERSION(WDT_VER);
-