[COMMON] watchdog: Add watchdog reset confirm function.
authorJaehyoung Choi <jkkkkk.choi@samsung.com>
Thu, 14 Sep 2017 08:55:52 +0000 (17:55 +0900)
committerJaehyoung Choi <jkkkkk.choi@samsung.com>
Fri, 11 May 2018 01:20:30 +0000 (10:20 +0900)
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 <jkkkkk.choi@samsung.com>
drivers/watchdog/s3c2410_wdt.c
drivers/watchdog/watchdog_dev.c
include/linux/watchdog.h

index 9fdc5da2ccc18aa838dfab9c5561205e35b43290..5ca05e078532bc5ce2f1516f02779915482075a5 100644 (file)
@@ -506,6 +506,53 @@ static int s3c2410wdt_restart(struct watchdog_device *wdd, unsigned long action,
        return 0;
 }
 
+inline void s3c2410wdt_sysfs_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 = {
@@ -521,6 +568,7 @@ static const struct watchdog_ops s3c2410wdt_ops = {
        .ping = s3c2410wdt_keepalive,
        .set_timeout = s3c2410wdt_set_heartbeat,
        .restart = s3c2410wdt_restart,
+       .reset_confirm = s3c2410wdt_sysfs_reset_confirm,
 };
 
 static const struct watchdog_device s3c2410_wdd = {
index b30fb637ae947885e04eba1819e7d8cd145764d1..dd69fd69f99baa65041b51139986dce313be6ba4 100644 (file)
@@ -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,
 };
 
index 44985c4a1e86214dca6579b42bda3c6d666bcdc9..6099de359a18c5fea15b6f41a6644a866fe6a7f3 100644 (file)
@@ -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