2019-09-06 02:19:58

by Jorge Ramirez-Ortiz

[permalink] [raw]
Subject: [PATCH v3] watchdog: qcom: support pre-timeout when the bark irq is available

Use the bark interrupt as the pre-timeout notifier whenever this
interrupt is available.

By default, the pretimeout notification shall occur one second earlier
than the timeout.

Signed-off-by: Jorge Ramirez-Ortiz <[email protected]>
---
v3:
remove unnecesary variable added to private.

v2:
register the pre-timeout notifier instead.

v1:
use the bark irq to notify the user.

drivers/watchdog/qcom-wdt.c | 60 +++++++++++++++++++++++++++++++++----
1 file changed, 55 insertions(+), 5 deletions(-)

diff --git a/drivers/watchdog/qcom-wdt.c b/drivers/watchdog/qcom-wdt.c
index 7be7f87be28f..847dbf79df1c 100644
--- a/drivers/watchdog/qcom-wdt.c
+++ b/drivers/watchdog/qcom-wdt.c
@@ -10,6 +10,8 @@
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/watchdog.h>

enum wdt_reg {
WDT_RST,
@@ -54,15 +56,37 @@ struct qcom_wdt *to_qcom_wdt(struct watchdog_device *wdd)
return container_of(wdd, struct qcom_wdt, wdd);
}

+static inline int qcom_wdt_enable(struct watchdog_device *wdd)
+{
+ /* enable the bark interrupt */
+ if (wdd->info->options & WDIOF_PRETIMEOUT)
+ return 3;
+
+ return 1;
+}
+
+static irqreturn_t qcom_wdt_irq(int irq, void *cookie)
+{
+ struct watchdog_device *wdd = (struct watchdog_device *) cookie;
+
+ watchdog_notify_pretimeout(wdd);
+
+ return IRQ_HANDLED;
+}
+
static int qcom_wdt_start(struct watchdog_device *wdd)
{
struct qcom_wdt *wdt = to_qcom_wdt(wdd);
+ unsigned int bark = wdd->pretimeout;
+
+ if (!(wdd->info->options & WDIOF_PRETIMEOUT))
+ bark = wdd->timeout;

writel(0, wdt_addr(wdt, WDT_EN));
writel(1, wdt_addr(wdt, WDT_RST));
- writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
+ writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
- writel(1, wdt_addr(wdt, WDT_EN));
+ writel(qcom_wdt_enable(wdd), wdt_addr(wdt, WDT_EN));
return 0;
}

@@ -89,6 +113,14 @@ static int qcom_wdt_set_timeout(struct watchdog_device *wdd,
return qcom_wdt_start(wdd);
}

+static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd,
+ unsigned int timeout)
+{
+ wdd->pretimeout = timeout;
+
+ return 0;
+}
+
static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
void *data)
{
@@ -105,7 +137,7 @@ static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
writel(1, wdt_addr(wdt, WDT_RST));
writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
- writel(1, wdt_addr(wdt, WDT_EN));
+ writel(qcom_wdt_enable(wdd), wdt_addr(wdt, WDT_EN));

/*
* Actually make sure the above sequence hits hardware before sleeping.
@@ -121,11 +153,12 @@ static const struct watchdog_ops qcom_wdt_ops = {
.stop = qcom_wdt_stop,
.ping = qcom_wdt_ping,
.set_timeout = qcom_wdt_set_timeout,
+ .set_pretimeout = qcom_wdt_set_pretimeout,
.restart = qcom_wdt_restart,
.owner = THIS_MODULE,
};

-static const struct watchdog_info qcom_wdt_info = {
+static struct watchdog_info qcom_wdt_info = {
.options = WDIOF_KEEPALIVEPING
| WDIOF_MAGICCLOSE
| WDIOF_SETTIMEOUT
@@ -146,7 +179,7 @@ static int qcom_wdt_probe(struct platform_device *pdev)
struct device_node *np = dev->of_node;
const u32 *regs;
u32 percpu_offset;
- int ret;
+ int irq, ret;

regs = of_device_get_match_data(dev);
if (!regs) {
@@ -222,6 +255,23 @@ static int qcom_wdt_probe(struct platform_device *pdev)
wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U);
watchdog_init_timeout(&wdt->wdd, 0, dev);

+ irq = platform_get_irq(pdev, 0);
+ if (irq >= 0) {
+ /* enable the pre-timeout notification */
+ qcom_wdt_info.options |= WDIOF_PRETIMEOUT;
+
+ ret = devm_request_irq(&pdev->dev, irq, qcom_wdt_irq,
+ IRQF_TRIGGER_RISING, "wdog_bark",
+ &wdt->wdd);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request irq\n");
+ return ret;
+ }
+ }
+
+ if (qcom_wdt_info.options & WDIOF_PRETIMEOUT)
+ wdt->wdd.pretimeout = wdt->wdd.timeout - 1;
+
ret = devm_watchdog_register_device(dev, &wdt->wdd);
if (ret)
return ret;
--
2.23.0