From 0778836bf9615399f5f7569f7977cf5fe7aa7cb1 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Sun, 5 Jul 2026 22:34:55 +0300 Subject: [PATCH 01/10] wb7, wb8: enable pstore/ramoops support in kernel config Enable CONFIG_PSTORE_RAM and CONFIG_PSTORE_CONSOLE for the wb8 and wb7 flavours. The backing reserved-memory node (compatible = "ramoops", last 1 MiB of DRAM, record-size 128K, console-size 256K, ecc 32) is injected at boot by the Wiren Board U-Boot DT fixup, so kernels running under an older U-Boot simply see no node and nothing changes. With the node present, panic and console logs survive warm resets and are harvested by the already-enabled systemd-pstore.service. Part of the Wiren Board panic-log persistence (pstore shuttle) work: U-Boot saves/restores this region across full power cycles via the swap partition. Targets feature/v6.18 (most recent kernel line) per project branch policy. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- arch/arm/configs/wirenboard7_defconfig | 8 ++++++++ arch/arm64/configs/wb8.config | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/arm/configs/wirenboard7_defconfig b/arch/arm/configs/wirenboard7_defconfig index 8da0c99bfeed..333f07913c2d 100644 --- a/arch/arm/configs/wirenboard7_defconfig +++ b/arch/arm/configs/wirenboard7_defconfig @@ -591,3 +591,11 @@ CONFIG_PREEMPTIRQ_DELAY_TEST=m CONFIG_STRICT_DEVMEM=y CONFIG_DEBUG_USER=y CONFIG_CONFIGFS_FS=y + +# Pstore/ramoops: persist kernel panic + console logs in a RAM region +# across warm resets. The reserved-memory "ramoops" node is injected by +# the Wiren Board U-Boot DT fixup (WIRENBOARD_RAMOOPS_FIXUP); without it +# these options are inert. PSTORE_RAM auto-selects REED_SOLOMON*. +CONFIG_PSTORE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_CONSOLE=y diff --git a/arch/arm64/configs/wb8.config b/arch/arm64/configs/wb8.config index a5c55fc686aa..cfdb31de11b7 100644 --- a/arch/arm64/configs/wb8.config +++ b/arch/arm64/configs/wb8.config @@ -382,3 +382,11 @@ CONFIG_SUN50I_H6_PRCM_PPU=y #SOUND CONFIG_SND_HDA_PREALLOC_SIZE=2048 + +# Pstore/ramoops: persist kernel panic + console logs in a RAM region +# across warm resets. The reserved-memory "ramoops" node is injected by +# the Wiren Board U-Boot DT fixup (WIRENBOARD_RAMOOPS_FIXUP); without it +# these options are inert. PSTORE_RAM auto-selects REED_SOLOMON*. +CONFIG_PSTORE=y +CONFIG_PSTORE_RAM=y +CONFIG_PSTORE_CONSOLE=y From 9331a0f8b341057905f65fa4cc4cb639a5661deb Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Sun, 5 Jul 2026 22:36:50 +0300 Subject: [PATCH 02/10] soc: sunxi: wb: warm-reset via SoC watchdog on panic when ramoops present A panicked Wiren Board 7 or 8 otherwise parks until the EC watchdog acts: on pre-2.4.0 EC firmware that is a full power cycle, destroying DRAM and the just-written ramoops records; 2.4.0+ warm-resets first. This panic notifier arms the idle SoC watchdog (~6 s, a few MMIO writes, safe in panic context) so the board takes a DRAM-preserving warm reset immediately and firmware-independently; U-Boot then parks the records on eMMC and performs the full recovery power cycle (pstore shuttle). The watchdog is resolved from the device tree by compatible and armed by mirroring the sunxi_wdt.c restart handler for both register layouts (sun6i-a31-wdt on H616/T507, sun4i-a10-wdt on R40/A40i), but with a 6 s interval (exact hardware encoding) instead of 0.5 s so kmsg_dump and the serial console can finish before the reset; a configured panic_timeout below 6 s intentionally wins. Gated at runtime on an actual ramoops node (injected by the Wiren Board U-Boot DT fixup): with an older U-Boot there is no node, the notifier never registers, and panic behaviour is unchanged - a kernel-only update is a strict no-op. Targets feature/v6.18 (most recent kernel line) per project branch policy. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- arch/arm/configs/wirenboard7_defconfig | 1 + arch/arm64/configs/wb8.config | 1 + drivers/soc/sunxi/Kconfig | 11 ++ drivers/soc/sunxi/Makefile | 1 + drivers/soc/sunxi/wb-ramoops-panic-reset.c | 167 +++++++++++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 drivers/soc/sunxi/wb-ramoops-panic-reset.c diff --git a/arch/arm/configs/wirenboard7_defconfig b/arch/arm/configs/wirenboard7_defconfig index 333f07913c2d..ad64ce845282 100644 --- a/arch/arm/configs/wirenboard7_defconfig +++ b/arch/arm/configs/wirenboard7_defconfig @@ -599,3 +599,4 @@ CONFIG_CONFIGFS_FS=y CONFIG_PSTORE=y CONFIG_PSTORE_RAM=y CONFIG_PSTORE_CONSOLE=y +CONFIG_WB_RAMOOPS_PANIC_RESET=y diff --git a/arch/arm64/configs/wb8.config b/arch/arm64/configs/wb8.config index cfdb31de11b7..3fe80d65461e 100644 --- a/arch/arm64/configs/wb8.config +++ b/arch/arm64/configs/wb8.config @@ -390,3 +390,4 @@ CONFIG_SND_HDA_PREALLOC_SIZE=2048 CONFIG_PSTORE=y CONFIG_PSTORE_RAM=y CONFIG_PSTORE_CONSOLE=y +CONFIG_WB_RAMOOPS_PANIC_RESET=y diff --git a/drivers/soc/sunxi/Kconfig b/drivers/soc/sunxi/Kconfig index 8aecbc9b1976..047a75943d80 100644 --- a/drivers/soc/sunxi/Kconfig +++ b/drivers/soc/sunxi/Kconfig @@ -19,3 +19,14 @@ config SUNXI_SRAM Say y here to enable the SRAM controller support. This device is responsible on mapping the SRAM in the sunXi SoCs whether to the CPU/DMA, or to the devices. + +config WB_RAMOOPS_PANIC_RESET + bool "Wiren Board: SoC watchdog warm reset on panic when ramoops is present" + depends on ARCH_SUNXI && PSTORE_RAM + help + On panic, arm the (otherwise idle) SoC watchdog so a Wiren + Board 7 (Allwinner R40/A40i) or Wiren Board 8 (Allwinner + T507/H616) takes a DRAM-preserving warm reset and the panic + log survives in the ramoops region for the bootloader to + persist. Arms itself only when a ramoops reserved-memory + node is present. diff --git a/drivers/soc/sunxi/Makefile b/drivers/soc/sunxi/Makefile index 549159571d4f..428cd117bebb 100644 --- a/drivers/soc/sunxi/Makefile +++ b/drivers/soc/sunxi/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_SUNXI_MBUS) += sunxi_mbus.o obj-$(CONFIG_SUNXI_SRAM) += sunxi_sram.o +obj-$(CONFIG_WB_RAMOOPS_PANIC_RESET) += wb-ramoops-panic-reset.o diff --git a/drivers/soc/sunxi/wb-ramoops-panic-reset.c b/drivers/soc/sunxi/wb-ramoops-panic-reset.c new file mode 100644 index 000000000000..5f5c4cf989d7 --- /dev/null +++ b/drivers/soc/sunxi/wb-ramoops-panic-reset.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Wiren Board 7 (Allwinner R40/A40i) and Wiren Board 8 (Allwinner + * T507/H616): warm-reset the SoC shortly after a panic so the panic + * log persists in the ramoops region. + * + * The ramoops region at the top of DRAM survives a SoC watchdog reset + * (DRAM stays powered), after which U-Boot parks the records on eMMC + * and performs a full recovery power cycle (the "pstore shuttle"). + * Without this hook a panicked kernel just parks until the EC watchdog + * acts: on pre-2.4.0 EC firmware the EC watchdog action is a full + * power cycle, destroying DRAM and the records; 2.4.0+ warm-resets + * first - this hook makes the reset immediate and firmware-independent. + * + * The panic notifier arms the SoC watchdog with a ~6 s interval using + * a few raw MMIO writes - nothing here can sleep, lock or allocate, so + * it is safe in panic context. The sunxi watchdog device (watchdog0) + * is idle on Wiren Board systems: userspace feeds the EC watchdog + * (watchdog1) instead. + * + * Why ~6 s instead of the 0.5 s hardware minimum: the panic notifiers + * run before kmsg_dump writes the ramoops records and before the + * serial console drains the backtrace, so a 0.5 s fuse could fire + * before the dump completes. An operator-configured panic_timeout + * shorter than 6 s intentionally wins over this hook. + * + * The hook arms itself only if a ramoops region actually registered: + * on systems booted by an older U-Boot (no injected ramoops node) + * panic behaviour is intentionally left unchanged, so updating the + * kernel alone changes nothing (no-lockstep updates). + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include + +/* + * Register layouts and arming sequence mirror the restart handler in + * drivers/watchdog/sunxi_wdt.c (sun4i_wdt_reg / sun6i_wdt_reg), except + * for the interval: the restart handler uses the minimal 0.5 s, we set + * 6 s, which has an exact hardware encoding (wdt_timeout_map[6] = 0x6). + * Neither WB variant uses a key value (that is sun20i/sun55i only). + */ +#define WDT_CTRL_RELOAD ((1 << 0) | (0x0a57 << 1)) +#define WDT_MODE_EN (1 << 0) +#define WDT_TIMEOUT_MASK 0x0F +#define WDT_INTV_6S 0x6 /* wdt_timeout_map[6] in sunxi_wdt.c */ + +struct wb_wdt_variant { + u8 ctrl; /* counter restart register */ + u8 cfg; /* reset configuration register */ + u8 mode; /* enable + interval register */ + u8 timeout_shift; + u8 reset_mask; + u8 reset_val; /* whole-system reset */ +}; + +/* H616/T507: watchdog in the timer block, dedicated CFG register */ +static const struct wb_wdt_variant sun6i_wdt_variant = { + .ctrl = 0x10, + .cfg = 0x14, + .mode = 0x18, + .timeout_shift = 4, + .reset_mask = 0x03, + .reset_val = 0x01, +}; + +/* R40/A40i: CFG and MODE share one register (offset 0x04) */ +static const struct wb_wdt_variant sun4i_wdt_variant = { + .ctrl = 0x00, + .cfg = 0x04, + .mode = 0x04, + .timeout_shift = 3, + .reset_mask = 0x02, + .reset_val = 0x02, +}; + +static const struct of_device_id wb_wdt_matches[] = { + { .compatible = "allwinner,sun6i-a31-wdt", .data = &sun6i_wdt_variant }, + { .compatible = "allwinner,sun4i-a10-wdt", .data = &sun4i_wdt_variant }, + { /* sentinel */ } +}; + +static void __iomem *wb_wdt_regs; +static const struct wb_wdt_variant *wb_wdt_variant; + +static int wb_ramoops_panic_reset(struct notifier_block *nb, + unsigned long action, void *data) +{ + const struct wb_wdt_variant *v = wb_wdt_variant; + u32 val; + + /* Set whole-system reset function */ + val = readl(wb_wdt_regs + v->cfg); + val &= ~(u32)v->reset_mask; + val |= v->reset_val; + writel(val, wb_wdt_regs + v->cfg); + + /* + * Set the ~6 s interval and enable. On sun4i CFG and MODE are + * the same register, so this read-modify-write must (and does) + * preserve the reset bit written above. + */ + val = readl(wb_wdt_regs + v->mode); + val &= ~((u32)WDT_TIMEOUT_MASK << v->timeout_shift); + val |= WDT_INTV_6S << v->timeout_shift; + val |= WDT_MODE_EN; + writel(val, wb_wdt_regs + v->mode); + + /* Restart the counter so the full interval runs from now */ + writel(WDT_CTRL_RELOAD, wb_wdt_regs + v->ctrl); + + pr_emerg("SoC watchdog armed, warm reset in ~6 s\n"); + + return NOTIFY_DONE; +} + +static struct notifier_block wb_ramoops_panic_nb = { + .notifier_call = wb_ramoops_panic_reset, + /* run late: let other notifiers log first, the dump is on panic anyway */ + .priority = INT_MIN + 1, +}; + +static int __init wb_ramoops_panic_reset_init(void) +{ + const struct of_device_id *match; + struct device_node *np; + + if (!of_machine_is_compatible("allwinner,sun50i-h616") && + !of_machine_is_compatible("allwinner,sun8i-r40")) + return 0; + + /* + * Arm only when a ramoops region exists (injected into + * /reserved-memory by the Wiren Board U-Boot DT fixup). + */ + np = of_find_compatible_node(NULL, NULL, "ramoops"); + if (!np) + return 0; + of_node_put(np); + + np = of_find_matching_node_and_match(NULL, wb_wdt_matches, &match); + if (!np) { + pr_warn("no SoC watchdog node found, not armed\n"); + return 0; + } + + wb_wdt_regs = of_iomap(np, 0); + of_node_put(np); + if (!wb_wdt_regs) + return -ENOMEM; + + wb_wdt_variant = match->data; + + atomic_notifier_chain_register(&panic_notifier_list, + &wb_ramoops_panic_nb); + pr_info("armed (ramoops present)\n"); + + return 0; +} +late_initcall(wb_ramoops_panic_reset_init); From f41160ed7151510c1be94a9a5247a0f7a4e82767 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Sun, 5 Jul 2026 22:37:39 +0300 Subject: [PATCH 03/10] mfd: wbec: know the newer EC poweron reasons EC firmware 2.4.x reports two reasons the driver's string table predates: 8 = watchdog warm reset (warm-watchdog-reset EC series) and 9 = full power cycle request (used by the U-Boot pstore shuttle after parking panic logs). Add them, plus the firmware's own placeholder 7 (Unknown) so the indices line up, and convert the table to designated initializers with a source-of-truth note (mirrors LINUX_POWERON_REASON in wb-embedded-controller src/wbec.c; append-only ABI). Out-of-range values still print Unknown, so older kernels against even newer firmware stay harmless. Targets feature/v6.18 (most recent kernel line) per project branch policy. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- drivers/mfd/wbec.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/mfd/wbec.c b/drivers/mfd/wbec.c index 269add5fd438..f873795c6cae 100644 --- a/drivers/mfd/wbec.c +++ b/drivers/mfd/wbec.c @@ -25,14 +25,21 @@ /* For power off WBEC activates PWON pin on PMIC for 6s */ #define WBEC_POWER_RESET_DELAY_MS 10000 +/* + * Mirrors LINUX_POWERON_REASON in wb-embedded-controller src/wbec.c; + * append-only ABI. + */ static const char * const wbec_poweron_reason[] = { - "Power supply on", - "Power button", - "RTC alarm", - "Reboot", - "Reboot instead of poweroff", - "Watchdog", - "PMIC is unexpectedly off", + [0] = "Power supply on", + [1] = "Power button", + [2] = "RTC alarm", + [3] = "Reboot", + [4] = "Reboot instead of poweroff", + [5] = "Watchdog", + [6] = "PMIC is unexpectedly off", + [7] = "Unknown", + [8] = "Watchdog (warm reset)", + [9] = "Full power cycle request", }; static const struct regmap_config wbec_regmap_config_v1 = { From cc0b8be0bcbfff9c814fd5c7c3240f1372cbf411 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 01:34:59 +0300 Subject: [PATCH 04/10] wb7, wb8: ship fixed pstore/ramoops reserved-memory node in DTBs Add a reserved-memory ramoops node at the fixed address 0x46000000 (1 MiB, record 128K, console 256K, ecc 32, no-map) to the Wiren Board 7 (sun8i-r40) and Wiren Board 8 (sun50i-h616) family device trees. The address matches U-Boot WB_RAMOOPS_BASE exactly. Carrying the node in the kernel DTB (rather than relying only on the U-Boot DT fixup) is what makes a kernel-only update useful: under an old U-Boot with no shuttle, the node is still present, the region survives a warm reset, and the same kernel harvests the panic log on the next boot. A new U-Boot's fixup yields to this node (it already takes precedence), and its shuttle adds full-power-cycle survival on top. Placed in the WB family base dtsis (84x/85x for WB8, 72x/74x for WB7; 73x inherits via 72x) so every shipped board gets it exactly once. The fixed low address is bench-verified to retain across a warm reset on all DRAM densities. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- .../allwinner/sun8i-r40-wirenboard72x.dtsi | 19 +++++++++++++++++++ .../allwinner/sun8i-r40-wirenboard74x.dtsi | 19 +++++++++++++++++++ .../allwinner/sun50i-h616-wirenboard84x.dtsi | 19 +++++++++++++++++++ .../allwinner/sun50i-h616-wirenboard85x.dtsi | 15 +++++++++++++++ 4 files changed, 72 insertions(+) diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi index 05531e205128..60b788355af0 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi @@ -38,6 +38,25 @@ stdout-path = "serial0:115200n8"; }; + /* Fixed-address pstore/ramoops region; survives a warm reset so the + * same kernel can harvest it even under an old U-Boot with no shuttle. + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + */ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + ramoops@46000000 { + compatible = "ramoops"; + reg = <0x46000000 0x100000>; + record-size = <0x20000>; + console-size = <0x40000>; + ecc-size = <32>; + no-map; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi index 508fc00c10a4..b95047ef1576 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi @@ -40,6 +40,25 @@ stdout-path = "serial0:115200n8"; }; + /* Fixed-address pstore/ramoops region; survives a warm reset so the + * same kernel can harvest it even under an old U-Boot with no shuttle. + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + */ + reserved-memory { + #address-cells = <1>; + #size-cells = <1>; + ranges; + + ramoops@46000000 { + compatible = "ramoops"; + reg = <0x46000000 0x100000>; + record-size = <0x20000>; + console-size = <0x40000>; + ecc-size = <32>; + no-map; + }; + }; + regulators { compatible = "simple-bus"; #address-cells = <1>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi index 32bae534ec59..c8bcee814eee 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi @@ -48,6 +48,25 @@ rtc_onboard = &rtc_onboard; }; + /* Fixed-address pstore/ramoops region; survives a warm reset so the + * same kernel can harvest it even under an old U-Boot with no shuttle. + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + */ + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + ramoops@46000000 { + compatible = "ramoops"; + reg = <0x0 0x46000000 0x0 0x100000>; + record-size = <0x20000>; + console-size = <0x40000>; + ecc-size = <32>; + no-map; + }; + }; + wirenboard { adc-type = <3>; pwm-buzzer = <1>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi index c9d81df23e05..97752b8ddbbe 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi @@ -404,6 +404,21 @@ alloc-ranges = <0x0 0x00000000 0x0 0x80000000>; linux,cma-default; }; + + /* Fixed-address pstore/ramoops region; survives a warm reset so + * the same kernel can harvest it even under an old U-Boot with no + * shuttle. The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + * #address-cells/#size-cells/ranges (2/2) are inherited from the + * /reserved-memory node in sun50i-h616.dtsi that this merges into. + */ + ramoops@46000000 { + compatible = "ramoops"; + reg = <0x0 0x46000000 0x0 0x100000>; + record-size = <0x20000>; + console-size = <0x40000>; + ecc-size = <32>; + no-map; + }; }; soc { From 2c7ae138e8c395d8a8c88c72a1ff5a5bba4a1f66 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 01:37:51 +0300 Subject: [PATCH 05/10] soc: sunxi: wb: brake panic warm-reset loops via an RTC reset-reason stamp A kernel that panics every boot would warm-reset forever. There is no hardware reset-cause latch on these SoCs (the watchdog status register is interrupt-mode only and clears on reset), so use the RTC general- purpose register documented "for storing power-off information": it survives a warm reset and is wiped by the EC's hard 5V cycle - exactly the reset-reason signal needed. The panic handler stamps RTC GP reg 3 (0x0700010c) before arming the watchdog. A boot that comes up still stamped knows it just warm-reset from a panic and declines to re-arm, letting the EC watchdog escalate to a hard power cycle (which wipes the stamp and breaks the loop). A 60 s healthy-uptime delayed work clears the stamp so ordinary panics always warm-reset. H616 only for now (the R40 RTC's wipe-on-hard-cycle behaviour is not yet verified; R40 keeps arming unconditionally). Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- drivers/soc/sunxi/wb-ramoops-panic-reset.c | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/drivers/soc/sunxi/wb-ramoops-panic-reset.c b/drivers/soc/sunxi/wb-ramoops-panic-reset.c index 5f5c4cf989d7..adda57506051 100644 --- a/drivers/soc/sunxi/wb-ramoops-panic-reset.c +++ b/drivers/soc/sunxi/wb-ramoops-panic-reset.c @@ -39,6 +39,7 @@ #include #include #include +#include /* * Register layouts and arming sequence mirror the restart handler in @@ -90,12 +91,56 @@ static const struct of_device_id wb_wdt_matches[] = { static void __iomem *wb_wdt_regs; static const struct wb_wdt_variant *wb_wdt_variant; +/* + * Loop brake. A kernel that panics on every boot would otherwise + * warm-reset forever. There is no hardware "reset cause" latch on these + * SoCs (the watchdog status register is interrupt-mode only and clears + * on reset), but the RTC block has general-purpose registers documented + * "for storing power-off information": one of them survives a warm reset + * and is wiped by the EC's hard 5 V cycle, which is exactly the reset- + * reason signal we need. The panic handler stamps it before arming; + * if a boot comes up still stamped and we have not yet cleared it after + * a healthy uptime, we decline to arm again and let the EC watchdog + * escalate to a hard power cycle (which wipes the stamp and breaks the + * loop). H616 only for now; the R40 RTC's wipe-on-hard-cycle behaviour + * is not yet verified, so R40 keeps arming unconditionally. + */ +#define WB_RTC_BREADCRUMB_PHYS 0x0700010c /* RTC GP data reg 3 */ +#define WB_RTC_BREADCRUMB_MAGIC 0x50414e31 /* "PAN1" */ + +static void __iomem *wb_rtc_breadcrumb; +static bool wb_reset_from_panic; + +static void wb_breadcrumb_clear_fn(struct work_struct *work) +{ + /* Healthy uptime reached: allow the next panic to warm-reset again. */ + if (wb_rtc_breadcrumb) + writel(0, wb_rtc_breadcrumb); + wb_reset_from_panic = false; + pr_info("healthy, panic warm-reset re-armed\n"); +} +static DECLARE_DELAYED_WORK(wb_breadcrumb_clear, wb_breadcrumb_clear_fn); + static int wb_ramoops_panic_reset(struct notifier_block *nb, unsigned long action, void *data) { const struct wb_wdt_variant *v = wb_wdt_variant; u32 val; + /* + * If this boot itself came back from a panic-armed warm reset and + * has not yet proven healthy, do not arm again: let the EC watchdog + * hard-cycle the board so a crash loop cannot spin on warm resets. + */ + if (wb_reset_from_panic) { + pr_emerg("panic again after a warm reset; not re-arming, leaving it to the EC watchdog\n"); + return NOTIFY_DONE; + } + + /* Stamp the reset-reason breadcrumb before triggering the reset. */ + if (wb_rtc_breadcrumb) + writel(WB_RTC_BREADCRUMB_MAGIC, wb_rtc_breadcrumb); + /* Set whole-system reset function */ val = readl(wb_wdt_regs + v->cfg); val &= ~(u32)v->reset_mask; @@ -158,6 +203,27 @@ static int __init wb_ramoops_panic_reset_init(void) wb_wdt_variant = match->data; + /* + * Reset-reason breadcrumb (H616 only for now). If this boot came up + * with the panic stamp still set, we just warm-reset from a panic: + * hold off arming until a healthy uptime clears it. + */ + if (of_machine_is_compatible("allwinner,sun50i-h616")) { + wb_rtc_breadcrumb = ioremap(WB_RTC_BREADCRUMB_PHYS, 4); + if (wb_rtc_breadcrumb && + readl(wb_rtc_breadcrumb) == WB_RTC_BREADCRUMB_MAGIC) { + wb_reset_from_panic = true; + pr_info("came back from a panic warm reset; holding off re-arm\n"); + } + } + /* + * Clear the stamp once we have stayed up long enough to count as a + * healthy session (runs whether or not the stamp was set, so a plain + * boot re-arms immediately and a post-panic boot re-arms after the + * delay). 60 s comfortably exceeds pstore harvest. + */ + schedule_delayed_work(&wb_breadcrumb_clear, msecs_to_jiffies(60000)); + atomic_notifier_chain_register(&panic_notifier_list, &wb_ramoops_panic_nb); pr_info("armed (ramoops present)\n"); From aa805ee317ade0ee74776e5ecdfb03b856edfc33 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 02:20:39 +0300 Subject: [PATCH 06/10] soc: sunxi: wb: fix stale top-of-DRAM header; only log re-arm when held off Review follow-up: the module header still said the ramoops region is at the top of DRAM and omitted the loop brake; update it for the fixed 0x46000000 placement and mention the decline-to-re-arm behaviour. Also gate the 'healthy, panic warm-reset re-armed' message on wb_reset_from_panic so it only prints when a hold-off was actually lifted, not on every boot. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- drivers/soc/sunxi/wb-ramoops-panic-reset.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/drivers/soc/sunxi/wb-ramoops-panic-reset.c b/drivers/soc/sunxi/wb-ramoops-panic-reset.c index adda57506051..c79394e8127a 100644 --- a/drivers/soc/sunxi/wb-ramoops-panic-reset.c +++ b/drivers/soc/sunxi/wb-ramoops-panic-reset.c @@ -4,9 +4,13 @@ * T507/H616): warm-reset the SoC shortly after a panic so the panic * log persists in the ramoops region. * - * The ramoops region at the top of DRAM survives a SoC watchdog reset - * (DRAM stays powered), after which U-Boot parks the records on eMMC - * and performs a full recovery power cycle (the "pstore shuttle"). + * The ramoops region (a fixed low DRAM address, 0x46000000, carried by + * the reserved-memory node in the board DT) survives a SoC watchdog + * reset (DRAM stays powered), after which U-Boot parks the records on + * eMMC and performs a full recovery power cycle (the "pstore shuttle"). + * A fixed address also lets a kernel-only update capture logs: the + * region survives the warm reset and this same kernel harvests it on + * the next boot even under an old U-Boot with no shuttle. * Without this hook a panicked kernel just parks until the EC watchdog * acts: on pre-2.4.0 EC firmware the EC watchdog action is a full * power cycle, destroying DRAM and the records; 2.4.0+ warm-resets @@ -27,7 +31,9 @@ * The hook arms itself only if a ramoops region actually registered: * on systems booted by an older U-Boot (no injected ramoops node) * panic behaviour is intentionally left unchanged, so updating the - * kernel alone changes nothing (no-lockstep updates). + * kernel alone changes nothing (no-lockstep updates). It also declines + * to re-arm when it detects it just warm-reset from a panic (see the + * loop brake below), leaving a repeat panic to the EC watchdog. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -114,10 +120,13 @@ static bool wb_reset_from_panic; static void wb_breadcrumb_clear_fn(struct work_struct *work) { /* Healthy uptime reached: allow the next panic to warm-reset again. */ + bool was_held = wb_reset_from_panic; + if (wb_rtc_breadcrumb) writel(0, wb_rtc_breadcrumb); wb_reset_from_panic = false; - pr_info("healthy, panic warm-reset re-armed\n"); + if (was_held) + pr_info("healthy, panic warm-reset re-armed\n"); } static DECLARE_DELAYED_WORK(wb_breadcrumb_clear, wb_breadcrumb_clear_fn); From f2228f1f273d002e2865b3eb013702ffde81af25 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 02:57:32 +0300 Subject: [PATCH 07/10] wb7, wb8: move fixed ramoops region to 0x43800000 (below decompression buffer) Match the U-Boot change: move the reserved-memory ramoops node from 0x46000000 to 0x43800000 across the WB7/WB8 family DTs, and update the address in the panic-reset module header. 0x46000000 sat only 32 MiB above the arm64 kernel decompression buffer at 0x44000000; a kernel larger than 32 MiB uncompressed would clobber it. 0x43800000 sits in the gap below the decompression buffer, immune to kernel growth, and is bench-verified to retain across a warm reset on all densities. Must stay in sync with U-Boot WB_RAMOOPS_BASE. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi | 6 +++--- arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi | 6 +++--- .../arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi | 6 +++--- .../arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi | 6 +++--- drivers/soc/sunxi/wb-ramoops-panic-reset.c | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi index 60b788355af0..e8d35c33af70 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi @@ -40,16 +40,16 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so the * same kernel can harvest it even under an old U-Boot with no shuttle. - * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). */ reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; - ramoops@46000000 { + ramoops@43800000 { compatible = "ramoops"; - reg = <0x46000000 0x100000>; + reg = <0x43800000 0x100000>; record-size = <0x20000>; console-size = <0x40000>; ecc-size = <32>; diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi index b95047ef1576..1d4f52a6c122 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi @@ -42,16 +42,16 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so the * same kernel can harvest it even under an old U-Boot with no shuttle. - * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). */ reserved-memory { #address-cells = <1>; #size-cells = <1>; ranges; - ramoops@46000000 { + ramoops@43800000 { compatible = "ramoops"; - reg = <0x46000000 0x100000>; + reg = <0x43800000 0x100000>; record-size = <0x20000>; console-size = <0x40000>; ecc-size = <32>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi index c8bcee814eee..13e8156cb153 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard84x.dtsi @@ -50,16 +50,16 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so the * same kernel can harvest it even under an old U-Boot with no shuttle. - * The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + * The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). */ reserved-memory { #address-cells = <2>; #size-cells = <2>; ranges; - ramoops@46000000 { + ramoops@43800000 { compatible = "ramoops"; - reg = <0x0 0x46000000 0x0 0x100000>; + reg = <0x0 0x43800000 0x0 0x100000>; record-size = <0x20000>; console-size = <0x40000>; ecc-size = <32>; diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi index 97752b8ddbbe..38c56bc06b42 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi @@ -407,13 +407,13 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so * the same kernel can harvest it even under an old U-Boot with no - * shuttle. The base MUST match U-Boot WB_RAMOOPS_BASE (0x46000000). + * shuttle. The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). * #address-cells/#size-cells/ranges (2/2) are inherited from the * /reserved-memory node in sun50i-h616.dtsi that this merges into. */ - ramoops@46000000 { + ramoops@43800000 { compatible = "ramoops"; - reg = <0x0 0x46000000 0x0 0x100000>; + reg = <0x0 0x43800000 0x0 0x100000>; record-size = <0x20000>; console-size = <0x40000>; ecc-size = <32>; diff --git a/drivers/soc/sunxi/wb-ramoops-panic-reset.c b/drivers/soc/sunxi/wb-ramoops-panic-reset.c index c79394e8127a..99c184beda65 100644 --- a/drivers/soc/sunxi/wb-ramoops-panic-reset.c +++ b/drivers/soc/sunxi/wb-ramoops-panic-reset.c @@ -4,7 +4,7 @@ * T507/H616): warm-reset the SoC shortly after a panic so the panic * log persists in the ramoops region. * - * The ramoops region (a fixed low DRAM address, 0x46000000, carried by + * The ramoops region (a fixed low DRAM address, 0x43800000, carried by * the reserved-memory node in the board DT) survives a SoC watchdog * reset (DRAM stays powered), after which U-Boot parks the records on * eMMC and performs a full recovery power cycle (the "pstore shuttle"). From 4b4abb2f23086bedfe08e7ba35b2f1982ba712b7 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 03:14:24 +0300 Subject: [PATCH 08/10] wb7: document ramoops 0x43800000 clearance on R40 (no build guard, WB7 untested) The fixed ramoops address was chosen for the arm64/H616 boot map and reused on 32-bit R40. Document in the R40 dtsi that it clears the used loads and sits ~4 MiB above the stock (unused) RAMDISK_ADDR_R, that there is no build-time guard on the R40 side, and that retention on real WB7 hardware is still pending validation. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01EVPkc2g2yir6SGyrED6uNT --- arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi | 9 +++++++++ arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi index e8d35c33af70..a3d23e7a8d86 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard72x.dtsi @@ -41,6 +41,15 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so the * same kernel can harvest it even under an old U-Boot with no shuttle. * The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). + * + * The address was chosen for the arm64/H616 boot map but is reused + * here for R40. On R40 it clears the used load addresses (fdt + * 0x43000000, overlay 0x43300000) and sits ~4 MiB above the stock + * RAMDISK_ADDR_R (0x43400000). WB firmware boots without an initrd + * (bootz "... - ..."), so ramdisk_addr_r is unused; if that ever + * changes, an initrd >4 MiB would overlap this region. There is no + * build-time guard on the R40 side (the U-Boot static_asserts are + * WB8-only). Pending: retention validation on real WB7 hardware. */ reserved-memory { #address-cells = <1>; diff --git a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi index 1d4f52a6c122..b4e85d3c4915 100644 --- a/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi +++ b/arch/arm/boot/dts/allwinner/sun8i-r40-wirenboard74x.dtsi @@ -43,6 +43,15 @@ /* Fixed-address pstore/ramoops region; survives a warm reset so the * same kernel can harvest it even under an old U-Boot with no shuttle. * The base MUST match U-Boot WB_RAMOOPS_BASE (0x43800000). + * + * The address was chosen for the arm64/H616 boot map but is reused + * here for R40. On R40 it clears the used load addresses (fdt + * 0x43000000, overlay 0x43300000) and sits ~4 MiB above the stock + * RAMDISK_ADDR_R (0x43400000). WB firmware boots without an initrd + * (bootz "... - ..."), so ramdisk_addr_r is unused; if that ever + * changes, an initrd >4 MiB would overlap this region. There is no + * build-time guard on the R40 side (the U-Boot static_asserts are + * WB8-only). Pending: retention validation on real WB7 hardware. */ reserved-memory { #address-cells = <1>; From 51f5ee45da11ea79b2b9b7ee3e32b5128e83a100 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 18:56:17 +0300 Subject: [PATCH 09/10] mfd: wbec: quiet the expected v1-protocol fallback The v2 pad-word SPI protocol is a newer-EC feature; boards with older EC firmware (e.g. WB74) only ever speak v1, so probe always tries v2 first and falls back. That fallback logged at info level as "WBEC not found with v2 protocol, trying v1", which reads like a fault and has repeatedly triggered false bug reports on perfectly healthy v1 boards. Demote the fallback notice to dev_dbg (still reachable via dynamic debug) and reword it as an ordinary negotiation step. The eventual success line ("WBEC found with v1/v2 protocol") already records which protocol won, so nothing is lost from the default log. Targets feature/v6.18 (most recent kernel line) per project branch policy. Co-Authored-By: Claude Fable 5 --- drivers/mfd/wbec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/mfd/wbec.c b/drivers/mfd/wbec.c index f873795c6cae..1df31b6ccfe9 100644 --- a/drivers/mfd/wbec.c +++ b/drivers/mfd/wbec.c @@ -361,7 +361,13 @@ static int wbec_probe(struct spi_device *spi) ret = wbec_check_present(wbec); if (ret == -ENODEV) { - dev_info(wbec->dev, "WBEC not found with v2 protocol, trying v1\n"); + /* + * Expected on every board that speaks the v1 protocol (the v2 + * pad-word protocol is a newer-EC feature): probe v2 first, then + * fall back to v1. Keep this at debug level so a normal v1 board + * does not log a scary "not found" line during ordinary probing. + */ + dev_dbg(wbec->dev, "no WBEC on v2 protocol, falling back to v1\n"); /* don't worry about memory leak, previous regmap will be freed by devm */ wbec->regmap = devm_regmap_init_spi(spi, &wbec_regmap_config_v1); From c946339d42f8e288bef1ea9e7c8311b8718d7704 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Mon, 6 Jul 2026 20:57:19 +0300 Subject: [PATCH 10/10] soc: sunxi: wb: extend the panic-loop breadcrumb to R40 The RTC reset-reason breadcrumb that brakes panic warm-reset loops was H616-only: it needs an RTC general-purpose register that survives the SoC warm reset but is wiped by the EC's hard 5 V cycle, and that behaviour had not been verified on R40, so R40 armed unconditionally. Make the breadcrumb register per-SoC by moving it into wb_wdt_variant (rtc_breadcrumb_phys; 0 = no breadcrumb, arm unconditionally). H616 keeps its RTC GP data reg 3 (0x0700010c). R40 gets GP data reg 0 (0x01c20500, RTC base 0x01c20400 + the sun6i-rtc GP_DATA block at +0x100), which WB7.4.2 bench testing confirmed survives a sun4i-WDT warm reset (0xCAFE55AA persisted through a panic reset) and is cleared by the EC full power cycle (reads 0, poweron_reason 9) - the same semantics as the H616 breadcrumb. GP reg 0 is free: no nvmem-cell in the R40 DT consumes the RTC nvmem provider. This drops the of_machine_is_compatible("...h616") special-case; the per-SoC field now drives it, so both WB7 and WB8 get the crash-loop brake. Targets feature/v6.18 (most recent kernel line) per project branch policy. Co-Authored-By: Claude Fable 5 --- drivers/soc/sunxi/wb-ramoops-panic-reset.c | 36 +++++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/soc/sunxi/wb-ramoops-panic-reset.c b/drivers/soc/sunxi/wb-ramoops-panic-reset.c index 99c184beda65..8db9501786ea 100644 --- a/drivers/soc/sunxi/wb-ramoops-panic-reset.c +++ b/drivers/soc/sunxi/wb-ramoops-panic-reset.c @@ -66,6 +66,14 @@ struct wb_wdt_variant { u8 timeout_shift; u8 reset_mask; u8 reset_val; /* whole-system reset */ + /* + * Physical address of the RTC general-purpose register used as the + * panic-loop-breaker breadcrumb (see the loop brake below). Must be a + * register that survives a warm reset but is wiped by the EC's hard 5 V + * cycle. 0 = no verified register on this SoC: arm unconditionally, no + * loop brake. + */ + u32 rtc_breadcrumb_phys; }; /* H616/T507: watchdog in the timer block, dedicated CFG register */ @@ -76,6 +84,7 @@ static const struct wb_wdt_variant sun6i_wdt_variant = { .timeout_shift = 4, .reset_mask = 0x03, .reset_val = 0x01, + .rtc_breadcrumb_phys = 0x0700010c, /* RTC base 0x07000000 + GP data reg 3 */ }; /* R40/A40i: CFG and MODE share one register (offset 0x04) */ @@ -86,6 +95,14 @@ static const struct wb_wdt_variant sun4i_wdt_variant = { .timeout_shift = 3, .reset_mask = 0x02, .reset_val = 0x02, + /* + * R40 shares the sun6i-rtc battery-backed GP data registers (8 x 32-bit + * at RTC base + 0x100). GP data reg 0 is bench-confirmed on WB7.4.2 to + * survive a sun4i-WDT warm reset and to be wiped by the EC full 5 V + * cycle - the same semantics as the H616 breadcrumb - and is free (no + * nvmem-cell consumer references it in the R40 DT). + */ + .rtc_breadcrumb_phys = 0x01c20500, /* RTC base 0x01c20400 + GP data reg 0 */ }; static const struct of_device_id wb_wdt_matches[] = { @@ -108,10 +125,12 @@ static const struct wb_wdt_variant *wb_wdt_variant; * if a boot comes up still stamped and we have not yet cleared it after * a healthy uptime, we decline to arm again and let the EC watchdog * escalate to a hard power cycle (which wipes the stamp and breaks the - * loop). H616 only for now; the R40 RTC's wipe-on-hard-cycle behaviour - * is not yet verified, so R40 keeps arming unconditionally. + * loop). The register is per-SoC (wb_wdt_variant.rtc_breadcrumb_phys): + * H616 uses RTC GP data reg 3, R40 uses RTC GP data reg 0 - both + * bench-confirmed to survive a warm reset and be wiped by the EC hard + * cycle. A SoC with the field left 0 has no breadcrumb and arms + * unconditionally. */ -#define WB_RTC_BREADCRUMB_PHYS 0x0700010c /* RTC GP data reg 3 */ #define WB_RTC_BREADCRUMB_MAGIC 0x50414e31 /* "PAN1" */ static void __iomem *wb_rtc_breadcrumb; @@ -213,12 +232,13 @@ static int __init wb_ramoops_panic_reset_init(void) wb_wdt_variant = match->data; /* - * Reset-reason breadcrumb (H616 only for now). If this boot came up - * with the panic stamp still set, we just warm-reset from a panic: - * hold off arming until a healthy uptime clears it. + * Reset-reason breadcrumb, per SoC (wb_wdt_variant.rtc_breadcrumb_phys; + * 0 = none, arm unconditionally). If this boot came up with the panic + * stamp still set, we just warm-reset from a panic: hold off arming + * until a healthy uptime clears it. */ - if (of_machine_is_compatible("allwinner,sun50i-h616")) { - wb_rtc_breadcrumb = ioremap(WB_RTC_BREADCRUMB_PHYS, 4); + if (wb_wdt_variant->rtc_breadcrumb_phys) { + wb_rtc_breadcrumb = ioremap(wb_wdt_variant->rtc_breadcrumb_phys, 4); if (wb_rtc_breadcrumb && readl(wb_rtc_breadcrumb) == WB_RTC_BREADCRUMB_MAGIC) { wb_reset_from_panic = true;