Skip to content

Commit 9ec0b7e

Browse files
Eddie JamesWim Van Sebroeck
authored andcommitted
watchdog: aspeed: Enable pre-timeout interrupt
Enable the core pre-timeout interrupt on AST2500 and AST2600. Signed-off-by: Eddie James <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent ef9b7bf commit 9ec0b7e

File tree

1 file changed

+88
-16
lines changed

1 file changed

+88
-16
lines changed

drivers/watchdog/aspeed_wdt.c

Lines changed: 88 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@
55
* Joel Stanley <[email protected]>
66
*/
77

8+
#include <linux/bits.h>
89
#include <linux/delay.h>
10+
#include <linux/interrupt.h>
911
#include <linux/io.h>
1012
#include <linux/kernel.h>
1113
#include <linux/module.h>
1214
#include <linux/of.h>
15+
#include <linux/of_irq.h>
1316
#include <linux/platform_device.h>
1417
#include <linux/watchdog.h>
1518

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

24+
struct aspeed_wdt_config {
25+
u32 ext_pulse_width_mask;
26+
u32 irq_shift;
27+
u32 irq_mask;
28+
};
29+
2130
struct aspeed_wdt {
2231
struct watchdog_device wdd;
2332
void __iomem *base;
2433
u32 ctrl;
25-
};
26-
27-
struct aspeed_wdt_config {
28-
u32 ext_pulse_width_mask;
34+
const struct aspeed_wdt_config *cfg;
2935
};
3036

3137
static const struct aspeed_wdt_config ast2400_config = {
3238
.ext_pulse_width_mask = 0xff,
39+
.irq_shift = 0,
40+
.irq_mask = 0,
3341
};
3442

3543
static const struct aspeed_wdt_config ast2500_config = {
3644
.ext_pulse_width_mask = 0xfffff,
45+
.irq_shift = 12,
46+
.irq_mask = GENMASK(31, 12),
47+
};
48+
49+
static const struct aspeed_wdt_config ast2600_config = {
50+
.ext_pulse_width_mask = 0xfffff,
51+
.irq_shift = 0,
52+
.irq_mask = GENMASK(31, 10),
3753
};
3854

3955
static const struct of_device_id aspeed_wdt_of_table[] = {
4056
{ .compatible = "aspeed,ast2400-wdt", .data = &ast2400_config },
4157
{ .compatible = "aspeed,ast2500-wdt", .data = &ast2500_config },
42-
{ .compatible = "aspeed,ast2600-wdt", .data = &ast2500_config },
58+
{ .compatible = "aspeed,ast2600-wdt", .data = &ast2600_config },
4359
{ },
4460
};
4561
MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
@@ -58,6 +74,7 @@ MODULE_DEVICE_TABLE(of, aspeed_wdt_of_table);
5874
#define WDT_CTRL_RESET_SYSTEM BIT(1)
5975
#define WDT_CTRL_ENABLE BIT(0)
6076
#define WDT_TIMEOUT_STATUS 0x10
77+
#define WDT_TIMEOUT_STATUS_IRQ BIT(2)
6178
#define WDT_TIMEOUT_STATUS_BOOT_SECONDARY BIT(1)
6279
#define WDT_CLEAR_TIMEOUT_STATUS 0x14
6380
#define WDT_CLEAR_TIMEOUT_AND_BOOT_CODE_SELECTION BIT(0)
@@ -160,6 +177,26 @@ static int aspeed_wdt_set_timeout(struct watchdog_device *wdd,
160177
return 0;
161178
}
162179

180+
static int aspeed_wdt_set_pretimeout(struct watchdog_device *wdd,
181+
unsigned int pretimeout)
182+
{
183+
struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
184+
u32 actual = pretimeout * WDT_RATE_1MHZ;
185+
u32 s = wdt->cfg->irq_shift;
186+
u32 m = wdt->cfg->irq_mask;
187+
188+
wdd->pretimeout = pretimeout;
189+
wdt->ctrl &= ~m;
190+
if (pretimeout)
191+
wdt->ctrl |= ((actual << s) & m) | WDT_CTRL_WDT_INTR;
192+
else
193+
wdt->ctrl &= ~WDT_CTRL_WDT_INTR;
194+
195+
writel(wdt->ctrl, wdt->base + WDT_CTRL);
196+
197+
return 0;
198+
}
199+
163200
static int aspeed_wdt_restart(struct watchdog_device *wdd,
164201
unsigned long action, void *data)
165202
{
@@ -232,6 +269,7 @@ static const struct watchdog_ops aspeed_wdt_ops = {
232269
.stop = aspeed_wdt_stop,
233270
.ping = aspeed_wdt_ping,
234271
.set_timeout = aspeed_wdt_set_timeout,
272+
.set_pretimeout = aspeed_wdt_set_pretimeout,
235273
.restart = aspeed_wdt_restart,
236274
.owner = THIS_MODULE,
237275
};
@@ -243,10 +281,29 @@ static const struct watchdog_info aspeed_wdt_info = {
243281
.identity = KBUILD_MODNAME,
244282
};
245283

284+
static const struct watchdog_info aspeed_wdt_pretimeout_info = {
285+
.options = WDIOF_KEEPALIVEPING
286+
| WDIOF_PRETIMEOUT
287+
| WDIOF_MAGICCLOSE
288+
| WDIOF_SETTIMEOUT,
289+
.identity = KBUILD_MODNAME,
290+
};
291+
292+
static irqreturn_t aspeed_wdt_irq(int irq, void *arg)
293+
{
294+
struct watchdog_device *wdd = arg;
295+
struct aspeed_wdt *wdt = to_aspeed_wdt(wdd);
296+
u32 status = readl(wdt->base + WDT_TIMEOUT_STATUS);
297+
298+
if (status & WDT_TIMEOUT_STATUS_IRQ)
299+
watchdog_notify_pretimeout(wdd);
300+
301+
return IRQ_HANDLED;
302+
}
303+
246304
static int aspeed_wdt_probe(struct platform_device *pdev)
247305
{
248306
struct device *dev = &pdev->dev;
249-
const struct aspeed_wdt_config *config;
250307
const struct of_device_id *ofdid;
251308
struct aspeed_wdt *wdt;
252309
struct device_node *np;
@@ -259,11 +316,33 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
259316
if (!wdt)
260317
return -ENOMEM;
261318

319+
np = dev->of_node;
320+
321+
ofdid = of_match_node(aspeed_wdt_of_table, np);
322+
if (!ofdid)
323+
return -EINVAL;
324+
wdt->cfg = ofdid->data;
325+
262326
wdt->base = devm_platform_ioremap_resource(pdev, 0);
263327
if (IS_ERR(wdt->base))
264328
return PTR_ERR(wdt->base);
265329

266330
wdt->wdd.info = &aspeed_wdt_info;
331+
332+
if (wdt->cfg->irq_mask) {
333+
int irq = platform_get_irq_optional(pdev, 0);
334+
335+
if (irq > 0) {
336+
ret = devm_request_irq(dev, irq, aspeed_wdt_irq,
337+
IRQF_SHARED, dev_name(dev),
338+
wdt);
339+
if (ret)
340+
return ret;
341+
342+
wdt->wdd.info = &aspeed_wdt_pretimeout_info;
343+
}
344+
}
345+
267346
wdt->wdd.ops = &aspeed_wdt_ops;
268347
wdt->wdd.max_hw_heartbeat_ms = WDT_MAX_TIMEOUT_MS;
269348
wdt->wdd.parent = dev;
@@ -273,13 +352,6 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
273352

274353
watchdog_set_nowayout(&wdt->wdd, nowayout);
275354

276-
np = dev->of_node;
277-
278-
ofdid = of_match_node(aspeed_wdt_of_table, np);
279-
if (!ofdid)
280-
return -EINVAL;
281-
config = ofdid->data;
282-
283355
/*
284356
* On clock rates:
285357
* - ast2400 wdt can run at PCLK, or 1MHz
@@ -331,15 +403,15 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
331403
(of_device_is_compatible(np, "aspeed,ast2600-wdt"))) {
332404
u32 reg = readl(wdt->base + WDT_RESET_WIDTH);
333405

334-
reg &= config->ext_pulse_width_mask;
406+
reg &= wdt->cfg->ext_pulse_width_mask;
335407
if (of_property_read_bool(np, "aspeed,ext-active-high"))
336408
reg |= WDT_ACTIVE_HIGH_MAGIC;
337409
else
338410
reg |= WDT_ACTIVE_LOW_MAGIC;
339411

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

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

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

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

0 commit comments

Comments
 (0)