From 816981102c4237b5a40746c6a2d0fa3389584ce0 Mon Sep 17 00:00:00 2001 From: Jaehyoung Choi Date: Thu, 14 Sep 2017 17:55:52 +0900 Subject: [PATCH] [COMMON] watchdog: Add watchdog reset confirm function. Currently mainline code does not support functions to check whether watchdog works in real time. The reset_confirm function checks whether the watchdog timer actually generates a reset. Change-Id: I569ab2f910b63160753838801d3c0b03f5a1a46d Signed-off-by: Jaehyoung Choi --- drivers/watchdog/s3c2410_wdt.c | 49 +++++++++++++++++++++++++++++++++ drivers/watchdog/watchdog_dev.c | 18 ++++++++++++ include/linux/watchdog.h | 1 + 3 files changed, 68 insertions(+) diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 90b1c1d3c43d..f4caf1989b97 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -433,6 +433,53 @@ static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action, return 0; } +inline void s3c2410wdt_reset_confirm(struct watchdog_device *wdd) +{ + struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); + unsigned int wtcon, wtdat, wtcnt, disable_reg, mask_reset_reg; + unsigned long total_time = 0; + int ret; + + if (!wdt) + return; + + wtcon = readl(wdt->reg_base + S3C2410_WTCON); + + dev_info(wdt->dev, "Current Little_cluster watchdog %sable, wtcon = %x\n", + (wtcon & S3C2410_WTCON_ENABLE) ? "en" : "dis", wtcon); + + ret = regmap_read(wdt->pmureg, wdt->drv_data->mask_reset_reg, &mask_reset_reg); + if (ret) { + dev_err(wdt->dev, "Couldn't get MASK_WDT_RESET register\n"); + return; + } + + ret = regmap_read(wdt->pmureg, wdt->drv_data->disable_reg, &disable_reg); + if (ret) { + dev_err(wdt->dev, "Couldn't get DISABLE_WDT register\n"); + return; + } + + /* Fake watchdog bits in both registers must be cleared. */ + dev_info(wdt->dev, "DISABLE_WDT reg: %x, MASK_WDT_RESET reg: %x\n", disable_reg, mask_reset_reg); + + /* If watchdog is disabled, do not print wtcnt value. */ + if (!(wtcon & S3C2410_WTCON_ENABLE)) + return; + + do { + /* It continues to print the wtcnt and wddat values + * until watchdog reset is taken. + */ + wtdat = readl(wdt->reg_base + S3C2410_WTDAT); + wtcnt = readl(wdt->reg_base + S3C2410_WTCNT); + dev_info(wdt->dev, "%lu milliseconds, wtdat = %u, wtcnt = %u", + total_time, wtdat, wtcnt); + total_time += 500; + mdelay(500); + } while (total_time < (wdd->timeout * 1000)); +} + #define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE) static const struct watchdog_info s3c2410_wdt_ident = { @@ -448,6 +495,7 @@ static const struct watchdog_ops s3c2410wdt_ops = { .ping = s3c2410wdt_keepalive, .set_timeout = s3c2410wdt_set_heartbeat, .restart = s3c2410wdt_restart, + .reset_confirm = s3c2410wdt_reset_confirm, }; static const struct watchdog_device s3c2410_wdd = { @@ -657,6 +705,7 @@ inline int s3c2410wdt_set_emergency_reset(unsigned int timeout_cnt, int index) return 0; } + #endif #ifdef CONFIG_PM_SLEEP diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c index b30fb637ae94..dd69fd69f99b 100644 --- a/drivers/watchdog/watchdog_dev.c +++ b/drivers/watchdog/watchdog_dev.c @@ -527,6 +527,23 @@ static ssize_t pretimeout_governor_store(struct device *dev, } static DEVICE_ATTR_RW(pretimeout_governor); +static ssize_t reset_confirm_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + int ret = watchdog_start(wdd); + + if (ret) + return ret; + + if (wdd->ops->reset_confirm) + wdd->ops->reset_confirm(wdd); + + return sprintf(buf, "watchdog reset failed..\n"); +} +static DEVICE_ATTR_RO(reset_confirm); + static umode_t wdt_is_visible(struct kobject *kobj, struct attribute *attr, int n) { @@ -558,6 +575,7 @@ static struct attribute *wdt_attrs[] = { &dev_attr_nowayout.attr, &dev_attr_pretimeout_governor.attr, &dev_attr_pretimeout_available_governors.attr, + &dev_attr_reset_confirm.attr, NULL, }; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 44985c4a1e86..6099de359a18 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -53,6 +53,7 @@ struct watchdog_ops { unsigned int (*get_timeleft)(struct watchdog_device *); int (*restart)(struct watchdog_device *, unsigned long, void *); long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); + void (*reset_confirm)(struct watchdog_device *); }; /** struct watchdog_device - The structure that defines a watchdog device -- 2.20.1