Commit 9ec0b7e0 authored by Eddie James's avatar Eddie James Committed by Wim Van Sebroeck
Browse files

watchdog: aspeed: Enable pre-timeout interrupt



Enable the core pre-timeout interrupt on AST2500 and AST2600.

Signed-off-by: default avatarEddie James <eajames@linux.ibm.com>
Reviewed-by: default avatarGuenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20221101205338.577427-2-eajames@linux.ibm.com


Signed-off-by: default avatarGuenter Roeck <linux@roeck-us.net>
Signed-off-by: default avatarWim Van Sebroeck <wim@linux-watchdog.org>
parent ef9b7bf5
Loading
Loading
Loading
Loading
+88 −16
Original line number Diff line number Diff line
@@ -5,11 +5,14 @@
 * Joel Stanley <joel@jms.id.au>
 */

#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>

@@ -18,28 +21,41 @@ module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");

struct aspeed_wdt_config {
	u32 ext_pulse_width_mask;
	u32 irq_shift;
	u32 irq_mask;
};

struct aspeed_wdt {
	struct watchdog_device	wdd;
	void __iomem		*base;
	u32			ctrl;
};

struct aspeed_wdt_config {
	u32 ext_pulse_width_mask;
	const struct aspeed_wdt_config *cfg;
};

static const struct aspeed_wdt_config ast2400_config = {
	.ext_pulse_width_mask = 0xff,
	.irq_shift = 0,
	.irq_mask = 0,
};

static const struct aspeed_wdt_config ast2500_config = {
	.ext_pulse_width_mask = 0xfffff,
	.irq_shift = 12,
	.irq_mask = GENMASK(31, 12),
};

static const struct aspeed_wdt_config ast2600_config = {
	.ext_pulse_width_mask = 0xfffff,
	.irq_shift = 0,
	.irq_mask = GENMASK(31, 10),
};

static const struct of_device_id aspeed_wdt_of_table[] = {
	{ .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
	{ .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
	{ .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
	{ .compatible = "aspeed,ast2600-wdt", .data = &ast2600_config },
	{ },
};
MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
#define   WDT_CTRL_RESET_SYSTEM		BIT(1)
#define   WDT_CTRL_ENABLE		BIT(0)
#define WDT_TIMEOUT_STATUS	0x10
#define   WDT_TIMEOUT_STATUS_IRQ		BIT(2)
#define   WDT_TIMEOUT_STATUS_BOOT_SECONDARY	BIT(1)
#define WDT_CLEAR_TIMEOUT_STATUS	0x14
#define   WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION	BIT(0)
@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
	return 0;
}

static int aspeed_wdt_set_pretimeout(struct watchdog_device *wdd,
				     unsigned int pretimeout)
{
	struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
	u32 actual = pretimeout * WDT_RATE_1MHZ;
	u32 s = wdt->cfg->irq_shift;
	u32 m = wdt->cfg->irq_mask;

	wdd->pretimeout = pretimeout;
	wdt->ctrl &= ~m;
	if (pretimeout)
		wdt->ctrl |= ((actual << s) & m) | WDT_CTRL_WDT_INTR;
	else
		wdt->ctrl &= ~WDT_CTRL_WDT_INTR;

	writel(wdt->ctrl, wdt->base + WDT_CTRL);

	return 0;
}

static int aspeed_wdt_restart(struct watchdog_device *wdd,
			      unsigned long action, void *data)
{
@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = {
	.stop		= aspeed_wdt_stop,
	.ping		= aspeed_wdt_ping,
	.set_timeout	= aspeed_wdt_set_timeout,
	.set_pretimeout = aspeed_wdt_set_pretimeout,
	.restart	= aspeed_wdt_restart,
	.owner		= THIS_MODULE,
};
@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = {
	.identity	= KBUILD_MODNAME,
};

static const struct watchdog_info aspeed_wdt_pretimeout_info = {
	.options	= WDIOF_KEEPALIVEPING
			| WDIOF_PRETIMEOUT
			| WDIOF_MAGICCLOSE
			| WDIOF_SETTIMEOUT,
	.identity	= KBUILD_MODNAME,
};

static irqreturn_t aspeed_wdt_irq(int irq, void *arg)
{
	struct watchdog_device *wdd = arg;
	struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
	u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);

	if (status & WDT_TIMEOUT_STATUS_IRQ)
		watchdog_notify_pretimeout(wdd);

	return IRQ_HANDLED;
}

static int aspeed_wdt_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	const struct aspeed_wdt_config *config;
	const struct of_device_id *ofdid;
	struct aspeed_wdt *wdt;
	struct device_node *np;
@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
	if (!wdt)
		return -ENOMEM;

	np = dev->of_node;

	ofdid = of_match_node(aspeed_wdt_of_table, np);
	if (!ofdid)
		return -EINVAL;
	wdt->cfg = ofdid->data;

	wdt->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(wdt->base))
		return PTR_ERR(wdt->base);

	wdt->wdd.info = &aspeed_wdt_info;

	if (wdt->cfg->irq_mask) {
		int irq = platform_get_irq_optional(pdev, 0);

		if (irq > 0) {
			ret = devm_request_irq(dev, irq, aspeed_wdt_irq,
					       IRQF_SHARED, dev_name(dev),
					       wdt);
			if (ret)
				return ret;

			wdt->wdd.info = &aspeed_wdt_pretimeout_info;
		}
	}

	wdt->wdd.ops = &aspeed_wdt_ops;
	wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
	wdt->wdd.parent = dev;
@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)

	watchdog_set_nowayout(&wdt->wdd, nowayout);

	np = dev->of_node;

	ofdid = of_match_node(aspeed_wdt_of_table, np);
	if (!ofdid)
		return -EINVAL;
	config = ofdid->data;

	/*
	 * On clock rates:
	 *  - ast2400 wdt can run at PCLK, or 1MHz
@@ -331,7 +403,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
		(of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
		u32 reg = readl(wdt->base + WDT_RESET_WIDTH);

		reg &= config->ext_pulse_width_mask;
		reg &= wdt->cfg->ext_pulse_width_mask;
		if (of_property_read_bool(np, "aspeed,ext-active-high"))
			reg |= WDT_ACTIVE_HIGH_MAGIC;
		else
@@ -339,7 +411,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)

		writel(reg, wdt->base + WDT_RESET_WIDTH);

		reg &= config->ext_pulse_width_mask;
		reg &= wdt->cfg->ext_pulse_width_mask;
		if (of_property_read_bool(np, "aspeed,ext-push-pull"))
			reg |= WDT_PUSH_PULL_MAGIC;
		else
@@ -349,7 +421,7 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
	}

	if (!of_property_read_u32(np, "aspeed,ext-pulse-duration", &duration)) {
		u32 max_duration = config->ext_pulse_width_mask + 1;
		u32 max_duration = wdt->cfg->ext_pulse_width_mask + 1;

		if (duration == 0 || duration > max_duration) {
			dev_err(dev, "Invalid pulse duration: %uus\n",