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

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

commit a7c5016a4124cad0d367982572a52961a776f8de
Author: Joshua Lange <jla...@2g-eng.com>
AuthorDate: Wed Feb 5 14:12:59 2020 -0600

    Added basic support for IWDG and WWDG to STM32H7
---
 arch/arm/src/stm32h7/Kconfig              |  10 +
 arch/arm/src/stm32h7/Make.defs            |   8 +
 arch/arm/src/stm32h7/hardware/stm32_wdg.h | 158 ++++++
 arch/arm/src/stm32h7/stm32_iwdg.c         | 699 ++++++++++++++++++++++++++
 arch/arm/src/stm32h7/stm32_wdg.h          | 119 +++++
 arch/arm/src/stm32h7/stm32_wwdg.c         | 798 ++++++++++++++++++++++++++++++
 6 files changed, 1792 insertions(+)

diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig
index 3c07db3..2b552ce 100644
--- a/arch/arm/src/stm32h7/Kconfig
+++ b/arch/arm/src/stm32h7/Kconfig
@@ -341,6 +341,16 @@ config STM32H7_SDMMC2
        select ARCH_HAVE_SDIO_PREFLIGHT
        select SDIO_BLOCKSETUP
 
+config STM32H7_IWDG
+       bool "IWDG"
+       default n
+       select WATCHDOG
+
+config STM32H7_WWDG
+       bool "WWDG"
+       default n
+       select WATCHDOG
+
 menu "STM32H7 I2C Selection"
 
 config STM32H7_I2C1
diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs
index a8e37aa..4a63041 100644
--- a/arch/arm/src/stm32h7/Make.defs
+++ b/arch/arm/src/stm32h7/Make.defs
@@ -217,3 +217,11 @@ ifneq ($(CONFIG_ARCH_CUSTOM_PMINIT),y)
 CHIP_CSRCS += stm32_pminitialize.c
 endif
 endif
+
+ifeq ($(CONFIG_STM32H7_IWDG),y)
+CHIP_CSRCS += stm32_iwdg.c
+endif
+
+ifeq ($(CONFIG_STM32H7_WWDG),y)
+CHIP_CSRCS += stm32_wwdg.c
+endif
diff --git a/arch/arm/src/stm32h7/hardware/stm32_wdg.h 
b/arch/arm/src/stm32h7/hardware/stm32_wdg.h
new file mode 100644
index 0000000..7004b60
--- /dev/null
+++ b/arch/arm/src/stm32h7/hardware/stm32_wdg.h
@@ -0,0 +1,158 @@
+/************************************************************************************
+ * arch/arm/src/stm32h7/hardware/stm32_wdg.h
+ *
+ *   Copyright (C) 2009, 2011-2013 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ 
************************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32_HARDWARE_STM32H7_WDG_H
+#define __ARCH_ARM_SRC_STM32_HARDWARE_STM32H7_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) */
+#define STM32_IWDG_WINR_OFFSET   0x000c  /* Window register (32-bit) */
+
+#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_IWDG1_BASE+STM32_IWDG_KR_OFFSET)
+#define STM32_IWDG_PR            (STM32_IWDG1_BASE+STM32_IWDG_PR_OFFSET)
+#define STM32_IWDG_RLR           (STM32_IWDG1_BASE+STM32_IWDG_RLR_OFFSET)
+#define STM32_IWDG_SR            (STM32_IWDG1_BASE+STM32_IWDG_SR_OFFSET)
+#define STM32_IWDG_WINR          (STM32_IWDG1_BASE+STM32_IWDG_WINR_OFFSET)
+
+#define STM32_WWDG_CR            (STM32_WWDG1_BASE+STM32_WWDG_CR_OFFSET)
+#define STM32_WWDG_CFR           (STM32_WWDG1_BASE+STM32_WWDG_CFR_OFFSET)
+#define STM32_WWDG_SR            (STM32_WWDG1_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
+ 
************************************************************************************/
+
+#endif /* __ARCH_ARM_SRC_STM32_HARDWARE_STM32H7_WDG_H */
diff --git a/arch/arm/src/stm32h7/stm32_iwdg.c 
b/arch/arm/src/stm32h7/stm32_iwdg.c
new file mode 100644
index 0000000..184592a
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_iwdg.c
@@ -0,0 +1,699 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_iwdg.c
+ *
+ *   Copyright (C) 2012, 2016 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <stdint.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/clock.h>
+#include <nuttx/timers/watchdog.h>
+#include <arch/board/board.h>
+
+#include "up_arch.h"
+#include "stm32_rcc.h"
+#include "stm32_wdg.h"
+
+#if defined(CONFIG_WATCHDOG) && defined(CONFIG_STM32H7_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_STM32H7_IWDG_DEFTIMOUT
+#  define CONFIG_STM32H7_IWDG_DEFTIMOUT IWDG_MAXTIMEOUT
+#endif
+
+#ifndef CONFIG_DEBUG_WATCHDOG_INFO
+#  undef CONFIG_STM32H7_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_STM32H7_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_STM32H7_IWDG_DEFERREDSETUP
+
+/* But you can only try one at a time */
+
+#if defined(CONFIG_STM32H7_IWDG_ONETIMESETUP) && 
defined(CONFIG_STM32H7_IWDG_DEFERREDSETUP)
+#  error "Both CONFIG_STM32H7_IWDG_ONETIMESETUP and 
CONFIG_STM32H7_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
+{
+  FAR 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_STM32H7_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(FAR struct stm32_lowerhalf_s *priv);
+
+/* "Lower half" driver methods **********************************************/
+
+static int      stm32_start(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_stop(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_keepalive(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_getstatus(FAR struct watchdog_lowerhalf_s *lower,
+                  FAR struct watchdog_status_s *status);
+static int      stm32_settimeout(FAR 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_STM32H7_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("%08x->%04x\n", addr, val);
+  return val;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_putreg
+ *
+ * Description:
+ *   Set the contents of an STM32 register to a value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_IWDG_REGDEBUG
+static void stm32_putreg(uint16_t val, uint32_t addr)
+{
+  /* Show the register value being written */
+
+  wdinfo("%08x<-%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(FAR 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_STM32H7_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(FAR struct watchdog_lowerhalf_s *lower)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  wdinfo("Entry: started=%d\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 zero.  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_STM32H7_IWDG_ONETIMESETUP) || 
defined(CONFIG_STM32H7_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_systimer();
+      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(FAR 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(FAR struct watchdog_lowerhalf_s *lower)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR 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_systimer();
+  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(FAR struct watchdog_lowerhalf_s *lower,
+                           FAR struct watchdog_status_s *status)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR 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_systimer() - 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    : %08x\n", status->flags);
+  wdinfo("  timeout  : %d\n", status->timeout);
+  wdinfo("  timeleft : %d\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(FAR struct watchdog_lowerhalf_s *lower,
+                            uint32_t timeout)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  uint32_t fiwdg;
+  uint64_t reload;
+  int prescaler;
+  int shift;
+
+  wdinfo("Entry: timeout=%d\n", timeout);
+  DEBUGASSERT(priv);
+
+  /* Can this timeout be represented? */
+
+  if (timeout < 1 || timeout > IWDG_MAXTIMEOUT)
+    {
+      wderr("ERROR: Cannot represent timeout=%d > %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_STM32H7_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_STM32H7_IWDG_ONETIMESETUP
+  /* If CONFIG_STM32H7_IWDG_DEFERREDSETUP is selected, then perform the 
register
+   * configuration only if the timer has been started.
+   */
+
+#ifdef CONFIG_STM32H7_IWDG_DEFERREDSETUP
+  if (priv->started)
+#endif
+    {
+      stm32_setprescaler(priv);
+    }
+#endif
+
+  wdinfo("prescaler=%d fiwdg=%d reload=%d\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(FAR const char *devpath, uint32_t lsifreq)
+{
+  FAR struct stm32_lowerhalf_s *priv = &g_wdgdev;
+
+  wdinfo("Entry: devpath=%s lsifreq=%d\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 CSR: %08x\n", getreg32(STM32_RCC_CSR));
+
+  /* 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((FAR struct watchdog_lowerhalf_s *)priv, 
CONFIG_STM32H7_IWDG_DEFTIMOUT);
+
+  /* Register the watchdog driver as /dev/watchdog0 */
+
+  watchdog_register(devpath, (FAR 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_STM32H7_JTAG_FULL_ENABLE) || \
+    defined(CONFIG_STM32H7_JTAG_NOJNTRST_ENABLE) || \
+    defined(CONFIG_STM32H7_JTAG_SW_ENABLE)
+    {
+      uint32_t cr = getreg32(STM32_DBGMCU_APB4_FZ1);
+      cr |= DBGMCU_APB4_WDGLSD1;
+      putreg32(cr, STM32_DBGMCU_APB4_FZ1);
+    }
+#endif
+}
+
+#endif /* CONFIG_WATCHDOG && CONFIG_STM32H7_IWDG */
diff --git a/arch/arm/src/stm32h7/stm32_wdg.h b/arch/arm/src/stm32h7/stm32_wdg.h
new file mode 100644
index 0000000..06a0011
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_wdg.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_wdg.h
+ *
+ *   Copyright (C) 2012, 2015 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32_STM32_WDG_H
+#define __ARCH_ARM_SRC_STM32_STM32_WDG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32_wdg.h"
+
+#ifdef CONFIG_WATCHDOG
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * 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_STM32H7_IWDG
+void stm32_iwdginitialize(FAR 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_STM32H7_WWDG
+void stm32_wwdginitialize(FAR const char *devpath);
+#endif
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ASSEMBLY__ */
+#endif /* CONFIG_WATCHDOG */
+#endif /* __ARCH_ARM_SRC_STM32_STM32_WDG_H */
diff --git a/arch/arm/src/stm32h7/stm32_wwdg.c 
b/arch/arm/src/stm32h7/stm32_wwdg.c
new file mode 100644
index 0000000..c0d1083
--- /dev/null
+++ b/arch/arm/src/stm32h7/stm32_wwdg.c
@@ -0,0 +1,798 @@
+/****************************************************************************
+ * arch/arm/src/stm32h7/stm32_wwdg.c
+ *
+ *   Copyright (C) 2012, 2016 Gregory Nutt. All rights reserved.
+ *   Author: Gregory Nutt <gn...@nuttx.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name NuttX nor the names of its contributors may be
+ *    used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/arch.h>
+
+#include <stdint.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/timers/watchdog.h>
+#include <arch/board/board.h>
+
+#include "up_arch.h"
+#include "stm32_wdg.h"
+
+#if defined(CONFIG_WATCHDOG) && defined(CONFIG_STM32H7_WWDG)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+/* Clocking *****************************************************************/
+/* The minimum frequency of the WWDG clock is:
+ *
+ *  Fmin = PCLK1 / 4096 / 8
+ *
+ * So the maximum delay (in milliseconds) is then:
+ *
+ *   1000 * (WWDG_CR_T_MAX+1) / Fmin
+ *
+ * For example, if PCLK1 = 42MHz, then the maximum delay is:
+ *
+ *   Fmin = 1281.74
+ *   1000 * 64 / Fmin = 49.93 msec
+ */
+
+#define WWDG_FMIN       (STM32_PCLK1_FREQUENCY / 4096 / 8)
+#define WWDG_MAXTIMEOUT (1000 * (WWDG_CR_T_MAX+1) / WWDG_FMIN)
+
+/* Configuration ************************************************************/
+
+#ifndef CONFIG_STM32H7_WWDG_DEFTIMOUT
+#  define CONFIG_STM32H7_WWDG_DEFTIMOUT WWDG_MAXTIMEOUT
+#endif
+
+#ifndef CONFIG_DEBUG_WATCHDOG_INFO
+#  undef CONFIG_STM32H7_WWDG_REGDEBUG
+#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
+{
+  FAR const struct watchdog_ops_s  *ops;  /* Lower half operations */
+  xcpt_t   handler;  /* Current EWI interrupt handler */
+  uint32_t timeout;  /* The actual timeout value */
+  uint32_t fwwdg;    /* WWDG clock frequency */
+  bool     started;  /* The timer has been started */
+  uint8_t  reload;   /* The 7-bit reload field reset value */
+  uint8_t  window;   /* The 7-bit window (W) field value */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+/* Register operations ******************************************************/
+
+#ifdef CONFIG_STM32H7_WWDG_REGDEBUG
+static uint16_t stm32_getreg(uint32_t addr);
+static void     stm32_putreg(uint16_t val, uint32_t addr);
+#else
+# define        stm32_getreg(addr)     getreg32(addr)
+# define        stm32_putreg(val,addr) putreg32(val,addr)
+#endif
+static void     stm32_setwindow(FAR struct stm32_lowerhalf_s *priv,
+                  uint8_t window);
+
+/* Interrupt hanlding *******************************************************/
+
+static int      stm32_interrupt(int irq, FAR void *context, FAR void *arg);
+
+/* "Lower half" driver methods **********************************************/
+
+static int      stm32_start(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_stop(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_keepalive(FAR struct watchdog_lowerhalf_s *lower);
+static int      stm32_getstatus(FAR struct watchdog_lowerhalf_s *lower,
+                  FAR struct watchdog_status_s *status);
+static int      stm32_settimeout(FAR struct watchdog_lowerhalf_s *lower,
+                  uint32_t timeout);
+static xcpt_t   stm32_capture(FAR struct watchdog_lowerhalf_s *lower,
+                  xcpt_t handler);
+static int      stm32_ioctl(FAR struct watchdog_lowerhalf_s *lower, int cmd,
+                  unsigned long arg);
+
+/****************************************************************************
+ * 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    = stm32_capture,
+  .ioctl      = stm32_ioctl,
+};
+
+/* "Lower half" driver state */
+
+static struct stm32_lowerhalf_s g_wdgdev;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_getreg
+ *
+ * Description:
+ *   Get the contents of an STM32 register
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_WWDG_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 registe 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("%08x->%04x\n", addr, val);
+  return val;
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_putreg
+ *
+ * Description:
+ *   Set the contents of an STM32 register to a value
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32H7_WWDG_REGDEBUG
+static void stm32_putreg(uint16_t val, uint32_t addr)
+{
+  /* Show the register value being written */
+
+  wdinfo("%08x<-%04x\n", addr, val);
+
+  /* Write the value */
+
+  putreg16(val, addr);
+}
+#endif
+
+/****************************************************************************
+ * Name: stm32_setwindow
+ *
+ * Description:
+ *   Set the CFR window value. The window value is compared to the down-
+ *   counter when the counter is updated.  The WWDG counter should be updated
+ *   only when the counter is below this window value (and greater than 64)
+ *   otherwise a reset will be generated
+ *
+ ****************************************************************************/
+
+static void stm32_setwindow(FAR struct stm32_lowerhalf_s *priv, uint8_t window)
+{
+  uint16_t regval;
+
+  /* Set W[6:0] bits according to selected window value */
+
+  regval = stm32_getreg(STM32_WWDG_CFR);
+  regval &= ~WWDG_CFR_W_MASK;
+  regval |= window << WWDG_CFR_W_SHIFT;
+  stm32_putreg(regval, STM32_WWDG_CFR);
+
+  /* Remember the window setting */
+
+  priv->window = window;
+}
+
+/****************************************************************************
+ * Name: stm32_interrupt
+ *
+ * Description:
+ *   WWDG early warning interrupt
+ *
+ * Input Parameters:
+ *   Usual interrupt handler arguments.
+ *
+ * Returned Value:
+ *   Always returns OK.
+ *
+ ****************************************************************************/
+
+static int stm32_interrupt(int irq, FAR void *context, FAR void *arg)
+{
+  FAR struct stm32_lowerhalf_s *priv = &g_wdgdev;
+  uint16_t regval;
+
+  /* Check if the EWI interrupt is really pending */
+
+  regval = stm32_getreg(STM32_WWDG_SR);
+  if ((regval & WWDG_SR_EWIF) != 0)
+    {
+      /* Is there a registered handler? */
+
+      if (priv->handler)
+        {
+          /* Yes... NOTE:  This interrupt service routine (ISR) must reload
+           * the WWDG counter to prevent the reset.  Otherwise, we will reset
+           * upon return.
+           */
+
+          priv->handler(irq, context, arg);
+        }
+
+      /* The EWI interrupt is cleared by writing '0' to the EWIF bit in the
+       * WWDG_SR register.
+       */
+
+      regval &= ~WWDG_SR_EWIF;
+      stm32_putreg(regval, STM32_WWDG_SR);
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * 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(FAR struct watchdog_lowerhalf_s *lower)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+
+  wdinfo("Entry\n");
+  DEBUGASSERT(priv);
+
+  /* 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.
+   */
+
+  stm32_putreg(WWDG_CR_WDGA | WWDG_CR_T_RESET | priv->reload, STM32_WWDG_CR);
+  priv->started = true;
+  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(FAR struct watchdog_lowerhalf_s *lower)
+{
+  /* 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.
+   */
+
+  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".
+ *
+ *   The application program must write in the WWDG_CR register at regular
+ *   intervals during normal operation to prevent an MCU reset. This operation
+ *   must occur only when the counter value is lower than the window register
+ *   value. The value to be stored in the WWDG_CR register must be between
+ *   0xff and 0xC0:
+ *
+ * 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(FAR struct watchdog_lowerhalf_s *lower)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+
+  wdinfo("Entry\n");
+  DEBUGASSERT(priv);
+
+  /* Write to T[6:0] bits to configure the counter value, no need to do
+   * a read-modify-write; writing a 0 to WDGA bit does nothing.
+   */
+
+  stm32_putreg((WWDG_CR_T_RESET | priv->reload), STM32_WWDG_CR);
+  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(FAR struct watchdog_lowerhalf_s *lower,
+                           FAR struct watchdog_status_s *status)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  uint32_t elapsed;
+  uint16_t reload;
+
+  wdinfo("Entry\n");
+  DEBUGASSERT(priv);
+
+  /* Return the status bit */
+
+  status->flags = WDFLAGS_RESET;
+  if (priv->started)
+    {
+      status->flags |= WDFLAGS_ACTIVE;
+    }
+
+  if (priv->handler)
+    {
+      status->flags |= WDFLAGS_CAPTURE;
+    }
+
+  /* Return the actual timeout is milliseconds */
+
+  status->timeout = priv->timeout;
+
+  /* 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;
+  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);
+  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(FAR struct watchdog_lowerhalf_s *lower,
+                            uint32_t timeout)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  uint32_t fwwdg;
+  uint32_t reload;
+  uint16_t regval;
+  int wdgtb;
+
+  DEBUGASSERT(priv);
+  wdinfo("Entry: timeout=%d\n", timeout);
+
+  /* Can this timeout be represented? */
+
+  if (timeout < 1 || timeout > WWDG_MAXTIMEOUT)
+    {
+      wderr("ERROR: Cannot represent timeout=%d > %d\n",
+            timeout, WWDG_MAXTIMEOUT);
+      return -ERANGE;
+    }
+
+  /* Determine prescaler value.
+   *
+   * Fwwdg = PCLK1/4096/prescaler.
+   *
+   * Where
+   *  Fwwwdg is the frequency of the WWDG clock
+   *  wdgtb is one of {1, 2, 4, or 8}
+   */
+
+  /* Select the smallest prescaler that will result in a reload field value 
that is
+   * less than the maximum.
+   */
+
+  for (wdgtb = 0; ; wdgtb++)
+    {
+      /* WDGTB = 0 -> Divider = 1  = 1 << 0
+       * WDGTB = 1 -> Divider = 2  = 1 << 1
+       * WDGTB = 2 -> Divider = 4  = 1 << 2
+       * WDGTB = 3 -> Divider = 8  = 1 << 3
+       */
+
+      /* Get the WWDG counter frequency in Hz. */
+
+      fwwdg = (STM32_PCLK1_FREQUENCY/4096) >> wdgtb;
+
+      /* The formula to calculate the timeout value is given by:
+       *
+       * timeout =  1000 * (reload + 1) / Fwwdg, OR
+       * reload = timeout * Fwwdg / 1000 - 1
+       *
+       * Where
+       *  timeout is the desired timout in milliseconds
+       *  reload is the contents of T{5:0]
+       *  Fwwdg is the frequency of the WWDG clock
+       */
+
+       reload = timeout * fwwdg / 1000 - 1;
+
+      /* 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 0
+      wdinfo("wdgtb=%d fwwdg=%d reload=%d timout=%d\n",
+             wdgtb, fwwdg, reload,  1000 * (reload + 1) / fwwdg);
+#endif
+      if (reload <= WWDG_CR_T_MAX || wdgtb == 3)
+        {
+          /* 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 wdgtb to be incremented.
+           */
+
+          break;
+        }
+    }
+
+  /* Make sure that the final reload value is within range */
+
+  if (reload > WWDG_CR_T_MAX)
+    {
+      reload = WWDG_CR_T_MAX;
+    }
+
+  /* Calculate and save the actual timeout value in milliseconds:
+   *
+   * timeout =  1000 * (reload + 1) / Fwwdg
+   */
+
+  priv->timeout = 1000 * (reload + 1) / fwwdg;
+
+  /* Remember the selected values */
+
+  priv->fwwdg  = fwwdg;
+  priv->reload = reload;
+
+  wdinfo("wdgtb=%d fwwdg=%d reload=%d timout=%d\n",
+         wdgtb, fwwdg, reload, priv->timeout);
+
+  /* Set WDGTB[1:0] bits according to calculated value */
+
+  regval = stm32_getreg(STM32_WWDG_CFR);
+  regval &= ~WWDG_CFR_WDGTB_MASK;
+  regval |= (uint16_t)wdgtb << WWDG_CFR_WDGTB_SHIFT;
+  stm32_putreg(regval, STM32_WWDG_CFR);
+
+  /* Reset the 7-bit window value to the maximum value.. essentially disabling
+   * the lower limit of the watchdog reset time.
+   */
+
+  stm32_setwindow(priv, 0x7f);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_capture
+ *
+ * Description:
+ *   Don't reset on watchdog timer timeout; instead, call this user provider
+ *   timeout handler.  NOTE:  Providing handler==NULL will restore the reset
+ *   behavior.
+ *
+ * Input Parameters:
+ *   lower      - A pointer the publicly visible representation of the 
"lower-half"
+ *                driver state structure.
+ *   newhandler - The new watchdog expiration function pointer.  If this
+ *                function pointer is NULL, then the reset-on-expiration
+ *                behavior is restored,
+ *
+ * Returned Value:
+ *   The previous watchdog expiration function pointer or NULL is there was
+ *   no previous function pointer, i.e., if the previous behavior was
+ *   reset-on-expiration (NULL is also returned if an error occurs).
+ *
+ ****************************************************************************/
+
+static xcpt_t stm32_capture(FAR struct watchdog_lowerhalf_s *lower,
+                            xcpt_t handler)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  irqstate_t flags;
+  xcpt_t oldhandler;
+  uint16_t regval;
+
+  DEBUGASSERT(priv);
+  wdinfo("Entry: handler=%p\n", handler);
+
+  /* Get the old handler return value */
+
+  flags = enter_critical_section();
+  oldhandler = priv->handler;
+
+  /* Save the new handler */
+
+   priv->handler = handler;
+
+  /* Are we attaching or detaching the handler? */
+
+  regval = stm32_getreg(STM32_WWDG_CFR);
+  if (handler)
+    {
+      /* Attaching... Enable the EWI interrupt */
+
+      regval |= WWDG_CFR_EWI;
+      stm32_putreg(regval, STM32_WWDG_CFR);
+
+      up_enable_irq(STM32_IRQ_WWDG1);
+    }
+  else
+    {
+      /* Detaching... Disable the EWI interrupt */
+
+      regval &= ~WWDG_CFR_EWI;
+      stm32_putreg(regval, STM32_WWDG_CFR);
+
+      up_disable_irq(STM32_IRQ_WWDG1);
+    }
+
+  leave_critical_section(flags);
+  return oldhandler;
+}
+
+/****************************************************************************
+ * Name: stm32_ioctl
+ *
+ * Description:
+ *   Any ioctl commands that are not recognized by the "upper-half" driver
+ *   are forwarded to the lower half driver through this method.
+ *
+ * Input Parameters:
+ *   lower - A pointer the publicly visible representation of the "lower-half"
+ *           driver state structure.
+ *   cmd   - The ioctl command value
+ *   arg   - The optional argument that accompanies the 'cmd'.  The
+ *           interpretation of this argument depends on the particular
+ *           command.
+ *
+ * Returned Value:
+ *   Zero on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+static int stm32_ioctl(FAR struct watchdog_lowerhalf_s *lower, int cmd,
+                    unsigned long arg)
+{
+  FAR struct stm32_lowerhalf_s *priv = (FAR struct stm32_lowerhalf_s *)lower;
+  int ret = -ENOTTY;
+
+  DEBUGASSERT(priv);
+  wdinfo("Entry: cmd=%d arg=%ld\n", cmd, arg);
+
+  /* WDIOC_MINTIME: Set the minimum ping time.  If two keepalive ioctls
+   * are received within this time, a reset event will be generated.
+   * Argument: A 32-bit time value in milliseconds.
+   */
+
+  if (cmd == WDIOC_MINTIME)
+    {
+      uint32_t mintime = (uint32_t)arg;
+
+      /* The minimum time should be strictly less than the total delay
+       * which, in turn, will be less than or equal to WWDG_CR_T_MAX
+       */
+
+      ret = -EINVAL;
+      if (mintime < priv->timeout)
+        {
+          uint32_t window = (priv->timeout - mintime) * priv->fwwdg / 1000 - 1;
+          DEBUGASSERT(window < priv->reload);
+          stm32_setwindow(priv, window | WWDG_CR_T_RESET);
+          ret = OK;
+        }
+    }
+
+  return ret;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_wwdginitialize
+ *
+ * Description:
+ *   Initialize the WWDG 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
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+void stm32_wwdginitialize(FAR const char *devpath)
+{
+  FAR struct stm32_lowerhalf_s *priv = &g_wdgdev;
+
+  wdinfo("Entry: devpath=%s\n", devpath);
+
+  /* NOTE we assume that clocking to the WWDG has already been provided by
+   * the RCC initialization logic.
+   */
+
+  /* Initialize the driver state structure.  Here we assume: (1) the state
+   * structure lies in .bss and was zeroed at reset time.  (2) This function
+   * is only called once so it is never necessary to re-zero the structure.
+   */
+
+  priv->ops = &g_wdgops;
+
+  /* Attach our EWI interrupt handler (But don't enable it yet) */
+
+  irq_attach(STM32_IRQ_WWDG1, stm32_interrupt, NULL);
+
+  /* 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((FAR struct watchdog_lowerhalf_s *)priv,
+                   CONFIG_STM32H7_WWDG_DEFTIMOUT);
+
+  /* Register the watchdog driver as /dev/watchdog0 */
+
+  watchdog_register(devpath, (FAR struct watchdog_lowerhalf_s *)priv);
+
+  /* When the microcontroller enters debug mode (Cortex-M core halted),
+   * the WWDG counter either continues to work normally or stops, depending
+   * on the WWDG1 STOP configuration bit in DBG module.
+   */
+
+#if defined(CONFIG_STM32H7_JTAG_FULL_ENABLE) || \
+    defined(CONFIG_STM32H7_JTAG_NOJNTRST_ENABLE) || \
+    defined(CONFIG_STM32H7_JTAG_SW_ENABLE)
+    {
+      uint32_t cr = getreg32(STM32_DBGMCU_APB3_FZ1);
+      cr |= DBGMCU_APB3_WWDG1STOP;
+      putreg32(cr, STM32_DBGMCU_APB3_FZ1);
+    }
+#endif
+}
+
+#endif /* CONFIG_WATCHDOG && CONFIG_STM32H7_WWDG */

Reply via email to