arch/arm/stm32h7: Fix and enhance WWDG (Window Watchdog) support

This patch addresses several issues and adds enhancements to the WWDG
(Window Watchdog) implementation for the STM32H7 platform. The changes
include:

- Extend the definitions of WWDG_CFR_PCLK1 macros to support dividers
  up to 128, and update the stm32_settimeout() function to consider this
  extended range.
- Fix the "elapsed" calculations in the stm32_getstatus() function to
  ensure correct time remaining calculations.
- Clear the EWIF (Early Wakeup Interrupt Flag) bit in the stm32_start()
  function, as this bit might be set by hardware before the watchdog is
  actually started.
- Initialize the WWDG clock in the RCC_APB3ENR register and set the
  RCC_GCR_WW1RSC bit as per the STM32 reference manual to ensure proper
  behavior when enabling the WWDG1.

Signed-off-by: Szymon Magrian <szymon.magrian@hexagon.com>
This commit is contained in:
MASZ 2025-09-10 14:29:54 +02:00 committed by Xiang Xiao
parent 3af0ab64bb
commit 94ad260843
3 changed files with 45 additions and 13 deletions

View file

@ -118,12 +118,16 @@
#define WWDG_CFR_W_SHIFT (0) /* Bits 6:0 W[6:0] 7-bit window value */
#define WWDG_CFR_W_MASK (0x7f << WWDG_CFR_W_SHIFT)
#define WWDG_CFR_WDGTB_SHIFT (7) /* Bits 8:7 [1:0]: Timer Base */
#define WWDG_CFR_WDGTB_MASK (3 << WWDG_CFR_WDGTB_SHIFT)
# define WWDG_CFR_PCLK1 (0 << WWDG_CFR_WDGTB_SHIFT) /* 00: CK Counter Clock (PCLK1 div 4096) div 1 */
# define WWDG_CFR_PCLK1d2 (1 << WWDG_CFR_WDGTB_SHIFT) /* 01: CK Counter Clock (PCLK1 div 4096) div 2 */
# define WWDG_CFR_PCLK1d4 (2 << WWDG_CFR_WDGTB_SHIFT) /* 10: CK Counter Clock (PCLK1 div 4096) div 4 */
# define WWDG_CFR_PCLK1d8 (3 << WWDG_CFR_WDGTB_SHIFT) /* 11: CK Counter Clock (PCLK1 div 4096) div 8 */
#define WWDG_CFR_WDGTB_SHIFT (11) /* Bits 13:11 [2:0]: Timer Base */
#define WWDG_CFR_WDGTB_MASK (7 << WWDG_CFR_WDGTB_SHIFT)
# define WWDG_CFR_PCLK1 (0 << WWDG_CFR_WDGTB_SHIFT) /* 000: CK Counter Clock (PCLK1 div 4096) div 1 */
# define WWDG_CFR_PCLK1d2 (1 << WWDG_CFR_WDGTB_SHIFT) /* 001: CK Counter Clock (PCLK1 div 4096) div 2 */
# define WWDG_CFR_PCLK1d4 (2 << WWDG_CFR_WDGTB_SHIFT) /* 010: CK Counter Clock (PCLK1 div 4096) div 4 */
# define WWDG_CFR_PCLK1d8 (3 << WWDG_CFR_WDGTB_SHIFT) /* 011: CK Counter Clock (PCLK1 div 4096) div 8 */
# define WWDG_CFR_PCLK1d16 (4 << WWDG_CFR_WDGTB_SHIFT) /* 100: CK Counter Clock (PCLK1 div 4096) div 16 */
# define WWDG_CFR_PCLK1d32 (5 << WWDG_CFR_WDGTB_SHIFT) /* 101: CK Counter Clock (PCLK1 div 4096) div 32 */
# define WWDG_CFR_PCLK1d64 (6 << WWDG_CFR_WDGTB_SHIFT) /* 110: CK Counter Clock (PCLK1 div 4096) div 64 */
# define WWDG_CFR_PCLK1d128 (7 << WWDG_CFR_WDGTB_SHIFT) /* 111: CK Counter Clock (PCLK1 div 4096) div 128 */
#define WWDG_CFR_EWI (1 << 9) /* Bit 9: Early Wakeup Interrupt */

View file

@ -49,7 +49,7 @@
/* The minimum frequency of the WWDG clock is:
*
* Fmin = PCLK1 / 4096 / 8
* Fmin = PCLK1 / 4096 / 128
*
* So the maximum delay (in milliseconds) is then:
*
@ -57,11 +57,11 @@
*
* For example, if PCLK1 = 42MHz, then the maximum delay is:
*
* Fmin = 1281.74
* 1000 * 64 / Fmin = 49.93 msec
* Fmin = 42,000,000 / 4096 / 128 = ~80.11 Hz
* 1000 * 64 / Fmin = ~798.92 msec
*/
#define WWDG_FMIN (STM32_PCLK1_FREQUENCY / 4096 / 8)
#define WWDG_FMIN (STM32_PCLK1_FREQUENCY / 4096 / 128)
#define WWDG_MAXTIMEOUT (1000 * (WWDG_CR_T_MAX+1) / WWDG_FMIN)
/* Configuration ************************************************************/
@ -334,6 +334,10 @@ static int stm32_start(struct watchdog_lowerhalf_s *lower)
wdinfo("Entry\n");
DEBUGASSERT(priv);
/* Clear the pending interrupt bit */
modifyreg32(STM32_WWDG_SR, WWDG_SR_EWIF, 0);
/* The watchdog is always disabled after a reset. It is enabled by setting
* the WDGA bit in the WWDG_CR register, then it cannot be disabled again
* except by a reset.
@ -454,13 +458,13 @@ static int stm32_getstatus(struct watchdog_lowerhalf_s *lower,
/* Get the time remaining until the watchdog expires (in milliseconds) */
reload = (stm32_getreg(STM32_WWDG_CR) >> WWDG_CR_T_SHIFT) & 0x7f;
elapsed = priv->reload - reload;
elapsed = (WWDG_CR_T_RESET | priv->reload) - reload;
status->timeleft = (priv->timeout * elapsed) / (priv->reload + 1);
wdinfo("Status :\n");
wdinfo(" flags : %08x\n", status->flags);
wdinfo(" timeout : %d\n", status->timeout);
wdinfo(" timeleft : %d\n", status->flags);
wdinfo(" timeleft : %d\n", status->timeleft);
return OK;
}
@ -548,7 +552,7 @@ static int stm32_settimeout(struct watchdog_lowerhalf_s *lower,
wdinfo("wdgtb=%d fwwdg=%d reload=%d timeout=%d\n",
wdgtb, fwwdg, reload, 1000 * (reload + 1) / fwwdg);
#endif
if (reload <= WWDG_CR_T_MAX || wdgtb == 3)
if (reload <= WWDG_CR_T_MAX || wdgtb == 7)
{
/* Note that we explicitly break out of the loop rather than using
* the 'for' loop termination logic because we do not want the

View file

@ -595,6 +595,30 @@ static inline void rcc_enableapb3(void)
regval |= RCC_APB3ENR_LTDCEN;
#endif
#ifdef CONFIG_STM32H7_WWDG
/* RM0433 Rev 8
* Reference manual - STM32H742, STM32H743/753 and STM32H750 Value line
* advanced Arm-based 32-bit MCUs
* https://www.st.com/resource/en/reference_manual/rm0433-stm32h742-
* stm32h743753-and-stm32h750-value-line-advanced-armbased-32bit-mcus-
* stmicroelectronics.pdf
* (Access date: 10-09-2025)
* Reset and Clock Control (RCC) -> RCC clock block functional
* description --> Kernel clock selection -> Watchdog clocks (page 365)
* "before enabling the WWDG1, the application must set the WW1RSC
* bit to 1.
* If the WW1RSC remains 0, when the WWDG1 is enabled, the behavior is
* not guaranteed"
*/
uint32_t rcc_gcr = getreg32(STM32_RCC_GCR);
rcc_gcr |= RCC_GCR_WW1RSC;
putreg32(rcc_gcr, STM32_RCC_GCR);
regval |= RCC_APB3ENR_WWDG1EN;
#endif
putreg32(regval, STM32_RCC_APB3ENR); /* Enable peripherals */
}