This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 8983489b8bc914d887a9b2d5e1924e846f3c598d
Author: Liam Howatt <[email protected]>
AuthorDate: Mon Aug 17 09:32:52 2026 -0400

    arch/arm/stm32h5: Add IWDG support.
    
    Copy the common m3m4_v1 implementation. The only
    difference is a debug log of the register which
    contains the LSI enabled status bit. In the common
    implementation the register is RCC_CSR. On stm32h5
    it is RCC_BDCR.
    
    The common implementation is _not_ used because it's
    only built if CONFIG_STM32_COMMON_LEGACY which
    stm32h5 currently is not. IWDG is added similarly to stm32l4.
    STM32_HAVE_IP_WDG_M3M4_V1 is selected but a distinct
    source file implements it.
    
    Signed-off-by: Liam Howatt <[email protected]>
    Co-authored-by: Nathan Best <[email protected]>
---
 arch/arm/src/stm32h5/CMakeLists.txt       |   4 +
 arch/arm/src/stm32h5/Kconfig              |   1 +
 arch/arm/src/stm32h5/Make.defs            |   4 +
 arch/arm/src/stm32h5/hardware/stm32_wdg.h | 150 +++++++
 arch/arm/src/stm32h5/stm32_iwdg.c         | 703 ++++++++++++++++++++++++++++++
 arch/arm/src/stm32h5/stm32_wdg.h          | 104 +++++
 6 files changed, 966 insertions(+)

diff --git a/arch/arm/src/stm32h5/CMakeLists.txt 
b/arch/arm/src/stm32h5/CMakeLists.txt
index 8ae49dc10ca..047ac1bb59a 100644
--- a/arch/arm/src/stm32h5/CMakeLists.txt
+++ b/arch/arm/src/stm32h5/CMakeLists.txt
@@ -116,6 +116,10 @@ if(CONFIG_STM32_PWM)
   list(APPEND SRCS stm32_pwm.c)
 endif()
 
+if(CONFIG_STM32_IWDG)
+  list(APPEND SRCS stm32_iwdg.c)
+endif()
+
 # Required chip type specific files
 
 if(CONFIG_STM32_STM32H5XXXX)
diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig
index c4c8c3bdbff..e9c88e6166d 100644
--- a/arch/arm/src/stm32h5/Kconfig
+++ b/arch/arm/src/stm32h5/Kconfig
@@ -28,6 +28,7 @@ config STM32_H5_PERIPHERALS
        select STM32_HAVE_ADC_H5
        select STM32_HAVE_USART_H5
        select STM32_HAVE_I2C_H5
+       select STM32_HAVE_IP_WDG_M3M4_V1
 
 choice
        prompt "STM32 H5 Chip Selection"
diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs
index 67de7e860aa..7d35a2b939b 100644
--- a/arch/arm/src/stm32h5/Make.defs
+++ b/arch/arm/src/stm32h5/Make.defs
@@ -112,6 +112,10 @@ ifeq ($(CONFIG_STM32_PWM),y)
 CHIP_CSRCS += stm32_pwm.c
 endif
 
+ifeq ($(CONFIG_STM32_IWDG),y)
+CHIP_CSRCS += stm32_iwdg.c
+endif
+
 # Required chip type specific files
 
 ifeq ($(CONFIG_STM32_STM32H5XXXX),y)
diff --git a/arch/arm/src/stm32h5/hardware/stm32_wdg.h 
b/arch/arm/src/stm32h5/hardware/stm32_wdg.h
new file mode 100644
index 00000000000..8127a2e4d95
--- /dev/null
+++ b/arch/arm/src/stm32h5/hardware/stm32_wdg.h
@@ -0,0 +1,150 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/hardware/stm32_wdg.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_WDG_H
+#define __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_WDG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32_IWDG_KR_OFFSET     0x0000  /* Key register (32-bit) */
+#define STM32_IWDG_PR_OFFSET     0x0004  /* Prescaler register (32-bit) */
+#define STM32_IWDG_RLR_OFFSET    0x0008  /* Reload register (32-bit) */
+#define STM32_IWDG_SR_OFFSET     0x000c  /* Status register (32-bit) */
+#if defined(CONFIG_STM32_STM32F30XX)
+#  define STM32_IWDG_WINR_OFFSET 0x000c  /* Window register (32-bit) */
+#endif
+
+#define STM32_WWDG_CR_OFFSET     0x0000  /* Control Register (32-bit) */
+#define STM32_WWDG_CFR_OFFSET    0x0004  /* Configuration register (32-bit) */
+#define STM32_WWDG_SR_OFFSET     0x0008  /* Status register (32-bit) */
+
+/* Register Addresses *******************************************************/
+
+#define STM32_IWDG_KR            (STM32_IWDG_BASE+STM32_IWDG_KR_OFFSET)
+#define STM32_IWDG_PR            (STM32_IWDG_BASE+STM32_IWDG_PR_OFFSET)
+#define STM32_IWDG_RLR           (STM32_IWDG_BASE+STM32_IWDG_RLR_OFFSET)
+#define STM32_IWDG_SR            (STM32_IWDG_BASE+STM32_IWDG_SR_OFFSET)
+#if defined(CONFIG_STM32_STM32F30XX)
+#  define STM32_IWDG_WINR        (STM32_IWDG_BASE+STM32_IWDG_WINR_OFFSET)
+#endif
+
+#define STM32_WWDG_CR            (STM32_WWDG_BASE+STM32_WWDG_CR_OFFSET)
+#define STM32_WWDG_CFR           (STM32_WWDG_BASE+STM32_WWDG_CFR_OFFSET)
+#define STM32_WWDG_SR            (STM32_WWDG_BASE+STM32_WWDG_SR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* Key register (32-bit) */
+
+#define IWDG_KR_KEY_SHIFT        (0)       /* Bits 15-0: Key value (write 
only, read 0000h) */
+#define IWDG_KR_KEY_MASK         (0xffff << IWDG_KR_KEY_SHIFT)
+
+#define IWDG_KR_KEY_ENABLE       (0x5555)  /* Enable register access */
+#define IWDG_KR_KEY_DISABLE      (0x0000)  /* Disable register access */
+#define IWDG_KR_KEY_RELOAD       (0xaaaa)  /* Reload the counter */
+#define IWDG_KR_KEY_START        (0xcccc)  /* Start the watchdog */
+
+/* Prescaler register (32-bit) */
+
+#define IWDG_PR_SHIFT            (0)       /* Bits 2-0: Prescaler divider */
+#define IWDG_PR_MASK             (7 << IWDG_PR_SHIFT)
+#  define IWDG_PR_DIV4           (0 << IWDG_PR_SHIFT) /* 000: divider /4 */
+#  define IWDG_PR_DIV8           (1 << IWDG_PR_SHIFT) /* 001: divider /8 */
+#  define IWDG_PR_DIV16          (2 << IWDG_PR_SHIFT) /* 010: divider /16 */
+#  define IWDG_PR_DIV32          (3 << IWDG_PR_SHIFT) /* 011: divider /32 */
+#  define IWDG_PR_DIV64          (4 << IWDG_PR_SHIFT) /* 100: divider /64 */
+#  define IWDG_PR_DIV128         (5 << IWDG_PR_SHIFT) /* 101: divider /128 */
+#  define IWDG_PR_DIV256         (6 << IWDG_PR_SHIFT) /* 11x: divider /256 */
+
+/* Reload register (32-bit) */
+
+#define IWDG_RLR_RL_SHIFT        (0)       /* Bits11:0 RL[11:0]: Watchdog 
counter reload value */
+#define IWDG_RLR_RL_MASK         (0x0fff << IWDG_RLR_RL_SHIFT)
+
+#define IWDG_RLR_MAX             (0xfff)
+
+/* Status register (32-bit) */
+
+#define IWDG_SR_PVU              (1 << 0)  /* Bit 0: Watchdog prescaler value 
update */
+#define IWDG_SR_RVU              (1 << 1)  /* Bit 1: Watchdog counter reload 
value update */
+
+#if defined(CONFIG_STM32_STM32F30XX)
+#  define IWDG_SR_WVU            (1 << 2)  /* Bit 2:  */
+#endif
+
+/* Window register (32-bit) */
+
+#if defined(CONFIG_STM32_STM32F30XX)
+#  define IWDG_WINR_SHIFT        (0)
+#  define IWDG_WINR_MASK         (0x0fff << IWDG_WINR_SHIFT)
+#endif
+
+/* Control Register (32-bit) */
+
+#define WWDG_CR_T_SHIFT          (0)       /* Bits 6:0 T[6:0]: 7-bit counter 
(MSB to LSB) */
+#define WWDG_CR_T_MASK           (0x7f << WWDG_CR_T_SHIFT)
+#  define WWDG_CR_T_MAX          (0x3f << WWDG_CR_T_SHIFT)
+#  define WWDG_CR_T_RESET        (0x40 << WWDG_CR_T_SHIFT)
+#define WWDG_CR_WDGA             (1 << 7)  /* Bit 7: Activation bit */
+
+/* Configuration register (32-bit) */
+
+#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_EWI             (1 << 9)  /* Bit 9: Early Wakeup Interrupt */
+
+/* Status register (32-bit) */
+
+#define WWDG_SR_EWIF             (1 << 0)  /* Bit 0: Early Wakeup Interrupt 
Flag */
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Data
+ ****************************************************************************/
+
+/****************************************************************************
+ * Public Functions Prototypes
+ ****************************************************************************/
+
+#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_WDG_H */
diff --git a/arch/arm/src/stm32h5/stm32_iwdg.c 
b/arch/arm/src/stm32h5/stm32_iwdg.c
new file mode 100644
index 00000000000..c4a4820e45a
--- /dev/null
+++ b/arch/arm/src/stm32h5/stm32_iwdg.c
@@ -0,0 +1,703 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/stm32_iwdg.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+
+#include <nuttx/debug.h>
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/timers/watchdog.h>
+#include <arch/board/board.h>
+
+#include "arm_internal.h"
+#include "stm32_rcc.h"
+#include "hardware/stm32_dbgmcu.h"
+#include "stm32_wdg.h"
+
+#if defined(CONFIG_WATCHDOG) && defined(CONFIG_STM32_IWDG)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Clocking *****************************************************************/
+
+/* The minimum frequency of the IWDG clock is:
+ *
+ *  Fmin = Flsi / 256
+ *
+ * So the maximum delay (in milliseconds) is then:
+ *
+ *   1000 * IWDG_RLR_MAX / Fmin
+ *
+ * For example, if Flsi = 30Khz (the nominal, uncalibrated value), then the
+ * maximum delay is:
+ *
+ *   Fmin = 117.1875
+ *   1000 * 4095 / Fmin = 34,944 MSec
+ */
+
+#define IWDG_FMIN       (STM32_LSI_FREQUENCY / 256)
+#define IWDG_MAXTIMEOUT (1000 * IWDG_RLR_MAX / IWDG_FMIN)
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_STM32_IWDG_DEFTIMOUT
+#  define CONFIG_STM32_IWDG_DEFTIMOUT IWDG_MAXTIMEOUT
+#endif
+
+#ifndef CONFIG_DEBUG_WATCHDOG_INFO
+#  undef CONFIG_STM32_IWDG_REGDEBUG
+#endif
+
+/* REVISIT:  It appears that you can only setup the prescaler and reload
+ * registers once.  After that, the SR register's PVU and RVU bits never go
+ * to zero.  So we defer setting up these registers until the watchdog
+ * is started, then refuse any further attempts to change timeout.
+ */
+
+#define CONFIG_STM32_IWDG_ONETIMESETUP 1
+
+/* REVISIT:  Another possibility is that we CAN change the prescaler and
+ * reload values after starting the timer.  This option is untested but the
+ * implementation place conditioned on the following:
+ */
+
+#undef CONFIG_STM32_IWDG_DEFERREDSETUP
+
+/* But you can only try one at a time */
+
+#if defined(CONFIG_STM32_IWDG_ONETIMESETUP) && 
defined(CONFIG_STM32_IWDG_DEFERREDSETUP)
+#  error "Both CONFIG_STM32_IWDG_ONETIMESETUP and 
CONFIG_STM32_IWDG_DEFERREDSETUP are defined"
+#endif
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure provides the private representation of the "lower-half"
+ * driver state structure.  This structure must be cast-compatible with the
+ * well-known watchdog_lowerhalf_s structure.
+ */
+
+struct stm32_lowerhalf_s
+{
+  const struct watchdog_ops_s  *ops; /* Lower half operations */
+  uint32_t lsifreq;                  /* The calibrated frequency of the LSI 
oscillator */
+  uint32_t timeout;                  /* The (actual) selected timeout */
+  uint32_t lastreset;                /* The last reset time */
+  bool     started;                  /* true: The watchdog timer has been 
started */
+  uint8_t  prescaler;                /* Clock prescaler value */
+  uint16_t reload;                   /* Timer reload value */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* Register operations ******************************************************/
+
+#ifdef CONFIG_STM32_IWDG_REGDEBUG
+static uint16_t stm32_getreg(uint32_t addr);
+static void     stm32_putreg(uint16_t val, uint32_t addr);
+#else
+#  define       stm32_getreg(addr)     getreg16(addr)
+#  define       stm32_putreg(val,addr) putreg16(val,addr)
+#endif
+
+static inline void stm32_setprescaler(struct stm32_lowerhalf_s *priv);
+
+/* "Lower half" driver methods **********************************************/
+
+static int      stm32_start(struct watchdog_lowerhalf_s *lower);
+static int      stm32_stop(struct watchdog_lowerhalf_s *lower);
+static int      stm32_keepalive(struct watchdog_lowerhalf_s *lower);
+static int      stm32_getstatus(struct watchdog_lowerhalf_s *lower,
+                  struct watchdog_status_s *status);
+static int      stm32_settimeout(struct watchdog_lowerhalf_s *lower,
+                  uint32_t timeout);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+/* "Lower half" driver methods */
+
+static const struct watchdog_ops_s g_wdgops =
+{
+  .start      = stm32_start,
+  .stop       = stm32_stop,
+  .keepalive  = stm32_keepalive,
+  .getstatus  = stm32_getstatus,
+  .settimeout = stm32_settimeout,
+  .capture    = NULL,
+  .ioctl      = NULL,
+};
+
+/* "Lower half" driver state */
+
+static struct stm32_lowerhalf_s g_wdgdev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_getreg
+ *
+ * Description:
+ *   Get the contents of an STM32 IWDG register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_IWDG_REGDEBUG
+static uint16_t stm32_getreg(uint32_t addr)
+{
+  static uint32_t prevaddr = 0;
+  static uint32_t count = 0;
+  static uint16_t preval = 0;
+
+  /* Read the value from the register */
+
+  uint16_t val = getreg16(addr);
+
+  /* Is this the same value that we read from the same register last time?
+   * Are we polling the register?  If so, suppress some of the output.
+   */
+
+  if (addr == prevaddr && val == preval)
+    {
+      if (count == 0xffffffff || ++count > 3)
+        {
+          if (count == 4)
+            {
+              wdinfo("...\n");
+            }
+
+          return val;
+        }
+    }
+
+  /* No this is a new address or value */
+
+  else
+    {
+      /* Did we print "..." for the previous value? */
+
+      if (count > 3)
+        {
+          /* Yes.. then show how many times the value repeated */
+
+          wdinfo("[repeats %d more times]\n", count - 3);
+        }
+
+      /* Save the new address, value, and count */
+
+      prevaddr = addr;
+      preval   = val;
+      count    = 1;
+    }
+
+  /* Show the register value read */
+
+  wdinfo("%08" PRIx32 "->%04x\n", addr, val);
+  return val;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_putreg
+ *
+ * Description:
+ *   Set the contents of an STM32 register to a value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_IWDG_REGDEBUG
+static void stm32_putreg(uint16_t val, uint32_t addr)
+{
+  /* Show the register value being written */
+
+  wdinfo("%08" PRIx32 "<-%04x\n", addr, val);
+
+  /* Write the value */
+
+  putreg16(val, addr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_setprescaler
+ *
+ * Description:
+ *   Set up the prescaler and reload values.  This seems to be something
+ *   that can only be done one time.
+ *
+ * Input Parameters:
+ *   priv   - A pointer the internal representation of the "lower-half"
+ *             driver state structure.
+ *
+ ****************************************************************************/
+
+static inline void stm32_setprescaler(struct stm32_lowerhalf_s *priv)
+{
+  /* Enable write access to IWDG_PR and IWDG_RLR registers */
+
+  stm32_putreg(IWDG_KR_KEY_ENABLE, STM32_IWDG_KR);
+
+  /* Wait for the PVU and RVU bits to be reset be hardware.  These bits
+   * were set the last time that the PR register was written and may not
+   * yet be cleared.
+   *
+   * If the setup is only permitted one time, then this wait should not
+   * be necessary.
+   */
+
+#ifndef CONFIG_STM32_IWDG_ONETIMESETUP
+  while ((stm32_getreg(STM32_IWDG_SR) & (IWDG_SR_PVU | IWDG_SR_RVU)) != 0);
+#endif
+
+  /* Set the prescaler */
+
+  stm32_putreg((uint16_t)priv->prescaler << IWDG_PR_SHIFT, STM32_IWDG_PR);
+
+  /* Set the reload value */
+
+  stm32_putreg((uint16_t)priv->reload, STM32_IWDG_RLR);
+
+  /* Reload the counter (and disable write access) */
+
+  stm32_putreg(IWDG_KR_KEY_RELOAD, STM32_IWDG_KR);
+}
+
+/****************************************************************************
+ * Name: stm32_start
+ *
+ * Description:
+ *   Start the watchdog timer, resetting the time to the current timeout,
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_start(struct watchdog_lowerhalf_s *lower)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  wdinfo("Entry: started\n");
+  DEBUGASSERT(priv);
+
+  /* Have we already been started? */
+
+  if (!priv->started)
+    {
+      /* REVISIT: It appears that you can only setup the prescaler and reload
+       * registers once. After that, the SR register's PVU and RVU bits never
+       * go to 0. So we defer setting up these registers until the watchdog
+       * is started, then refuse any further attempts to change timeout.
+       */
+
+      /* Set up prescaler and reload value for the selected timeout before
+       * starting the watchdog timer.
+       */
+
+#if defined(CONFIG_STM32_IWDG_ONETIMESETUP) || 
defined(CONFIG_STM32_IWDG_DEFERREDSETUP)
+      stm32_setprescaler(priv);
+#endif
+
+      /* Enable IWDG (the LSI oscillator will be enabled by hardware).  NOTE:
+       * If the "Hardware watchdog" feature is enabled through the device
+       * option bits, the watchdog is automatically enabled at power-on.
+       */
+
+      flags           = enter_critical_section();
+      stm32_putreg(IWDG_KR_KEY_START, STM32_IWDG_KR);
+      priv->lastreset = clock_systime_ticks();
+      priv->started   = true;
+      leave_critical_section(flags);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_stop
+ *
+ * Description:
+ *   Stop the watchdog timer
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_stop(struct watchdog_lowerhalf_s *lower)
+{
+  /* There is no way to disable the IDWG timer once it has been started */
+
+  wdinfo("Entry\n");
+  return -ENOSYS;
+}
+
+/****************************************************************************
+ * Name: stm32_keepalive
+ *
+ * Description:
+ *   Reset the watchdog timer to the current timeout value, prevent any
+ *   imminent watchdog timeouts.  This is sometimes referred as "pinging"
+ *   the watchdog timer or "petting the dog".
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the
+ *           "lower-half" driver state structure.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_keepalive(struct watchdog_lowerhalf_s *lower)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  wdinfo("Entry\n");
+
+  /* Reload the IWDG timer */
+
+  flags = enter_critical_section();
+  stm32_putreg(IWDG_KR_KEY_RELOAD, STM32_IWDG_KR);
+  priv->lastreset = clock_systime_ticks();
+  leave_critical_section(flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_getstatus
+ *
+ * Description:
+ *   Get the current watchdog timer status
+ *
+ * Input Parameters:
+ *   lower  - A pointer the publicly visible representation of the
+ *            "lower-half" driver state structure.
+ *   status - The location to return the watchdog status information.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_getstatus(struct watchdog_lowerhalf_s *lower,
+                           struct watchdog_status_s *status)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+  uint32_t ticks;
+  uint32_t elapsed;
+
+  wdinfo("Entry\n");
+  DEBUGASSERT(priv);
+
+  /* Return the status bit */
+
+  status->flags = WDFLAGS_RESET;
+  if (priv->started)
+    {
+      status->flags |= WDFLAGS_ACTIVE;
+    }
+
+  /* Return the actual timeout in milliseconds */
+
+  status->timeout = priv->timeout;
+
+  /* Get the elapsed time since the last ping */
+
+  ticks   = clock_systime_ticks() - priv->lastreset;
+  elapsed = (int32_t)TICK2MSEC(ticks);
+
+  if (elapsed > priv->timeout)
+    {
+      elapsed = priv->timeout;
+    }
+
+  /* Return the approximate time until the watchdog timer expiration */
+
+  status->timeleft = priv->timeout - elapsed;
+
+  wdinfo("Status     :\n");
+  wdinfo("  flags    : %08" PRIx32 "\n", status->flags);
+  wdinfo("  timeout  : %" PRId32 "\n", status->timeout);
+  wdinfo("  timeleft : %" PRId32 "\n", status->timeleft);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_settimeout
+ *
+ * Description:
+ *   Set a new timeout value (and reset the watchdog timer)
+ *
+ * Input Parameters:
+ *   lower   - A pointer the publicly visible representation of the
+ *             "lower-half" driver state structure.
+ *   timeout - The new timeout value in milliseconds.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_settimeout(struct watchdog_lowerhalf_s *lower,
+                            uint32_t timeout)
+{
+  struct stm32_lowerhalf_s *priv = (struct stm32_lowerhalf_s *)lower;
+  uint32_t fiwdg;
+  uint64_t reload;
+  int prescaler;
+  int shift;
+
+  wdinfo("Entry: timeout=%" PRId32 "\n", timeout);
+  DEBUGASSERT(priv);
+
+  /* Can this timeout be represented? */
+
+  if (timeout < 1 || timeout > IWDG_MAXTIMEOUT)
+    {
+      wderr("ERROR: Cannot represent timeout=%" PRId32 " > %d\n",
+            timeout, IWDG_MAXTIMEOUT);
+      return -ERANGE;
+    }
+
+  /* REVISIT:  It appears that you can only setup the prescaler and reload
+   * registers once.  After that, the SR register's PVU and RVU bits never go
+   * to zero.
+   */
+
+#ifdef CONFIG_STM32_IWDG_ONETIMESETUP
+  if (priv->started)
+    {
+      wdwarn("WARNING: Timer is already started\n");
+      return -EBUSY;
+    }
+#endif
+
+  /* Select the smallest prescaler that will result in a reload value that is
+   * less than the maximum.
+   */
+
+  for (prescaler = 0; ; prescaler++)
+    {
+      /* PR = 0 -> Divider = 4   = 1 << 2
+       * PR = 1 -> Divider = 8   = 1 << 3
+       * PR = 2 -> Divider = 16  = 1 << 4
+       * PR = 3 -> Divider = 32  = 1 << 5
+       * PR = 4 -> Divider = 64  = 1 << 6
+       * PR = 5 -> Divider = 128 = 1 << 7
+       * PR = 6 -> Divider = 256 = 1 << 8
+       * PR = n -> Divider       = 1 << (n+2)
+       */
+
+      shift = prescaler + 2;
+
+      /* Get the IWDG counter frequency in Hz. For a nominal 32Khz LSI clock,
+       * this is value in the range of 7500 and 125.
+       */
+
+      fiwdg = priv->lsifreq >> shift;
+
+      /* We want:
+       *  1000 * reload / Fiwdg = timeout
+       * Or:
+       *  reload = Fiwdg * timeout / 1000
+       */
+
+      reload = (uint64_t)fiwdg * (uint64_t)timeout / 1000;
+
+      /* If this reload valid is less than the maximum or we are not ready
+       * at the prescaler value, then break out of the loop to use these
+       * settings.
+       */
+
+      if (reload <= IWDG_RLR_MAX || prescaler == 6)
+        {
+          /* Note that we explicitly break out of the loop rather than using
+           * the 'for' loop termination logic because we do not want the
+           * value of prescaler to be incremented.
+           */
+
+          break;
+        }
+    }
+
+  /* Make sure that the final reload value is within range */
+
+  if (reload > IWDG_RLR_MAX)
+    {
+      reload = IWDG_RLR_MAX;
+    }
+
+  /* Get the actual timeout value in milliseconds.
+   *
+   * We have:
+   *  reload = Fiwdg * timeout / 1000
+   * So we want:
+   *  timeout = 1000 * reload / Fiwdg
+   */
+
+  priv->timeout = (1000 * (uint32_t)reload) / fiwdg;
+
+  /* Save setup values for later use */
+
+  priv->prescaler = prescaler;
+  priv->reload    = reload;
+
+  /* Write the prescaler and reload values to the IWDG registers.
+   *
+   * REVISIT:  It appears that you can only setup the prescaler and reload
+   * registers once.  After that, the SR register's PVU and RVU bits never go
+   * to zero.
+   */
+
+#ifndef CONFIG_STM32_IWDG_ONETIMESETUP
+  /* If CONFIG_STM32_IWDG_DEFERREDSETUP is selected, then perform the
+   * register configuration only if the timer has been started.
+   */
+
+#ifdef CONFIG_STM32_IWDG_DEFERREDSETUP
+  if (priv->started)
+#endif
+    {
+      stm32_setprescaler(priv);
+    }
+#endif
+
+  wdinfo("prescaler=%d fiwdg=%" PRId32 " reload=%" PRId64 "\n",
+         prescaler, fiwdg, reload);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_iwdginitialize
+ *
+ * Description:
+ *   Initialize the IWDG watchdog timer.  The watchdog timer is initialized
+ *   and registers as 'devpath'.  The initial state of the watchdog timer is
+ *   disabled.
+ *
+ * Input Parameters:
+ *   devpath - The full path to the watchdog.  This should be of the form
+ *     /dev/watchdog0
+ *   lsifreq - The calibrated LSI clock frequency
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void stm32_iwdginitialize(const char *devpath, uint32_t lsifreq)
+{
+  struct stm32_lowerhalf_s *priv = &g_wdgdev;
+
+  wdinfo("Entry: devpath=%s lsifreq=%" PRId32 "\n", devpath, lsifreq);
+
+  /* NOTE we assume that clocking to the IWDG has already been provided by
+   * the RCC initialization logic.
+   */
+
+  /* Initialize the driver state structure. */
+
+  priv->ops     = &g_wdgops;
+  priv->lsifreq = lsifreq;
+  priv->started = false;
+
+  /* Make sure that the LSI oscillator is enabled.  NOTE:  The LSI oscillator
+   * is enabled here but is not disabled by this file, because this file does
+   * not know the global usage of the oscillator.  Any clock management
+   * logic (say, as part of a power management scheme) needs handle other
+   * LSI controls outside of this file.
+   */
+
+  stm32_rcc_enablelsi();
+  wdinfo("RCC BDCR: %08" PRIx32 "\n", getreg32(STM32_RCC_BDCR));
+
+  /* Select an arbitrary initial timeout value.  But don't start the watchdog
+   * yet. NOTE: If the "Hardware watchdog" feature is enabled through the
+   * device option bits, the watchdog is automatically enabled at power-on.
+   */
+
+  stm32_settimeout((struct watchdog_lowerhalf_s *)priv,
+                   CONFIG_STM32_IWDG_DEFTIMOUT);
+
+  /* Register the watchdog driver as /dev/watchdog0 */
+
+  watchdog_register(devpath, (struct watchdog_lowerhalf_s *)priv);
+
+  /* When the microcontroller enters debug mode (Cortex-M4F core halted),
+   * the IWDG counter either continues to work normally or stops, depending
+   * on DBG_IWDG_STOP configuration bit in DBG module.
+   */
+
+#if defined(CONFIG_STM32_JTAG_FULL_ENABLE) || \
+    defined(CONFIG_STM32_JTAG_NOJNTRST_ENABLE) || \
+    defined(CONFIG_STM32_JTAG_SW_ENABLE)
+    {
+#if defined(CONFIG_STM32_STM32F20XX) || defined(CONFIG_STM32_STM32F30XX) || \
+    defined(CONFIG_STM32_STM32F4XXX) || defined(CONFIG_STM32_STM32L15XX)
+      uint32_t cr = getreg32(STM32_DBGMCU_APB1_FZ);
+      cr |= DBGMCU_APB1_IWDGSTOP;
+      putreg32(cr, STM32_DBGMCU_APB1_FZ);
+#else /* if defined(CONFIG_STM32_STM32F10XX) */
+      uint32_t cr = getreg32(STM32_DBGMCU_CR);
+      cr |= DBGMCU_CR_IWDGSTOP;
+      putreg32(cr, STM32_DBGMCU_CR);
+#endif
+    }
+#endif
+}
+
+#endif /* CONFIG_WATCHDOG && CONFIG_STM32_IWDG */
diff --git a/arch/arm/src/stm32h5/stm32_wdg.h b/arch/arm/src/stm32h5/stm32_wdg.h
new file mode 100644
index 00000000000..0beaef1ddbf
--- /dev/null
+++ b/arch/arm/src/stm32h5/stm32_wdg.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/stm32_wdg.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H5_STM32_WDG_H
+#define __ARCH_ARM_SRC_STM32H5_STM32_WDG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32_wdg.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_iwdginitialize
+ *
+ * Description:
+ *   Initialize the IWDG watchdog time.  The watchdog timer is initialized
+ *   and registers as 'devpath.  The initial state of the watchdog time is
+ *   disabled.
+ *
+ * Input Parameters:
+ *   devpath - The full path to the watchdog.  This should be of the form
+ *     /dev/watchdog0
+ *   lsifreq - The calibrated LSI clock frequency
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_IWDG
+void stm32_iwdginitialize(const char *devpath, uint32_t lsifreq);
+#endif
+
+/****************************************************************************
+ * Name: stm32_wwdginitialize
+ *
+ * Description:
+ *   Initialize the WWDG watchdog time.  The watchdog timer is initializeed
+ *   and registers as 'devpath.  The initial state of the watchdog time is
+ *   disabled.
+ *
+ * Input Parameters:
+ *   devpath - The full path to the watchdog.  This should be of the form
+ *     /dev/watchdog0
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_WWDG
+void stm32_wwdginitialize(const char *devpath);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ARCH_ARM_SRC_STM32H5_STM32_WDG_H */

Reply via email to