Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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 <chin-ting_kuo@aspeedtech.com>
Reviewed-by: Andrew Jeffery <andrew@codeconstruct.com.au>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Link: https://lore.kernel.org/r/20250113093737.845097-2-chin-ting_kuo@aspeedtech.com
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@linux-watchdog.org>

authored by

Chin-Ting Kuo and committed by
Wim Van Sebroeck
5c03f9f4 0ad2507d

+79 -2
+79 -2
drivers/watchdog/aspeed_wdt.c
··· 11 11 #include <linux/io.h> 12 12 #include <linux/kernel.h> 13 13 #include <linux/kstrtox.h> 14 + #include <linux/mfd/syscon.h> 14 15 #include <linux/module.h> 15 16 #include <linux/of.h> 16 17 #include <linux/of_irq.h> 17 18 #include <linux/platform_device.h> 19 + #include <linux/regmap.h> 18 20 #include <linux/watchdog.h> 19 21 20 22 static bool nowayout = WATCHDOG_NOWAYOUT; 21 23 module_param(nowayout, bool, 0); 22 24 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" 23 25 __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 + }; 24 32 25 33 struct aspeed_wdt_config { 26 34 u32 ext_pulse_width_mask; 27 35 u32 irq_shift; 28 36 u32 irq_mask; 37 + struct aspeed_wdt_scu scu; 29 38 }; 30 39 31 40 struct aspeed_wdt { ··· 48 39 .ext_pulse_width_mask = 0xff, 49 40 .irq_shift = 0, 50 41 .irq_mask = 0, 42 + .scu = { 43 + .compatible = "aspeed,ast2400-scu", 44 + .reset_status_reg = 0x3c, 45 + .wdt_reset_mask = 0x1, 46 + .wdt_reset_mask_shift = 1, 47 + }, 51 48 }; 52 49 53 50 static const struct aspeed_wdt_config ast2500_config = { 54 51 .ext_pulse_width_mask = 0xfffff, 55 52 .irq_shift = 12, 56 53 .irq_mask = GENMASK(31, 12), 54 + .scu = { 55 + .compatible = "aspeed,ast2500-scu", 56 + .reset_status_reg = 0x3c, 57 + .wdt_reset_mask = 0x1, 58 + .wdt_reset_mask_shift = 2, 59 + }, 57 60 }; 58 61 59 62 static const struct aspeed_wdt_config ast2600_config = { 60 63 .ext_pulse_width_mask = 0xfffff, 61 64 .irq_shift = 0, 62 65 .irq_mask = GENMASK(31, 10), 66 + .scu = { 67 + .compatible = "aspeed,ast2600-scu", 68 + .reset_status_reg = 0x74, 69 + .wdt_reset_mask = 0xf, 70 + .wdt_reset_mask_shift = 16, 71 + }, 63 72 }; 64 73 65 74 static const struct of_device_id aspeed_wdt_of_table[] = { ··· 238 211 mdelay(1000); 239 212 240 213 return 0; 214 + } 215 + 216 + static void aspeed_wdt_update_bootstatus(struct platform_device *pdev, 217 + struct aspeed_wdt *wdt) 218 + { 219 + const struct resource *res; 220 + struct aspeed_wdt_scu scu = wdt->cfg->scu; 221 + struct regmap *scu_base; 222 + u32 reset_mask_width; 223 + u32 reset_mask_shift; 224 + u32 idx = 0; 225 + u32 status; 226 + int ret; 227 + 228 + if (!of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt")) { 229 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 230 + idx = ((intptr_t)wdt->base & 0x00000fff) / resource_size(res); 231 + } 232 + 233 + scu_base = syscon_regmap_lookup_by_compatible(scu.compatible); 234 + if (IS_ERR(scu_base)) { 235 + wdt->wdd.bootstatus = WDIOS_UNKNOWN; 236 + return; 237 + } 238 + 239 + ret = regmap_read(scu_base, scu.reset_status_reg, &status); 240 + if (ret) { 241 + wdt->wdd.bootstatus = WDIOS_UNKNOWN; 242 + return; 243 + } 244 + 245 + reset_mask_width = hweight32(scu.wdt_reset_mask); 246 + reset_mask_shift = scu.wdt_reset_mask_shift + 247 + reset_mask_width * idx; 248 + 249 + if (status & (scu.wdt_reset_mask << reset_mask_shift)) 250 + wdt->wdd.bootstatus = WDIOF_CARDRESET; 251 + 252 + /* clear wdt reset event flag */ 253 + if (of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2400-wdt") || 254 + of_device_is_compatible(pdev->dev.of_node, "aspeed,ast2500-wdt")) { 255 + ret = regmap_read(scu_base, scu.reset_status_reg, &status); 256 + if (!ret) { 257 + status &= ~(scu.wdt_reset_mask << reset_mask_shift); 258 + regmap_write(scu_base, scu.reset_status_reg, status); 259 + } 260 + } else { 261 + regmap_write(scu_base, scu.reset_status_reg, 262 + scu.wdt_reset_mask << reset_mask_shift); 263 + } 241 264 } 242 265 243 266 /* access_cs0 shows if cs0 is accessible, hence the reverted bit */ ··· 535 458 writel(duration - 1, wdt->base + WDT_RESET_WIDTH); 536 459 } 537 460 461 + aspeed_wdt_update_bootstatus(pdev, wdt); 462 + 538 463 status = readl(wdt->base + WDT_TIMEOUT_STATUS); 539 464 if (status & WDT_TIMEOUT_STATUS_BOOT_SECONDARY) { 540 - wdt->wdd.bootstatus = WDIOF_CARDRESET; 541 - 542 465 if (of_device_is_compatible(np, "aspeed,ast2400-wdt") || 543 466 of_device_is_compatible(np, "aspeed,ast2500-wdt")) 544 467 wdt->wdd.groups = bswitch_groups;