This device doesn't provide any information about boot status.
As workaround we use a persitent bit to track watchdog activity.

Signed-off-by: Harald Geyer <[email protected]>
---
Changes since v2:
* make code ordering more consistent
* move part of the commit message to a code comment
* rewrite the commit message

Changes since v1:
* make code formatting more consistent with the rest of the driver
* Cc some people who might have better documentation then I do

Changes since initially posting this patch on 08/04/2015:
* fix a spelling error in the commit message
* rebase to a recent version

 drivers/rtc/rtc-stmp3xxx.c          |   37 +++++++++++++++++++++++++++++++++++
 drivers/watchdog/stmp3xxx_rtc_wdt.c |    6 +++++-
 include/linux/stmp3xxx_rtc_wdt.h    |    2 ++
 3 files changed, 44 insertions(+), 1 deletion(-)

diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c
index ca54d03..47947ea 100644
--- a/drivers/rtc/rtc-stmp3xxx.c
+++ b/drivers/rtc/rtc-stmp3xxx.c
@@ -30,6 +30,7 @@
 #include <linux/of.h>
 #include <linux/stmp_device.h>
 #include <linux/stmp3xxx_rtc_wdt.h>
+#include <linux/watchdog.h>
 
 #define STMP3XXX_RTC_CTRL                      0x0
 #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN         0x00000001
@@ -62,6 +63,9 @@
 /* missing bitmask in headers */
 #define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER 0x80000000
 
+#define STMP3XXX_RTC_PERSISTENT2               0x80
+#define STMP3XXX_RTC_PERSISTENT2_WDT_ACTIVE    0x00000001
+
 struct stmp3xxx_rtc_data {
        struct rtc_device *rtc;
        void __iomem *io;
@@ -81,6 +85,20 @@ struct stmp3xxx_rtc_data {
  * The watchdog driver is passed the below accessor function via platform_data
  * to configure the watchdog. Locking is not needed because accessing SET/CLR
  * registers is atomic.
+ *
+ * Since this device doesn't report the cause of the last reset, we use
+ * a persistent bit to track watchdog activity. The code from the Freescale
+ * BSP uses the STMP3XXX_RTC_PERSISTENT1 register, which is dedicated to
+ * controlling the boot ROM, for this purpose. However it seems the bit
+ * there can't be cleared once it has been set. So we are using
+ * STMP3XXX_RTC_PERSISTENT2 instead, which is the first register available
+ * for "software use" without restriction.
+ *
+ * I (Harald Geyer <[email protected]>) don't know if the code touching the
+ * STMP3XXX_RTC_PERSISTENT1 register is doing anything useful. Maybe this
+ * is just a leftover from the BSP code, but then maybe there is something
+ * in the boot ROM or in some bootloader that is using this. Hard to tell
+ * without proper documentation about this register.
  */
 
 static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout)
@@ -93,16 +111,30 @@ static void stmp3xxx_wdt_set_timeout(struct device *dev, 
u32 timeout)
                       rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET);
                writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
                       rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + 
STMP_OFFSET_REG_SET);
+               writel(STMP3XXX_RTC_PERSISTENT2_WDT_ACTIVE,
+                      rtc_data->io + STMP3XXX_RTC_PERSISTENT2 + 
STMP_OFFSET_REG_SET);
        } else {
                writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
                       rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR);
                writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
                       rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + 
STMP_OFFSET_REG_CLR);
+               writel(STMP3XXX_RTC_PERSISTENT2_WDT_ACTIVE,
+                      rtc_data->io + STMP3XXX_RTC_PERSISTENT2 + 
STMP_OFFSET_REG_CLR);
        }
 }
 
+static void stmp3xxx_wdt_clear_bootstatus(struct device *dev)
+{
+       struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
+
+       writel(STMP3XXX_RTC_PERSISTENT2_WDT_ACTIVE,
+              rtc_data->io + STMP3XXX_RTC_PERSISTENT2 + STMP_OFFSET_REG_CLR);
+}
+
 static struct stmp3xxx_wdt_pdata wdt_pdata = {
        .wdt_set_timeout = stmp3xxx_wdt_set_timeout,
+       .wdt_clear_bootstatus = stmp3xxx_wdt_clear_bootstatus,
+       .bootstatus = 0,
 };
 
 static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
@@ -110,6 +142,8 @@ static void stmp3xxx_wdt_register(struct platform_device 
*rtc_pdev)
        struct platform_device *wdt_pdev =
                platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id);
 
+       stmp3xxx_wdt_clear_bootstatus(&rtc_pdev->dev);
+
        if (wdt_pdev) {
                wdt_pdev->dev.parent = &rtc_pdev->dev;
                wdt_pdev->dev.platform_data = &wdt_pdata;
@@ -357,6 +391,9 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
                return err;
        }
 
+       if (readl(STMP3XXX_RTC_PERSISTENT2 + rtc_data->io) &
+                        STMP3XXX_RTC_PERSISTENT2_WDT_ACTIVE)
+               wdt_pdata.bootstatus |= WDIOF_CARDRESET;
        stmp3xxx_wdt_register(pdev);
        return 0;
 }
diff --git a/drivers/watchdog/stmp3xxx_rtc_wdt.c 
b/drivers/watchdog/stmp3xxx_rtc_wdt.c
index e09a01f..7609f78 100644
--- a/drivers/watchdog/stmp3xxx_rtc_wdt.c
+++ b/drivers/watchdog/stmp3xxx_rtc_wdt.c
@@ -52,7 +52,8 @@ static int wdt_set_timeout(struct watchdog_device *wdd, 
unsigned new_timeout)
 }
 
 static const struct watchdog_info stmp3xxx_wdt_ident = {
-       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+       .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
+               WDIOF_CARDRESET,
        .identity = "STMP3XXX RTC Watchdog",
 };
 
@@ -79,6 +80,7 @@ static int wdt_notify_sys(struct notifier_block *nb, unsigned 
long code,
 
        switch (code) {
        case SYS_DOWN:  /* keep enabled, system might crash while going down */
+               pdata->wdt_clear_bootstatus(dev->parent);
                break;
        case SYS_HALT:  /* allow the system to actually halt */
        case SYS_POWER_OFF:
@@ -95,12 +97,14 @@ static struct notifier_block wdt_notifier = {
 
 static int stmp3xxx_wdt_probe(struct platform_device *pdev)
 {
+       struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(&pdev->dev);
        int ret;
 
        watchdog_set_drvdata(&stmp3xxx_wdd, &pdev->dev);
 
        stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, 
STMP3XXX_MAX_TIMEOUT);
        stmp3xxx_wdd.parent = &pdev->dev;
+       stmp3xxx_wdd.bootstatus = pdata->bootstatus;
 
        ret = watchdog_register_device(&stmp3xxx_wdd);
        if (ret < 0) {
diff --git a/include/linux/stmp3xxx_rtc_wdt.h b/include/linux/stmp3xxx_rtc_wdt.h
index 1dd12c9..62dd9e6 100644
--- a/include/linux/stmp3xxx_rtc_wdt.h
+++ b/include/linux/stmp3xxx_rtc_wdt.h
@@ -10,6 +10,8 @@
 
 struct stmp3xxx_wdt_pdata {
        void (*wdt_set_timeout)(struct device *dev, u32 timeout);
+       void (*wdt_clear_bootstatus)(struct device *dev);
+       unsigned int bootstatus;
 };
 
 #endif /* __LINUX_STMP3XXX_RTC_WDT_H */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to