summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJerry Hoemann <jerry.hoemann@hpe.com>2018-02-26 06:22:25 +0300
committerWim Van Sebroeck <wim@linux-watchdog.org>2018-03-03 19:04:18 +0300
commit0458f403fee348edd3457530dee57c0f946a0df6 (patch)
tree509d83c03a93305603aa1caf6996252450636050
parent703fc3df91eacdaee8bf63996fb3a76de55dbb5e (diff)
downloadlinux-0458f403fee348edd3457530dee57c0f946a0df6.tar.xz
watchdog: hpwdt: Programable Pretimeout NMI
Make whether or not the hpwdt watchdog delivers a pretimeout NMI programable by the user. The underlying iLO hardware is programmable as to whether or not a pre-timeout NMI is delivered to the system before the iLO resets the system. However, the iLO does not allow for programming the length of time that NMI is delivered before the system is reset. By watchdog API, in hpwdt_set_pretimeout a val == 0 disables the NMI. When val != 0, hpwdt_set_pretimeout will enable the pretimeout NMI provided the current timeout is greator than the HW specified pretimeout length. Otherwise an error is returned. In set_timeout, if the new timeout is <= an already established pretimeout, the pretimeout is canceled. This matches the action watchdog_set_timeout in the watchdog core would do if an hpwdt specific set_timeout function wasn't specified. Signed-off-by: Jerry Hoemann <jerry.hoemann@hpe.com> Signed-off-by: Guenter Roeck <linux@roeck-us.net> Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
-rw-r--r--drivers/watchdog/hpwdt.c53
1 files changed, 49 insertions, 4 deletions
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 68e84a212d00..b8205c6e61c1 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -31,11 +31,12 @@
#define TICKS_TO_SECS(ticks) ((ticks) * 128 / 1000)
#define HPWDT_MAX_TIMER TICKS_TO_SECS(65535)
#define DEFAULT_MARGIN 30
+#define PRETIMEOUT_SEC 9
static bool ilo5;
static unsigned int soft_margin = DEFAULT_MARGIN; /* in seconds */
-static unsigned int reload; /* the computed soft_margin */
static bool nowayout = WATCHDOG_NOWAYOUT;
+static bool pretimeout = IS_ENABLED(CONFIG_HPWDT_NMI_DECODING);
static void __iomem *pci_mem_addr; /* the PCI-memory address */
static unsigned long __iomem *hpwdt_nmistat;
@@ -55,10 +56,11 @@ MODULE_DEVICE_TABLE(pci, hpwdt_devices);
*/
static int hpwdt_start(struct watchdog_device *wdd)
{
- reload = SECS_TO_TICKS(wdd->timeout);
+ int control = 0x81 | (pretimeout ? 0x4 : 0);
+ int reload = SECS_TO_TICKS(wdd->timeout);
iowrite16(reload, hpwdt_timer_reg);
- iowrite8(0x85, hpwdt_timer_con);
+ iowrite8(control, hpwdt_timer_con);
return 0;
}
@@ -81,7 +83,10 @@ static int hpwdt_stop_core(struct watchdog_device *wdd)
static int hpwdt_ping(struct watchdog_device *wdd)
{
+ int reload = SECS_TO_TICKS(wdd->timeout);
+
iowrite16(reload, hpwdt_timer_reg);
+
return 0;
}
@@ -93,12 +98,37 @@ static unsigned int hpwdt_gettimeleft(struct watchdog_device *wdd)
static int hpwdt_settimeout(struct watchdog_device *wdd, unsigned int val)
{
wdd->timeout = val;
+ if (val <= wdd->pretimeout) {
+ wdd->pretimeout = 0;
+ pretimeout = 0;
+ if (watchdog_active(wdd))
+ hpwdt_start(wdd);
+ }
hpwdt_ping(wdd);
return 0;
}
#ifdef CONFIG_HPWDT_NMI_DECODING
+static int hpwdt_set_pretimeout(struct watchdog_device *wdd, unsigned int req)
+{
+ unsigned int val = 0;
+
+ if (req) {
+ val = PRETIMEOUT_SEC;
+ if (val >= wdd->timeout)
+ return -EINVAL;
+ }
+
+ wdd->pretimeout = val;
+ pretimeout = !!val;
+
+ if (watchdog_active(wdd))
+ hpwdt_start(wdd);
+
+ return 0;
+}
+
static int hpwdt_my_nmi(void)
{
return ioread8(hpwdt_nmistat) & 0x6;
@@ -121,6 +151,9 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
if (ilo5 && ulReason == NMI_UNKNOWN && mynmi)
return NMI_DONE;
+ if (ilo5 && !pretimeout)
+ return NMI_DONE;
+
hpwdt_stop();
hex_byte_pack(panic_msg, mynmi);
@@ -132,7 +165,8 @@ static int hpwdt_pretimeout(unsigned int ulReason, struct pt_regs *regs)
static const struct watchdog_info ident = {
- .options = WDIOF_SETTIMEOUT |
+ .options = WDIOF_PRETIMEOUT |
+ WDIOF_SETTIMEOUT |
WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.identity = "HPE iLO2+ HW Watchdog Timer",
@@ -149,6 +183,9 @@ static const struct watchdog_ops hpwdt_ops = {
.ping = hpwdt_ping,
.set_timeout = hpwdt_settimeout,
.get_timeleft = hpwdt_gettimeleft,
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ .set_pretimeout = hpwdt_set_pretimeout,
+#endif
};
static struct watchdog_device hpwdt_dev = {
@@ -157,6 +194,9 @@ static struct watchdog_device hpwdt_dev = {
.min_timeout = 1,
.max_timeout = HPWDT_MAX_TIMER,
.timeout = DEFAULT_MARGIN,
+#ifdef CONFIG_HPWDT_NMI_DECODING
+ .pretimeout = PRETIMEOUT_SEC,
+#endif
};
@@ -317,4 +357,9 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#ifdef CONFIG_HPWDT_NMI_DECODING
+module_param(pretimeout, bool, 0);
+MODULE_PARM_DESC(pretimeout, "Watchdog pretimeout enabled");
+#endif
+
module_pci_driver(hpwdt_driver);