Skip to content

Commit aef57a1

Browse files
dgarskedanielinux
authored andcommitted
versal: support the system watchdog timer (WWDT)
1 parent 08f20f8 commit aef57a1

14 files changed

Lines changed: 872 additions & 2 deletions

File tree

‎.github/workflows/test-configs.yml‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,6 +1345,23 @@ jobs:
13451345
config-file: ./config/examples/versal_vmk180_sdcard.config
13461346
make-args: DISK_FS=both
13471347

1348+
# Default instance (FPD) and the stop-before-the-OS path
1349+
versal_vmk180_wdt_fpd_test:
1350+
uses: ./.github/workflows/test-build-aarch64.yml
1351+
with:
1352+
arch: aarch64
1353+
config-file: ./config/examples/versal_vmk180.config
1354+
make-args: WATCHDOG=1 WATCHDOG_TIMEOUT_MS=30000
1355+
1356+
# LPD instance, and the only build with both the WATCHDOG_HANDOFF branch of
1357+
# hal_prepare_boot() and the SDHCI refresh hook (src/sdhci.c is SD-only)
1358+
versal_vmk180_sdcard_wdt_lpd_handoff_test:
1359+
uses: ./.github/workflows/test-build-aarch64.yml
1360+
with:
1361+
arch: aarch64
1362+
config-file: ./config/examples/versal_vmk180_sdcard.config
1363+
make-args: WATCHDOG=1 WATCHDOG_HANDOFF=1 WATCHDOG_BASE=0xFF120000
1364+
13481365
sim_wolfhsm_test:
13491366
uses: ./.github/workflows/test-build.yml
13501367
with:

‎config/examples/versal_vmk180.config‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ NO_XIP=1
5656
#BOOT_EL1=1
5757
#BOOT_EL2=1
5858

59+
# System watchdog (SWDT/WWDT)
60+
# WATCHDOG=1 has wolfBoot service the watchdog through its boot and stop it
61+
# before Linux, as U-Boot does. WATCHDOG_BASE selects the instance
62+
# (0xFD4D0000 FPD / 0xFF120000 LPD), and the design's PDI must release that
63+
# block's reset. WATCHDOG_HANDOFF=1 leaves it armed for the OS instead.
64+
#WATCHDOG=1
65+
#WATCHDOG_TIMEOUT_MS=30000
66+
#WATCHDOG_BASE=0xFD4D0000
67+
#WATCHDOG_CLK_HZ=150000000
68+
#WATCHDOG_HANDOFF=1
69+
5970
# ELF loading support
6071
ELF?=1
6172

‎config/examples/versal_vmk180_sdcard.config‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,17 @@ WOLFBOOT_SECTOR_SIZE=0x1000
145145
# ============================================================================
146146
CFLAGS_EXTRA+=-DDEBUG_UART_NUM=0
147147

148+
# System watchdog (SWDT/WWDT)
149+
# WATCHDOG=1 has wolfBoot service the watchdog through its boot and stop it
150+
# before Linux, as U-Boot does. WATCHDOG_BASE selects the instance
151+
# (0xFD4D0000 FPD / 0xFF120000 LPD), and the design's PDI must release that
152+
# block's reset. WATCHDOG_HANDOFF=1 leaves it armed for the OS instead.
153+
#WATCHDOG=1
154+
#WATCHDOG_TIMEOUT_MS=30000
155+
#WATCHDOG_BASE=0xFD4D0000
156+
#WATCHDOG_CLK_HZ=150000000
157+
#WATCHDOG_HANDOFF=1
158+
148159
# ============================================================================
149160
# Optional Debug Options (uncomment to enable)
150161
# ============================================================================

‎docs/Targets.md‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5290,6 +5290,22 @@ Key configuration options in `config/examples/versal_vmk180.config`:
52905290
- `EXT_FLASH=1` - External flash support
52915291
- `ELF=1` - ELF loading support
52925292

5293+
### System Watchdog (SWDT/WWDT)
5294+
5295+
Versal has two system watchdog timers: `LPD_SWDT` at `0xFF120000` (wired for the RPU) and `FPD_SWDT` at `0xFD4D0000` (for the APU). `WATCHDOG=1` has wolfBoot reprogram, refresh and settle one of them, for a design that arms it from its `.cdo` so a hung bootloader resets the board.
5296+
5297+
| Option | Default | Meaning |
5298+
|--------|---------|---------|
5299+
| `WATCHDOG` | `0` | Manage the watchdog through wolfBoot's boot and settle it before the OS. |
5300+
| `WATCHDOG_TIMEOUT_MS` | `30000` | Timeout to program. It has to exceed a signature verify, which is one call with no loop to refresh from. |
5301+
| `WATCHDOG_BASE` | `0xFD4D0000` | Instance to use; `0xFF120000` is the LPD one. |
5302+
| `WATCHDOG_CLK_HZ` | `150000000` | Counting clock, `FPD_LSBUS` or `LPD_LSBUS`. The default is the rate the VMK180 reference design uses, not the 100 MHz its device tree advertises; below the real rate the window shortens in proportion, so a design with a different lsbus divider must set this. |
5303+
| `WATCHDOG_HANDOFF` | `0` | Leave it armed with a full window for an OS that will take it over, instead of stopping it. The OS watchdog node then has to be enabled and its driver servicing it. |
5304+
5305+
The default stops the watchdog in `hal_prepare_boot()`, as U-Boot does before it boots Linux. wolfBoot reprograms the timer with no closed (first) window so that its own refreshes - from the hash, decompression, flash and disk loops via `wolfBoot_watchdog_feed()` - are always legal. A refresh or a disable is only honored while the open (second) window is current, elsewhere the hardware records a bad event and ignores a disable, so both wait for that window and report rather than assume. Window and generic mode are both handled and a generic-mode watchdog stays generic; Q&A mode is reported and left alone.
5306+
5307+
The PDI must release the block's reset and clock (`CRF.RST_FPD_SWDT` at `0xFD1A0314`, `CRL.RST_LPD_SWDT` at `0xFF5E034C`); wolfBoot makes no EEMI calls. Without `WATCHDOG` it neither reads nor writes the block, since a read of one still in reset can abort at EL2 - so a CDO-armed watchdog is only handled in a `WATCHDOG=1` build.
5308+
52935309
### QSPI Boot (default)
52945310

52955311
Use `config/examples/versal_vmk180.config`.

‎docs/compile.md‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ iterating over a range of flash sectors and erasing them one at a time. Setting
538538
single HAL flash erase invocation with a larger erase length versus the iterative approach. On targets where multi-sector erases are more performant, this option can be used to dramatically speed up the
539539
image swap procedure.
540540

541+
### Hardware watchdog
542+
543+
`WATCHDOG=1` tells the HAL to manage a hardware watchdog across wolfBoot's own boot, with `WATCHDOG_TIMEOUT_MS` as the timeout it programs. wolfBoot calls `wolfBoot_watchdog_feed()` from its hash, decompression, flash copy/erase and disk read loops; the weak default in `src/libwolfboot.c` is a no-op that a HAL overrides, and without `WATCHDOG` the calls compile out. A signature verify is one blocking call with no loop to refresh from, so the timeout has to exceed it. Which watchdog a target drives, and what it does with it at the handoff, is per-HAL: see `hal/versal.c`, `hal/mpfs250.c`, `hal/s32k1xx.c`, `hal/renesas-rx.c` and the per-target options in [Targets.md](Targets.md).
544+
541545
### Building with the ARM Compiler for Embedded (armclang)
542546

543547
wolfBoot can be built with the [ARM Compiler for Embedded](https://developer.arm.com/Tools%20and%20Software/Arm%20Compiler%20for%20Embedded)

‎hal/versal.c‎

Lines changed: 166 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,146 @@ uint64_t hal_get_timer_us(void)
270270
}
271271

272272

273+
/* ============================================================================
274+
* System Watchdog Timer (WWDT)
275+
* ============================================================================
276+
* Only touched under WATCHDOG=1: reading a block a PDI left in reset can
277+
* abort at EL2. A restart or a disable is honored only in the open (second)
278+
* window, so both wait for it; see docs/Targets.md.
279+
*/
280+
#ifdef WATCHDOG
281+
282+
static int versal_wwdt_wait_open(void)
283+
{
284+
uint64_t deadline;
285+
286+
deadline = timer_deadline_us((uint32_t)(WWDT_OPEN_WAIT_US > 0xFFFFFFFFULL ?
287+
0xFFFFFFFFULL : WWDT_OPEN_WAIT_US));
288+
while ((WWDT_ESR & WWDT_ESR_WSW) == 0) {
289+
if (timer_expired(deadline)) {
290+
return -1;
291+
}
292+
}
293+
return 0;
294+
}
295+
296+
static int versal_wwdt_stop(void)
297+
{
298+
uint32_t esr;
299+
300+
if (WWDT_GWCSR & WWDT_GWCSR_GWEN) {
301+
WWDT_GWCSR = WWDT_GWCSR & ~WWDT_GWCSR_GWEN;
302+
dsb();
303+
return ((WWDT_GWCSR & WWDT_GWCSR_GWEN) == 0) ? 0 : -1;
304+
}
305+
if ((WWDT_ESR & WWDT_ESR_WEN) == 0) {
306+
return 0;
307+
}
308+
if (WWDT_MWR & WWDT_MWR_AEN) {
309+
wolfBoot_printf("WDT: always-enabled, cannot be disabled\n");
310+
return -1;
311+
}
312+
/* WDP guards WEN, so it has to come down first */
313+
if (WWDT_FCR & WWDT_FCR_WDP) {
314+
WWDT_MWR = WWDT_MWR_MWC;
315+
dsb();
316+
WWDT_FCR = WWDT_FCR & ~WWDT_FCR_WDP;
317+
dsb();
318+
}
319+
if (versal_wwdt_wait_open() != 0) {
320+
wolfBoot_printf("WDT: open window never arrived, still enabled\n");
321+
return -1;
322+
}
323+
324+
WWDT_MWR = WWDT_MWR_MWC;
325+
dsb();
326+
esr = WWDT_ESR;
327+
esr &= ~(WWDT_ESR_WEN | WWDT_ESR_WSW);
328+
WWDT_ESR = esr;
329+
dsb();
330+
331+
if (WWDT_ESR & WWDT_ESR_WEN) {
332+
wolfBoot_printf("WDT: disable rejected\n");
333+
return -1;
334+
}
335+
return 0;
336+
}
337+
338+
static int versal_wwdt_arm(uint32_t timeout_ms)
339+
{
340+
uint64_t ticks;
341+
uint32_t count;
342+
343+
ticks = (uint64_t)timeout_ms * (WATCHDOG_CLK_HZ / 1000ULL);
344+
count = (ticks > WWDT_COUNT_MAX) ? (uint32_t)WWDT_COUNT_MAX
345+
: (uint32_t)ticks;
346+
347+
/* Q&A mode expects a token handshake wolfBoot does not take part in */
348+
if (WWDT_FCR & WWDT_FCR_WM) {
349+
wolfBoot_printf("WDT: Q&A mode in use, leaving it alone\n");
350+
return -1;
351+
}
352+
/* Generic mode stays generic: widen and refresh it where it is */
353+
if (WWDT_GWCSR & WWDT_GWCSR_GWEN) {
354+
WWDT_GWOR = count;
355+
WWDT_GWRR = WWDT_GWRR_REFRESH;
356+
dsb();
357+
return 0;
358+
}
359+
/* Windows are configured while disabled, so bring a running one down */
360+
if (versal_wwdt_stop() != 0) {
361+
return -1;
362+
}
363+
364+
WWDT_MWR = WWDT_MWR_MWC;
365+
dsb();
366+
WWDT_FWR = 0;
367+
WWDT_SWR = count;
368+
WWDT_FCR = 0;
369+
dsb();
370+
/* Enabling can auto-clear MWC, so reopen the space first */
371+
WWDT_MWR = WWDT_MWR_MWC;
372+
dsb();
373+
WWDT_ESR = WWDT_ESR | WWDT_ESR_WEN;
374+
dsb();
375+
376+
if ((WWDT_ESR & WWDT_ESR_WEN) == 0) {
377+
wolfBoot_printf("WDT: enable did not take, is the block in reset?\n");
378+
return -1;
379+
}
380+
return 0;
381+
}
382+
383+
/* Overrides the weak no-op in src/libwolfboot.c */
384+
void RAMFUNCTION wolfBoot_watchdog_feed(void)
385+
{
386+
if (WWDT_GWCSR & WWDT_GWCSR_GWEN) {
387+
WWDT_GWRR = WWDT_GWRR_REFRESH;
388+
dsb();
389+
return;
390+
}
391+
/* A restart in the closed window is a bad event, and Q&A mode expects a
392+
* token handshake instead of a WSW write */
393+
if ((WWDT_ESR & (WWDT_ESR_WEN | WWDT_ESR_WSW))
394+
!= (WWDT_ESR_WEN | WWDT_ESR_WSW)
395+
|| (WWDT_FCR & WWDT_FCR_WM)) {
396+
return;
397+
}
398+
WWDT_MWR = WWDT_MWR_MWC;
399+
dsb();
400+
WWDT_ESR = WWDT_ESR | WWDT_ESR_WSW;
401+
dsb();
402+
}
403+
404+
/* Overrides the weak no-op in src/sdhci.c */
405+
void sdhci_platform_wdt_pet(void)
406+
{
407+
wolfBoot_watchdog_feed();
408+
}
409+
410+
#endif /* WATCHDOG */
411+
412+
273413
/* ============================================================================
274414
* QSPI Flash Driver (GQSPI)
275415
* ============================================================================
@@ -486,8 +626,9 @@ static int qspi_dma_wait(void)
486626
{
487627
uint32_t timeout = GQSPIDMA_TIMEOUT_TRIES;
488628

489-
while (!(GQSPIDMA_ISR & GQSPIDMA_ISR_DONE) && --timeout)
490-
;
629+
while (!(GQSPIDMA_ISR & GQSPIDMA_ISR_DONE) && --timeout) {
630+
wolfBoot_watchdog_feed();
631+
}
491632

492633
if (timeout == 0) {
493634
QSPI_DEBUG_PRINTF("QSPI: DMA timeout\n");
@@ -1185,6 +1326,16 @@ void hal_init(void)
11851326
#endif
11861327
#endif
11871328

1329+
#ifdef WATCHDOG
1330+
/* ESR[LBE] names the last bad event, which is how a mis-fed or
1331+
* already-expired watchdog announces itself */
1332+
wolfBoot_printf("WDT: base 0x%x ESR 0x%x FCR 0x%x\n",
1333+
(uint32_t)WATCHDOG_BASE, (uint32_t)WWDT_ESR, (uint32_t)WWDT_FCR);
1334+
if (versal_wwdt_arm(WATCHDOG_TIMEOUT_MS) == 0) {
1335+
wolfBoot_printf("WDT: armed, %d ms\n", (int)WATCHDOG_TIMEOUT_MS);
1336+
}
1337+
#endif
1338+
11881339
#ifdef EXT_FLASH
11891340
qspi_init();
11901341
#endif
@@ -1197,6 +1348,19 @@ void hal_prepare_boot(void)
11971348
qspi_exit_4byte_addr(&qspiDev);
11981349
#endif
11991350

1351+
#ifdef WATCHDOG
1352+
#ifdef WATCHDOG_HANDOFF
1353+
/* Full window ahead of the OS, whose driver takes over from here. The
1354+
* arm can have failed, so report the state rather than assert it. */
1355+
wolfBoot_watchdog_feed();
1356+
wolfBoot_printf("WDT: handed over, ESR 0x%x\n", (uint32_t)WWDT_ESR);
1357+
#else
1358+
if (versal_wwdt_stop() == 0) {
1359+
wolfBoot_printf("WDT: stopped\n");
1360+
}
1361+
#endif
1362+
#endif
1363+
12001364
/* Flush any pending UART output (with timeout) */
12011365
#ifdef DEBUG_UART
12021366
{

‎hal/versal.h‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,80 @@
592592
#define VERSAL_SD1_BASE 0xF1050000UL
593593

594594

595+
/* ============================================================================
596+
* System Watchdog Timer (SWDT, also called WWDT)
597+
* ============================================================================
598+
* Register set per AM011 Table 141. LPD is wired for the RPU, FPD for the
599+
* APU; the design's PDI must release the block's reset and clock.
600+
*/
601+
#define VERSAL_WWDT_LPD_BASE 0xFF120000UL
602+
#define VERSAL_WWDT_FPD_BASE 0xFD4D0000UL
603+
604+
#ifndef WATCHDOG_BASE
605+
#define WATCHDOG_BASE VERSAL_WWDT_FPD_BASE
606+
#endif
607+
#ifndef WATCHDOG_TIMEOUT_MS
608+
#define WATCHDOG_TIMEOUT_MS 30000U
609+
#endif
610+
/* FPD_LSBUS or LPD_LSBUS (AM011 Table 136). The default is what the VMK180
611+
* reference design clocks them at, not the 100 MHz its DTB advertises; a
612+
* design with a different lsbus divider must say so, since a value below the
613+
* real rate shortens the window in proportion. */
614+
#ifndef WATCHDOG_CLK_HZ
615+
#define WATCHDOG_CLK_HZ 150000000UL
616+
#endif
617+
618+
/* One configured period plus slack, as the bound on a wait for the open
619+
* window. 64-bit so a large timeout cannot wrap; the caller clamps. */
620+
#define WWDT_OPEN_WAIT_US \
621+
((((uint64_t)WATCHDOG_TIMEOUT_MS) * 1000ULL) + 1000ULL)
622+
623+
/* Window timer registers, then the generic timer's separate page */
624+
#define WWDT_MWR_OFFSET 0x0000
625+
#define WWDT_ESR_OFFSET 0x0004
626+
#define WWDT_FCR_OFFSET 0x0008
627+
#define WWDT_FWR_OFFSET 0x000C
628+
#define WWDT_SWR_OFFSET 0x0010
629+
#define WWDT_GWRR_OFFSET 0x1000
630+
#define WWDT_GWCSR_OFFSET 0x2000
631+
#define WWDT_GWOR_OFFSET 0x2008
632+
633+
#define WWDT_REG(offset) \
634+
(*((volatile uint32_t*)(WATCHDOG_BASE + (offset))))
635+
#define WWDT_MWR WWDT_REG(WWDT_MWR_OFFSET)
636+
#define WWDT_ESR WWDT_REG(WWDT_ESR_OFFSET)
637+
#define WWDT_FCR WWDT_REG(WWDT_FCR_OFFSET)
638+
#define WWDT_FWR WWDT_REG(WWDT_FWR_OFFSET)
639+
#define WWDT_SWR WWDT_REG(WWDT_SWR_OFFSET)
640+
#define WWDT_GWRR WWDT_REG(WWDT_GWRR_OFFSET)
641+
#define WWDT_GWCSR WWDT_REG(WWDT_GWCSR_OFFSET)
642+
#define WWDT_GWOR WWDT_REG(WWDT_GWOR_OFFSET)
643+
644+
/* MWC opens the window register space for writing; AEN makes the watchdog
645+
* undisableable until the block is reset. */
646+
#define WWDT_MWR_MWC (1UL << 0)
647+
#define WWDT_MWR_AEN (1UL << 1)
648+
649+
/* WSW reads 1 while the open (second) window is current and writing it back
650+
* restarts the timer. WINT and WRP are W1C; FCV and LBE record bad events. */
651+
#define WWDT_ESR_WEN (1UL << 0)
652+
#define WWDT_ESR_WSW (1UL << 8)
653+
#define WWDT_ESR_WINT (1UL << 16)
654+
#define WWDT_ESR_WRP (1UL << 17)
655+
#define WWDT_ESR_FCV_SHIFT 20
656+
#define WWDT_ESR_FCV_MASK (7UL << WWDT_ESR_FCV_SHIFT)
657+
#define WWDT_ESR_LBE_SHIFT 24
658+
#define WWDT_ESR_LBE_MASK (7UL << WWDT_ESR_LBE_SHIFT)
659+
660+
/* WDP guards WEN against being cleared; WM selects Q&A over basic mode */
661+
#define WWDT_FCR_WDP (1UL << 0)
662+
#define WWDT_FCR_WM (1UL << 1)
663+
664+
#define WWDT_GWRR_REFRESH (1UL << 0)
665+
#define WWDT_GWCSR_GWEN (1UL << 0)
666+
#define WWDT_COUNT_MAX 0xFFFFFFFFUL
667+
668+
595669
/* ============================================================================
596670
* Helper Functions (C code only)
597671
* ============================================================================

0 commit comments

Comments
 (0)