watchdog: Adding Merrifield watchdog driver support
PATCH 1/1 watchdog: Adding Merrifield watchdog driver support
From: Gabriel Touzeau <[email protected]>
Added Merrifield watchdog driver support.
Based on initial implementation from prior Intel SCU-based platforms, this driver has several changes specific to the Tangier SoC / Merrifield platform.
--- drivers/watchdog/Kconfig | 12 + drivers/watchdog/Makefile | 1 + drivers/watchdog/intel_scu_watchdog_evo.c | 587 +++++++++++++++++++++++++++++ drivers/watchdog/intel_scu_watchdog_evo.h | 54 +++ 4 files changed, 654 insertions(+) create mode 100644 drivers/watchdog/intel_scu_watchdog_evo.c create mode 100644 drivers/watchdog/intel_scu_watchdog_evo.h
drivers/watchdog/Kconfig +12 -0
@@ -616,6 +616,18 @@ config INTEL_SCU_WATCHDOG To compile this driver as a module, choose M here. +config INTEL_SCU_WATCHDOG_EVO + bool "Intel SCU Watchdog Evolution for Mobile Platforms" + depends on X86_INTEL_MID + ---help--- + Hardware driver evolution for the watchdog timer built into the Intel + SCU for Intel Mobile Platforms. + + This driver supports the watchdog evolution implementation in SCU, + available for Merrifield generation. + + To compile this driver as a module, choose M here. + config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCI
drivers/watchdog/Makefile +1 -0
@@ -112,6 +112,7 @@ obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o +obj-$(CONFIG_INTEL_SCU_WATCHDOG_EVO) += intel_scu_watchdog_evo.o # M32R Architecture
drivers/watchdog/intel_scu_watchdog_evo.c new file +587 -0
587 lines not on this page · raw message
drivers/watchdog/intel_scu_watchdog_evo.h new file +54 -0
54 lines not on this page · raw message
Signature · 1 line
1.7.9.5
Hi Gabriel,
Quoted 6 lines · On Thu, Jan 02, 2014 at 04:56:42PM -0800, [email protected] wrote:
drivers/watchdog/intel_scu_watchdog_evo.c new file +6
-0+ +/* Statics */ +static struct intel_scu_watchdog_dev watchdog_device; + +/* Module params */ +static bool disable_kernel_watchdog;
drivers/watchdog/intel_scu_watchdog_evo.c line 81
Quoted 10 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +10
-0+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" );
You need to add spaces at the end of your strings otheriwse concatenation will produce a mess.
Quoted 11 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +11
-0+ +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" ); + +static int timeout =DEFAULT_TIMEOUT ; +module_param (timeout, int,S_IRUGO |S_IWUSR ); +MODULE_PARM_DESC(timeout,
drivers/watchdog/intel_scu_watchdog_evo.c line 102
Quoted 10 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +10
-0+"Default Watchdog timer setting" +"Complete cycle time" +"The range is from 1 to 170" + "This is the time for all keep alives to arrive"); + +/* Setting reset_on_release will cause an immediate reset when the watchdog +* is released. If false, the watchdog timer is refreshed for one more +* interval. At the end of that interval, the watchdog timer will reset the +* system. +*/
Multi-line comments in majority of kernel code are in form of
/*
* Multi-line comment should be
* formatted like this.
*/
Quoted 112 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +112
-0+static bool reset_on_release = true; + +/* Check current timeouts */ +static int check_timeouts (int pre_timeout_time,int timeout_time) +{ +if (pre_timeout_time < timeout_time) +return 0 ; + +return -EINVAL ; +} + +/* Set the different timeouts needed by the SCU FW and start the +* kernel watchdog */ +static int watchdog_set_timeouts_and_start (int pretimeout, +int timeout) +{ +int ret, input_size; +struct ipc_wd_start { +u32 pretimeout; +u32 timeout; + } ipc_wd_start = { pretimeout, timeout }; + +/* SCU expects the input size for watchdog IPC to +* be based on double-word */ + input_size = (sizeof (ipc_wd_start) +3 ) /4 ; + + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_START , (u32 *)&ipc_wd_start, + input_size,NULL ,0 ); +if (ret) { +pr_crit ("Error configuring and starting watchdog: %d\n" , + ret); +return -EIO ; + } + +return 0 ; +} + +/* Provisioning function for future enhancement : allow to fine tune timing +according to watchdog action settings */ +static int watchdog_set_appropriate_timeouts (void ) +{ +pr_debug ("Setting shutdown timeouts\n" ); +return watchdog_set_timeouts_and_start (pre_timeout, timeout); +} + +/* Keep alive */ +static int watchdog_keepalive (void ) +{ +int ret; + +/* Pet the watchdog */ + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_KEEPALIVE ,NULL ,0 ,NULL ,0 ); +if (ret) { +pr_crit ("Error executing keepalive: %x\n" , ret); +return -EIO ; + } + +return 0 ; +} + +/* stops the timer */ +static int watchdog_stop (void ) +{ +int ret; + + watchdog_device.started = false; + + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_STOP ,NULL ,0 ,NULL ,0 ); +if (ret) { +pr_crit ("Error stopping watchdog: %x\n" , ret); +return -EIO ; + } +return 0 ; +} + +/* warning interrupt handler */ +static irqreturn_t watchdog_warning_interrupt (int irq,void *dev_id) +{ +pr_warn ("[SHTDWN] %s, WATCHDOG TIMEOUT!\n" , __func__); + +/* Let's reset the platform after dumping some data */ +trigger_all_cpu_backtrace (); +panic ("Kernel Watchdog" ); + +/* This code should not be reached */ +return IRQ_HANDLED ; +} + +/* Program and starts the timer */ +static int watchdog_config_and_start (u32 newtimeout,u32 newpretimeout) +{ +int ret; + + timeout = newtimeout; + pre_timeout = newpretimeout; + +pr_info ("timeout=%ds, pre_timeout=%ds\n" , timeout, pre_timeout); + +/* Configure the watchdog */ + ret =watchdog_set_timeouts_and_start (pre_timeout, timeout); +if (ret) { +pr_err ("%s: Cannot configure the watchdog\n" , __func__); + +/* Make sure the watchdog timer is stopped */ +watchdog_stop (); +return ret; + } + + watchdog_device.started = true;
drivers/watchdog/intel_scu_watchdog_evo.c line 224
Quoted 10 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +10
-0+ +return 0 ; +} + +/* Open */ +static int intel_scu_open (struct inode *inode,struct file *file) +{ +/* Set flag to indicate that watchdog device is open */ +if (test_and_set_bit (0 , &watchdog_device.driver_open)) { +pr_err ("watchdog device is busy\n" );
You probably do not want to spam the logs. Returning -EBUSY should be enough.
Quoted 16 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +16
-0+return -EBUSY ; + } + +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 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.
drivers/watchdog/intel_scu_watchdog_evo.c line 250
Quoted 10 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +10
-0+*/ + +if (!test_bit (0 , &watchdog_device.driver_open)) { +pr_err ("intel_scu_release, without open\n" ); +return -ENOTTY ; + } + +if (!watchdog_device.started) { +/* Just close, since timer has not been started */ +pr_err ("Closed, without starting timer\n" );
Why pr_err? It looks like it might be useful when debugging the driver, but not for production code.
Thanks.
Signature · 1 line
Dmitry
Quoted 6 lines · On Thu, 2 Jan 2014 16:56:42 -0800 [email protected] wrote:
From: Gabriel Touzeau <[email protected]>
Added Merrifield watchdog driver support.
Based on initial implementation from prior Intel SCU-based platforms, this driver has several changes specific to the Tangier SoC / Merrifield platform.
We have a watchdog driver layer now. Any new driver should be using that. It deals with a lot of the work for you so you'll just need the actual methods doing the work. It also registers both the legacy device and new style watchdog nodes as appropriate so you can have multiple watchdogs on a system.
Look at watchdog_core.c and watchdog_dev.c and the various things that use it.
Alan
Guenter Roeck
maintainerQuoted 7 lines · On 01/02/2014 04:56 PM, [email protected] wrote:
From: Gabriel Touzeau <[email protected]>
Added Merrifield watchdog driver support.
Based on initial implementation from prior Intel SCU-based platforms, this driver has several changes specific to the Tangier SoC / Merrifield platform.
New watchdog drivers should be implemented using the watchdog subsystem.
Quoted 20 lines
Signed-off-by Eric Ernst <[email protected]>Signed-off-by Yann Puech <[email protected]>Signed-off-by Jeremy Compostella <[email protected]>Signed-off-by Gabriel Touzeau <[email protected]>Cc David Cohen <[email protected]>--- drivers/watchdog/Kconfig | 12 + drivers/watchdog/Makefile | 1 + drivers/watchdog/intel_scu_watchdog_evo.c | 587 +++++++++++++++++++++++++++++ drivers/watchdog/intel_scu_watchdog_evo.h | 54 +++ 4 files changed, 654 insertions(+)create mode 100644
drivers/watchdog/intel_scu_watchdog_evo.ccreate mode 100644drivers/watchdog/intel_scu_watchdog_evo.hdrivers/watchdog/Kconfig +0
-0@@ -616,6 +616,18 @@ config INTEL_SCU_WATCHDOG
drivers/watchdog/Kconfig config INTEL_SCU_WATCHDOG · line 626
Quoted 10 lines
drivers/watchdog/Kconfig +8
-0To compile this driver as a module, choose M here. +config INTEL_SCU_WATCHDOG_EVO + bool "Intel SCU Watchdog Evolution for Mobile Platforms" + depends on X86_INTEL_MID + ---help--- + Hardware driver evolution for the watchdog timer built into the Intel + SCU for Intel Mobile Platforms. + + This driver supports the watchdog evolution implementation in SCU,
"watchdog evolution" ... odd terminology.
Quoted 38 lines
drivers/watchdog/Kconfig +4
-0+ available for Merrifield generation. + + To compile this driver as a module, choose M here. + config ITCO_WDT tristate "Intel TCO Timer/Watchdog" depends on (X86 || IA64) && PCIdrivers/watchdog/Makefile +1
-0@@ -112,6 +112,7 @@ obj-$(CONFIG_W83977F_WDT) += w83977f_wdt.o obj-$(CONFIG_MACHZ_WDT) += machzwd.o obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o obj-$(CONFIG_INTEL_SCU_WATCHDOG) += intel_scu_watchdog.o +obj-$(CONFIG_INTEL_SCU_WATCHDOG_EVO) += intel_scu_watchdog_evo.o # M32R Architecturedrivers/watchdog/intel_scu_watchdog_evo.c new file +13
-0@@ -0,0 +1,587 @@ +/* +* intel_scu_watchdog_evo: An Intel SCU IOH Based Watchdog Device +* for Tangier SoC (Merrifield platform) +* +* Based on previous intel_scu based watchdog driver, intel_scu_watchdog. +* +* Copyright (C) 2009-2013 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of version 2 of the GNU General +* Public License as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be
drivers/watchdog/intel_scu_watchdog_evo.c line 23
Quoted 10 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +10
-0+* useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* You should have received a copy of the GNU General Public +* License along with this program; if not, write to the Free +* Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* The full GNU General Public License is included in this +* distribution in the file called COPYING. +*
Providing the FSF address is discouraged, and using it results in checkpatch errors.
Quoted 625 lines
drivers/watchdog/intel_scu_watchdog_evo.c new file +564
-0+*/ + +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt + +#include <linux/interrupt.h> +#include <linux/kernel_stat.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/nmi.h> +#include <linux/reboot.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <asm/intel-mid.h> +#include <asm/intel_scu_ipc.h> + +#include "intel_scu_watchdog_evo.h" + +/* Defines */ +#define STRING_RESET_TYPE_MAX_LEN 11 +#define STRING_COLD_OFF "COLD_OFF" +#define STRING_COLD_RESET "COLD_RESET" +#define STRING_COLD_BOOT "COLD_BOOT" + +#define EXT_TIMER0_MSI 15 + +#define IPC_WATCHDOG 0xf8 + +/* watchdog message options */ +enum { +SCU_WATCHDOG_START =0 , +SCU_WATCHDOG_STOP , +SCU_WATCHDOG_KEEPALIVE , +SCU_WATCHDOG_SET_ACTION_ON_TIMEOUT +}; + +/* watchdog reset options */ +enum { +SCU_COLD_OFF_ON_TIMEOUT =0 , +SCU_COLD_RESET_ON_TIMEOUT , +SCU_COLD_BOOT_ON_TIMEOUT , +SCU_DO_NOTHING_ON_TIMEOUT +}; + +/* Statics */ +static struct intel_scu_watchdog_dev watchdog_device; + +/* Module params */ +static bool disable_kernel_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" ); + +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" ); + +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" ); + +/* Setting reset_on_release will cause an immediate reset when the watchdog +* is released. If false, the watchdog timer is refreshed for one more +* interval. At the end of that interval, the watchdog timer will reset the +* system. +*/ +static bool reset_on_release = true; + +/* Check current timeouts */ +static int check_timeouts (int pre_timeout_time,int timeout_time) +{ +if (pre_timeout_time < timeout_time) +return 0 ; + +return -EINVAL ; +} + +/* Set the different timeouts needed by the SCU FW and start the +* kernel watchdog */ +static int watchdog_set_timeouts_and_start (int pretimeout, +int timeout) +{ +int ret, input_size; +struct ipc_wd_start { +u32 pretimeout; +u32 timeout; + } ipc_wd_start = { pretimeout, timeout }; + +/* SCU expects the input size for watchdog IPC to +* be based on double-word */ + input_size = (sizeof (ipc_wd_start) +3 ) /4 ; + + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_START , (u32 *)&ipc_wd_start, + input_size,NULL ,0 ); +if (ret) { +pr_crit ("Error configuring and starting watchdog: %d\n" , + ret); +return -EIO ; + } + +return 0 ; +} + +/* Provisioning function for future enhancement : allow to fine tune timing +according to watchdog action settings */ +static int watchdog_set_appropriate_timeouts (void ) +{ +pr_debug ("Setting shutdown timeouts\n" ); +return watchdog_set_timeouts_and_start (pre_timeout, timeout); +} + +/* Keep alive */ +static int watchdog_keepalive (void ) +{ +int ret; + +/* Pet the watchdog */ + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_KEEPALIVE ,NULL ,0 ,NULL ,0 ); +if (ret) { +pr_crit ("Error executing keepalive: %x\n" , ret); +return -EIO ; + } + +return 0 ; +} + +/* stops the timer */ +static int watchdog_stop (void ) +{ +int ret; + + watchdog_device.started = false; + + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_STOP ,NULL ,0 ,NULL ,0 ); +if (ret) { +pr_crit ("Error stopping watchdog: %x\n" , ret); +return -EIO ; + } +return 0 ; +} + +/* warning interrupt handler */ +static irqreturn_t watchdog_warning_interrupt (int irq,void *dev_id) +{ +pr_warn ("[SHTDWN] %s, WATCHDOG TIMEOUT!\n" , __func__); + +/* Let's reset the platform after dumping some data */ +trigger_all_cpu_backtrace (); +panic ("Kernel Watchdog" ); + +/* This code should not be reached */ +return IRQ_HANDLED ; +} + +/* Program and starts the timer */ +static int watchdog_config_and_start (u32 newtimeout,u32 newpretimeout) +{ +int ret; + + timeout = newtimeout; + pre_timeout = newpretimeout; + +pr_info ("timeout=%ds, pre_timeout=%ds\n" , timeout, pre_timeout); + +/* Configure the watchdog */ + ret =watchdog_set_timeouts_and_start (pre_timeout, timeout); +if (ret) { +pr_err ("%s: Cannot configure the watchdog\n" , __func__); + +/* Make sure the watchdog timer is stopped */ +watchdog_stop (); +return ret; + } + + watchdog_device.started = true; + +return 0 ; +} + +/* Open */ +static int intel_scu_open (struct inode *inode,struct file *file) +{ +/* Set flag to indicate that watchdog device is open */ +if (test_and_set_bit (0 , &watchdog_device.driver_open)) { +pr_err ("watchdog device is busy\n" ); +return -EBUSY ; + } + +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 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_err ("intel_scu_release, without open\n" ); +return -ENOTTY ; + } + +if (!watchdog_device.started) { +/* Just close, since timer has not been started */ +pr_err ("Closed, without starting timer\n" ); +return 0 ; + } + +/* The watchdog should not be closed after the timer is started */ +pr_crit ("Unexpected close of /dev/watchdog!\n" ); + +/* Refresh the timer for one more interval */ +watchdog_keepalive (); + +/* Reboot system if requested */ +if (reset_on_release) { +pr_crit ("Initiating system reboot.\n" ); +emergency_restart (); + } + +pr_crit ("Immediate Reboot Disabled\n" ); +pr_crit ("System will reset when watchdog timer expire!\n" ); + +return 0 ; +} + +/* Write */ +static ssize_t intel_scu_write (struct file *file,char const *data,size_t len, +loff_t *ppos) +{ +if (watchdog_device.shutdown_flag == true) +/* do nothing if we are shutting down */ +return len; + +if (watchdog_device.started) { +/* Watchdog already started, keep it alive */ +watchdog_keepalive (); + } + +return len; +} + +/* 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 val; +int options; + +static const struct watchdog_info ident = { + .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, +sizeof (ident)) ? -EFAULT :0 ; +case WDIOC_GETSTATUS : +case WDIOC_GETBOOTSTATUS : +return put_user (0 , p); +case WDIOC_KEEPALIVE : +if (!watchdog_device.started) +return -EINVAL ; + +watchdog_keepalive (); +return 0 ; +case WDIOC_SETPRETIMEOUT : +pr_info ("%s: setpretimeout ioctl\n" , __func__); + +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_info ("%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 (timeout, p); +case WDIOC_SETOPTIONS : +if (get_user (options, p)) +return -EFAULT ; + +if (options &WDIOS_DISABLECARD ) { +pr_info ("%s: Stopping the watchdog\n" , __func__); +watchdog_stop (); +return 0 ; + } + +if (options &WDIOS_ENABLECARD ) { +pr_info ("%s: Starting the watchdog\n" , __func__); + +if (watchdog_device.started) +return -EBUSY ; + +if (check_timeouts (pre_timeout, timeout)) { +pr_warn ("%s: Invalid thresholds\n" , + __func__); +return -EINVAL ; + } +if (watchdog_config_and_start (timeout, pre_timeout)) +return -EINVAL ; +return 0 ; + } +return 0 ; +default : +return -ENOTTY ; + } +} + +static int watchdog_set_reset_type (int reset_type) +{ +int ret, input_size; +u32 ipc_wd_on_timeout = reset_type; + +/* SCU expects the input size for watchdog IPC to +* be based on double-word */ + input_size = (sizeof (ipc_wd_on_timeout) +3 ) /4 ; + + ret =intel_scu_ipc_command (IPC_WATCHDOG , +SCU_WATCHDOG_SET_ACTION_ON_TIMEOUT , + (u32 *)&ipc_wd_on_timeout, + input_size,NULL ,0 ); +if (ret) { +pr_crit ("Error setting watchdog action: %d\n" , ret); +return -EIO ; + } + + watchdog_device.normal_wd_action = reset_type; + +return 0 ; +} + +/* Reboot notifier */ +static int reboot_notifier (struct notifier_block *this, +unsigned long code, +void *another_unused) +{ +int ret; + +if (code ==SYS_RESTART || code ==SYS_HALT || code ==SYS_POWER_OFF ) { +pr_warn ("Reboot notifier\n" ); + +if (watchdog_set_appropriate_timeouts ()) +pr_crit ("reboot notifier can't set time\n" ); + +switch (code) { +case SYS_RESTART : + ret =watchdog_set_reset_type ( + watchdog_device.reboot_wd_action); +break ; + +case SYS_HALT : +case SYS_POWER_OFF : + ret =watchdog_set_reset_type ( + watchdog_device.shutdown_wd_action); +break ; + } +if (ret) +pr_err ("%s: could not set reset type\n" , __func__); + +/* Don't do instant reset on close */ + reset_on_release = false; + +/* Kick once again */ +if (disable_kernel_watchdog == false) { + ret =watchdog_keepalive (); +if (ret) +pr_warn ("%s: no keep alive\n" , __func__); + +/* Don't allow any more keep-alives */ + watchdog_device.shutdown_flag = true; + } + } +return NOTIFY_DONE ; +} + + +/* Kernel Interfaces */ +static const struct file_operations intel_scu_fops = { + .owner =THIS_MODULE , + .llseek = no_llseek, + .write = intel_scu_write, + .unlocked_ioctl = intel_scu_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = intel_scu_ioctl, +#endif + .open = intel_scu_open, + .release = intel_scu_release, +}; + +static int handle_mrfl_dev_ioapic (int irq) +{ +int ioapic; +struct io_apic_irq_attr irq_attr; + + ioapic =mp_find_ioapic (irq); +if (ioapic >=0 ) { + irq_attr.ioapic = ioapic; + irq_attr.ioapic_pin = irq; + irq_attr.trigger =1 ; + irq_attr.polarity =0 ;/* Active high */ +io_apic_set_pci_routing (NULL , irq, &irq_attr); + }else { +pr_err ("cannot find interrupt %d in ioapic\n" , irq); +return -EINVAL ; + } + +return 0 ; +} + +/* Init code */ +static int __init intel_scu_watchdog_init (void ) +{ +int ret =0 ; + +/* +* This is only valid for Merrifield based platforms +*/ +if (intel_mid_identify_cpu () !=INTEL_MID_CPU_CHIP_TANGIER ) +return -ENODEV ; + + watchdog_device.normal_wd_action =SCU_COLD_RESET_ON_TIMEOUT ; + watchdog_device.reboot_wd_action =SCU_COLD_RESET_ON_TIMEOUT ; + watchdog_device.shutdown_wd_action =SCU_COLD_OFF_ON_TIMEOUT ; + +/* Initially, we are not in shutdown mode */ + watchdog_device.shutdown_flag = false; + +/* Check timeouts boot parameter */ +if (check_timeouts (pre_timeout, timeout)) { +pr_err ("%s: Invalid timeouts\n" , __func__); +return -EINVAL ; + } + +/* 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) { +pr_crit ("cannot register reboot notifier %d\n" , ret); + goto error_stop_timer; + } + +/* Do not publish the watchdog device when disabled */ +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) { +pr_crit ("Cannot register miscdev %d err =%d\n" , +WATCHDOG_MINOR , ret); + goto error_reboot_notifier; + } + } + +/* MSI #15 handler to dump registers */ +handle_mrfl_dev_ioapic (EXT_TIMER0_MSI ); + ret =request_irq ((unsigned int )EXT_TIMER0_MSI , + watchdog_warning_interrupt, +IRQF_SHARED |IRQF_NO_SUSPEND ,"watchdog" , + &watchdog_device); +if (ret) { +pr_err ("error requesting warning irq %d\n" , +EXT_TIMER0_MSI ); +pr_err ("error value returned is %d\n" , ret); + goto error_misc_register; + } + +if (disable_kernel_watchdog) { +pr_info ("%s: Disable kernel watchdog\n" , __func__); + +/* Make sure timer is stopped */ + ret =watchdog_stop (); +if (ret !=0 ) +pr_warn ("can't disable timer\n" ); + } + + + watchdog_device.started = false; + +return ret; + +error_misc_register: +misc_deregister (&watchdog_device.miscdev); + +error_reboot_notifier: +unregister_reboot_notifier (&watchdog_device.reboot_notifier); + +error_stop_timer: +watchdog_stop (); + +return ret; +} + +static void __exit intel_scu_watchdog_exit(void) +{ +int ret =0 ; + + ret =watchdog_stop (); +if (ret !=0 ) +pr_err ("cant disable timer\n" ); + +misc_deregister (&watchdog_device.miscdev); +unregister_reboot_notifier (&watchdog_device.reboot_notifier); +} + +#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" ); +MODULE_AUTHOR ("Gabriel Touzeau <[email protected]>" ); +MODULE_AUTHOR ("Jeremy Compostella <[email protected]" ); +MODULE_AUTHOR ("Eric Ernst <[email protected]>" ); +MODULE_DESCRIPTION ("Intel SCU Watchdog Device Driver for Merrifield platform" ); +MODULE_LICENSE ("GPL" ); +MODULE_ALIAS_MISCDEV (WATCHDOG_MINOR ); +MODULE_VERSION (WDT_VER );drivers/watchdog/intel_scu_watchdog_evo.h new file +54
-0@@ -0,0 +1,54 @@ +/* +* Intel_SCU 0.3: An Intel SCU IOH Based Watchdog Device +* for Intel's Tangier SoC / Merrifield platform +* +* Copyright (C) 2009-2013 Intel Corporation. All rights reserved. +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of version 2 of the GNU General +* Public License as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be +* useful, but WITHOUT ANY WARRANTY; without even the implied +* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +* PURPOSE. See the GNU General Public License for more details. +* You should have received a copy of the GNU General Public +* License along with this program; if not, write to the Free +* Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +* The full GNU General Public License is included in this +* distribution in the file called COPYING. +* +*/ + +#ifndef __INTEL_SCU_WATCHDOG_H +#define __INTEL_SCU_WATCHDOG_H + +#include <linux/miscdevice.h> +#include <linux/types.h> +#include <linux/notifier.h> + +#ifdef CONFIG_COMPAT +#include <linux/compat.h> +#endif + +#define PFX "intel_scu_watchdog: " +#define WDT_VER "0.3" + +#define DEFAULT_PRETIMEOUT 75 +#define DEFAULT_TIMEOUT 90 + +struct intel_scu_watchdog_dev { +ulong driver_open; +ulong driver_closed; +bool started; +struct notifier_block reboot_notifier; +struct miscdevice miscdev; +bool shutdown_flag; +int reset_type; +int normal_wd_action; +int reboot_wd_action; +int shutdown_wd_action; +}; + +#endif /* __INTEL_SCU_WATCHDOG_H */