Skip to content

Commit 5c03f9f

Browse files
chintingkuoWim Van Sebroeck
authored andcommitted
watchdog: aspeed: Update bootstatus handling
The boot status in the watchdog device struct is updated during controller probe stage. Application layer can get the boot status through the command, cat /sys/class/watchdog/watchdogX/bootstatus. The bootstatus can be, WDIOF_CARDRESET => System is reset due to WDT timeout occurs. Others => Other reset events, e.g., power on reset. On ASPEED platforms, boot status is recorded in the SCU registers. - AST2400: Only a bit is used to represent system reset triggered by any WDT controller. - AST2500/AST2600: System reset triggered by different WDT controllers can be distinguished by different SCU bits. Besides, on AST2400 and AST2500, since alternating boot event is also triggered by using WDT timeout mechanism, it is classified as WDIOF_CARDRESET. Signed-off-by: Chin-Ting Kuo <[email protected]> Reviewed-by: Andrew Jeffery <[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 0ad2507 commit 5c03f9f

File tree

1 file changed

+79
-2
lines changed

1 file changed

+79
-2
lines changed

drivers/watchdog/aspeed_wdt.c

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,30 @@
1111
#include <linux/io.h>
1212
#include <linux/kernel.h>
1313
#include <linux/kstrtox.h>
14+
#include <linux/mfd/syscon.h>
1415
#include <linux/module.h>
1516
#include <linux/of.h>
1617
#include <linux/of_irq.h>
1718
#include <linux/platform_device.h>
19+
#include <linux/regmap.h>
1820
#include <linux/watchdog.h>
1921

2022
static bool nowayout = WATCHDOG_NOWAYOUT;
2123
module_param(nowayout, bool, 0);
2224
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
2325
__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
26+
struct aspeed_wdt_scu {
27+
const char *compatible;
28+
u32 reset_status_reg;
29+
u32 wdt_reset_mask;
30+
u32 wdt_reset_mask_shift;
31+
};
2432

2533
struct aspeed_wdt_config {
2634
u32 ext_pulse_width_mask;
2735
u32 irq_shift;
2836
u32 irq_mask;
37+
struct aspeed_wdt_scu scu;
2938
};
3039

3140
struct aspeed_wdt {
@@ -39,18 +48,36 @@ static const struct aspeed_wdt_config ast2400_config = {
3948
.ext_pulse_width_mask = 0xff,
4049
.irq_shift = 0,
4150
.irq_mask = 0,
51+
.scu = {
52+
.compatible = "aspeed,ast2400-scu",
53+
.reset_status_reg = 0x3c,
54+
.wdt_reset_mask = 0x1,
55+
.wdt_reset_mask_shift = 1,
56+
},
4257
};
4358

4459
static const struct aspeed_wdt_config ast2500_config = {
4560
.ext_pulse_width_mask = 0xfffff,
4661
.irq_shift = 12,
4762
.irq_mask = GENMASK(31, 12),
63+
.scu = {
64+
.compatible = "aspeed,ast2500-scu",
65+
.reset_status_reg = 0x3c,
66+
.wdt_reset_mask = 0x1,
67+
.wdt_reset_mask_shift = 2,
68+
},
4869
};
4970

5071
static const struct aspeed_wdt_config ast2600_config = {
5172
.ext_pulse_width_mask = 0xfffff,
5273
.irq_shift = 0,
5374
.irq_mask = GENMASK(31, 10),
75+
.scu = {
76+
.compatible = "aspeed,ast2600-scu",
77+
.reset_status_reg = 0x74,
78+
.wdt_reset_mask = 0xf,
79+
.wdt_reset_mask_shift = 16,
80+
},
5481
};
5582

5683
static const struct of_device_id aspeed_wdt_of_table[] = {
@@ -213,6 +240,56 @@ static int aspeed_wdt_restart(struct watchdog_device *wdd,
213240
return 0;
214241
}
215242

243+
static void aspeed_wdt_update_bootstatus(struct platform_device *pdev,
244+
struct aspeed_wdt *wdt)
245+
{
246+
const struct resource *res;
247+
struct aspeed_wdt_scu scu = wdt->cfg->scu;
248+
struct regmap *scu_base;
249+
u32 reset_mask_width;
250+
u32 reset_mask_shift;
251+
u32 idx = 0;
252+
u32 status;
253+
int ret;
254+
255+
if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) {
256+
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
257+
idx = ((intptr_t)wdt->base & 0x00000fff) / resource_size(res);
258+
}
259+
260+
scu_base = syscon_regmap_lookup_by_compatible(scu.compatible);
261+
if (IS_ERR(scu_base)) {
262+
wdt->wdd.bootstatus = WDIOS_UNKNOWN;
263+
return;
264+
}
265+
266+
ret = regmap_read(scu_base, scu.reset_status_reg, &status);
267+
if (ret) {
268+
wdt->wdd.bootstatus = WDIOS_UNKNOWN;
269+
return;
270+
}
271+
272+
reset_mask_width = hweight32(scu.wdt_reset_mask);
273+
reset_mask_shift = scu.wdt_reset_mask_shift +
274+
reset_mask_width * idx;
275+
276+
if (status & (scu.wdt_reset_mask << reset_mask_shift))
277+
wdt->wdd.bootstatus = WDIOF_CARDRESET;
278+
279+
/* clear wdt reset event flag */
280+
if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt") ||
281+
of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-wdt")) {
282+
ret = regmap_read(scu_base, scu.reset_status_reg, &status);
283+
if (!ret) {
284+
status &= ~(scu.wdt_reset_mask << reset_mask_shift);
285+
regmap_write(scu_base, scu.reset_status_reg, status);
286+
}
287+
} else {
288+
regmap_write(scu_base, scu.reset_status_reg,
289+
scu.wdt_reset_mask << reset_mask_shift);
290+
}
291+
}
292+
216293
/* access_cs0 shows if cs0 is accessible, hence the reverted bit */
217294
static ssize_t access_cs0_show(struct device *dev,
218295
struct device_attribute *attr, char *buf)
@@ -458,10 +535,10 @@ static int aspeed_wdt_probe(struct platform_device *pdev)
458535
writel(duration - 1, wdt->base + WDT_RESET_WIDTH);
459536
}
460537

538+
aspeed_wdt_update_bootstatus(pdev, wdt);
539+
461540
status = readl(wdt->base + WDT_TIMEOUT_STATUS);
462541
if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) {
463-
wdt->wdd.bootstatus = WDIOF_CARDRESET;
464-
465542
if (of_device_is_compatible(np, "aspeed,ast2400-wdt") ||
466543
of_device_is_compatible(np, "aspeed,ast2500-wdt"))
467544
wdt->wdd.groups = bswitch_groups;

0 commit comments

Comments
 (0)