]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
watchdog: qcom: add support to get the bootstatus from IMEM
authorKathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
Wed, 8 Apr 2026 09:24:38 +0000 (14:54 +0530)
committerGuenter Roeck <linux@roeck-us.net>
Sun, 3 May 2026 15:35:52 +0000 (08:35 -0700)
When the system boots up after a watchdog reset, the EXPIRED_STATUS bit
in the WDT_STS register is cleared. To identify if the system was
restarted due to WDT expiry, XBL update the information in the IMEM region.
Update the driver to read the restart reason from IMEM and populate the
bootstatus accordingly.

With the CONFIG_WATCHDOG_SYSFS enabled, user can extract the information
as below:

cat /sys/devices/platform/soc@0/f410000.watchdog/watchdog/watchdog0/bootstatus
32

For backward compatibility, keep the EXPIRED_STATUS bit check. Add a new
function qcom_wdt_get_bootstatus() to read the restart reason from
IMEM.

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Kathiravan Thirumoorthy <kathiravan.thirumoorthy@oss.qualcomm.com>
Link: https://lore.kernel.org/r/20260408-wdt_reset_reason-v10-2-caf66786329f@oss.qualcomm.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
drivers/watchdog/qcom-wdt.c

index dfaac5995c84c1f377023e6e62770c5548528a4c..49bd04841f0c8c4c5eb4e6b3a4864586aa9dbf5f 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/platform_device.h>
 #include <linux/watchdog.h>
 
@@ -42,6 +43,7 @@ struct qcom_wdt_match_data {
        const u32 *offset;
        bool pretimeout;
        u32 max_tick_count;
+       u32 wdt_reason_val;
 };
 
 struct qcom_wdt {
@@ -185,6 +187,7 @@ static const struct qcom_wdt_match_data match_data_ipq5424 = {
        .offset = reg_offset_data_kpss,
        .pretimeout = true,
        .max_tick_count = 0xFFFFFU,
+       .wdt_reason_val = 5,
 };
 
 static const struct qcom_wdt_match_data match_data_kpss = {
@@ -193,6 +196,40 @@ static const struct qcom_wdt_match_data match_data_kpss = {
        .max_tick_count = 0xFFFFFU,
 };
 
+static int qcom_wdt_get_bootstatus(struct device *dev, struct qcom_wdt *wdt,
+                                  u32 val)
+{
+       struct device_node *imem;
+       struct resource res;
+       void __iomem *addr;
+       int ret;
+
+       imem = of_parse_phandle(dev->of_node, "sram", 0);
+       if (!imem) {
+               /* Read the EXPIRED_STATUS bit as a fallback */
+               if (readl(wdt_addr(wdt, WDT_STS)) & 1)
+                       wdt->wdd.bootstatus = WDIOF_CARDRESET;
+
+               return 0;
+       }
+
+       ret = of_address_to_resource(imem, 0, &res);
+       of_node_put(imem);
+       if (ret)
+               return ret;
+
+       addr = ioremap(res.start, resource_size(&res));
+       if (!addr)
+               return -ENOMEM;
+
+       if (readl(addr) == val)
+               wdt->wdd.bootstatus = WDIOF_CARDRESET;
+
+       iounmap(addr);
+
+       return 0;
+}
+
 static int qcom_wdt_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -273,8 +310,9 @@ static int qcom_wdt_probe(struct platform_device *pdev)
        wdt->wdd.parent = dev;
        wdt->layout = data->offset;
 
-       if (readl(wdt_addr(wdt, WDT_STS)) & 1)
-               wdt->wdd.bootstatus = WDIOF_CARDRESET;
+       ret = qcom_wdt_get_bootstatus(dev, wdt, data->wdt_reason_val);
+       if (ret)
+               dev_err(dev, "failed to get the bootstatus, %d\n", ret);
 
        /*
         * If 'timeout-sec' unspecified in devicetree, assume a 30 second