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

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

commit a25fbba5a5c428c520b57df050885bacacfac03d
Author: dechao_gong <[email protected]>
AuthorDate: Tue Jul 21 11:33:59 2026 +0800

    arch/arm/rtl8721dx: add shared Ameba watchdog driver
    
    Add a NuttX watchdog lower-half for the Ameba KM4 non-secure system
    watchdog (WDG2), registered as /dev/watchdog0.  The fwlib WDG API is
    ROM-resident, so no board.mk change is needed.
    
    The hardware cannot be stopped once enabled, so stop() is emulated via
    the early interrupt (EI) auto-refreshing the counter, and capture()
    delivers a pre-timeout callback through the same EI.  The EI has a
    three-part timing contract, all handled here: it must be armed with
    EIMOD=ENABLE at WDG_Init, its EIE gate only takes effect after
    WDG_Enable, and -- because the EI is level-based -- a pure capture path
    must mask EIE after the one-shot callback to avoid re-entrant storming
    while the reset is pending.  The EI flag is cleared twice per the slow
    WDG clock.
    
    Per-chip base address and IRQ live in ameba_wdg_chip.h so the shared
    driver needs no change to port to another Ameba IC.
    
    Verified on pke8721daf: timeout reset (BOOT REASON WDG2), stop()
    suppressing the reset, and capture() firing ~EICNT ms before the reset.
    
    Assisted-by: Claude <[email protected]>
    Signed-off-by: dechao_gong <[email protected]>
---
 .../arm/rtl8721dx/boards/pke8721daf/index.rst      |  13 +
 arch/arm/src/common/ameba/Kconfig                  |  15 +
 arch/arm/src/common/ameba/ameba_wdg.c              | 576 +++++++++++++++++++++
 arch/arm/src/common/ameba/ameba_wdg.h              |  70 +++
 arch/arm/src/rtl8721dx/CMakeLists.txt              |   4 +
 arch/arm/src/rtl8721dx/Make.defs                   |   4 +
 arch/arm/src/rtl8721dx/ameba_wdg_chip.h            |  77 +++
 .../arm/rtl8721dx/pke8721daf/configs/wdg/defconfig |  47 ++
 boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt |   7 +-
 boards/arm/rtl8721dx/pke8721daf/src/Makefile       |   9 +
 .../rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c   |  10 +
 .../pke8721daf/src/rtl8721dx_pke8721daf.h          |  13 +
 .../arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c   |  67 +++
 tools/nxstyle.c                                    |   1 +
 14 files changed, 912 insertions(+), 1 deletion(-)

diff --git a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst 
b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
index c2a71f6c085..0e66109df8a 100644
--- a/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
+++ b/Documentation/platforms/arm/rtl8721dx/boards/pke8721daf/index.rst
@@ -47,6 +47,8 @@ Supported in this NuttX port:
   on the SDK fwlib register layer
 * On-chip RTC exposed as a ``/dev/rtc0`` date/time character device with
   alarm support, driven directly on the SDK fwlib register layer
+* On-chip watchdog exposed as a ``/dev/watchdog0`` character device, driven
+  directly on the SDK fwlib register layer
 
 Buttons and LEDs
 ================
@@ -186,6 +188,17 @@ NSH ``date`` command, and arm a one-shot wakeup with the 
example::
     nsh> date -s "Jun 16 12:00:00 2026"  # set the RTC
     nsh> alarm 10                        # fire an alarm in 10 seconds
 
+wdg
+---
+
+Minimal NSH with the on-chip watchdog driver and the ``wdog`` example
+enabled (no Wi-Fi). The watchdog is registered at ``/dev/watchdog0`` from the
+board bring-up (``boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c``); it
+has no board wiring (it is an internal timer). Exercise it with the example,
+which opens the device, sets a timeout, and pings it::
+
+    nsh> wdog                            # run the watchdog example
+
 Wi-Fi
 =====
 
diff --git a/arch/arm/src/common/ameba/Kconfig 
b/arch/arm/src/common/ameba/Kconfig
index d49164a70d4..ea74d1435d0 100644
--- a/arch/arm/src/common/ameba/Kconfig
+++ b/arch/arm/src/common/ameba/Kconfig
@@ -134,4 +134,19 @@ config AMEBA_RTC
                day-of-year (no month/day register); the driver bridges that to 
the
                NuttX month/day calendar with the libc UTC calendar routines.
 
+config AMEBA_WDG
+       bool "Watchdog"
+       default n
+       select WATCHDOG
+       ---help---
+               Expose the Ameba system watchdog as a NuttX watchdog at
+               /dev/watchdog0 (start/stop/keepalive/settimeout, plus a capture
+               pre-timeout callback).  The timeout is programmed directly in
+               milliseconds.
+
+               The driver (arch/arm/src/common/ameba/ameba_wdg.c) sits on the 
SDK
+               fwlib WDG register layer.  The hardware cannot be halted once
+               enabled, so stop() arms the WDG early interrupt and refreshes 
the
+               counter from its handler to inhibit the reset.
+
 endmenu # Ameba Peripheral Support
diff --git a/arch/arm/src/common/ameba/ameba_wdg.c 
b/arch/arm/src/common/ameba/ameba_wdg.c
new file mode 100644
index 00000000000..07310c7161a
--- /dev/null
+++ b/arch/arm/src/common/ameba/ameba_wdg.c
@@ -0,0 +1,576 @@
+/****************************************************************************
+ * arch/arm/src/common/ameba/ameba_wdg.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
+ ****************************************************************************/
+
+/* NuttX watchdog lower half for the Realtek Ameba system watchdog.  It
+ * exposes the on-chip WDG as a NuttX watchdog at /dev/watchdog0 with the
+ * usual start/stop/keepalive/getstatus/settimeout methods, plus capture
+ * (a pre-timeout "early interrupt" callback).
+ *
+ * The WDG is driven through the SDK fwlib WDG API.  Those routines are
+ * marked _LONG_CALL_ and are resolved from ROM (they appear in the fwlib
+ * ROM symbol table), so -- like the UART/SPI drivers -- nothing extra is
+ * added to the board build.  Each call takes a WDG_TypeDef * register base;
+ * the base and the interrupt vector are the only chip-specific facts and
+ * live in the per-chip ameba_wdg_chip.h.  To keep the vendor headers out of
+ * the NuttX include world the few fwlib symbols, constants and structures
+ * used here are declared locally (layout-compatible mirrors) rather than
+ * pulled in from the SDK <ameba_wdg.h>.
+ *
+ * Hardware note (the "one catch"): the Ameba system WDG can be enabled but
+ * it *cannot be stopped by software* -- the fwlib exposes WDG_Enable() with
+ * no WDG_Cmd(DISABLE) counterpart in ROM, and the vendor HAL asserts on
+ * stop.  To honour the NuttX stop() contract we instead arm the WDG early
+ * interrupt and refresh the counter from its handler, so the timer keeps
+ * running but can never reach the reset threshold -- an effective "stop".
+ * The same early interrupt backs capture(): when a user handler is
+ * registered it is called at the pre-timeout point instead.
+ */
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <debug.h>
+
+#include <nuttx/irq.h>
+#include <nuttx/arch.h>
+#include <nuttx/clock.h>
+#include <nuttx/spinlock.h>
+#include <nuttx/timers/watchdog.h>
+
+#include "ameba_wdg.h"
+#include "ameba_wdg_chip.h"
+
+#ifdef CONFIG_WATCHDOG
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* "state" argument for the fwlib enable/disable style APIs. */
+
+#define AMEBA_DISABLE               0x0
+#define AMEBA_ENABLE                0x1
+
+/* fwlib WDG_CR interrupt bits (from the SDK ameba_wdg.h; identical on every
+ * current Ameba chip): early-interrupt enable and its write-1-to-clear flag.
+ */
+
+#define AMEBA_WDG_BIT_EIE           ((uint32_t)1 << 16)  /* early int enable  
*/
+#define AMEBA_WDG_BIT_EIC           ((uint32_t)1 << 17)  /* early int clear   
*/
+
+/* WDG timeout is a 16-bit millisecond count; the early interrupt fires this
+ * many milliseconds before the timeout.  Match the vendor HAL: 100ms of head
+ * room, or half the timeout when that is under 100ms (at least 1ms).
+ */
+
+#define AMEBA_WDG_TIMEOUT_MAX       65535u
+#define AMEBA_WDG_DEFAULT_MS        5000u
+#define AMEBA_WDG_EICNT(ms)         ((ms) > 100 ? 100 : (((ms) + 1) >> 1))
+
+/* The chip header hands us the register base as a plain address. */
+
+#define AMEBA_WDG  ((struct ameba_wdg_dev_s *)AMEBA_WDG_BASE)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* Layout-compatible mirrors of the fwlib WDG structures (same field order
+ * and types); passed by address to the fwlib WDG API.  The register block is
+ * only ever touched through the fwlib, so it is an opaque base here -- this
+ * driver never dereferences any field below, it only hands the base pointer
+ * to the ROM routines.  That is what keeps it chip-neutral: amebasmart's
+ * WDG_TypeDef has no 0x010 "dummy" register (only 4 words), but since the
+ * field is never read the extra word is harmless padding on that chip.
+ */
+
+/* WDG_TypeDef */
+
+struct ameba_wdg_dev_s
+{
+  volatile uint32_t mkeyr;           /* 0x000 magic key register  */
+  volatile uint32_t cr;              /* 0x004 control register    */
+  volatile uint32_t rlr;             /* 0x008 reload register     */
+  volatile uint32_t winr;            /* 0x00C window register     */
+  volatile uint32_t dummy;           /* 0x010 (absent on amebasmart) */
+};
+
+/* WDG_InitTypeDef */
+
+struct ameba_wdg_init_s
+{
+  uint16_t window;                   /* feed-protect window       */
+  uint16_t timeout;                  /* timeout count, in ms      */
+  uint16_t eicnt;                    /* early-interrupt threshold */
+  uint16_t eimod;                    /* early-interrupt enable    */
+};
+
+/* This is the private watchdog lower-half state.  It must be cast-compatible
+ * with struct watchdog_lowerhalf_s (the ops pointer is first).
+ */
+
+struct ameba_wdg_lowerhalf_s
+{
+  const struct watchdog_ops_s *ops;  /* Lower-half operations vtable */
+
+  spinlock_t lock;                   /* Protects the state below */
+  uint32_t   timeout;                /* The last programmed timeout (ms) */
+  clock_t    lastreset;              /* systime of the last (re)start/feed */
+  bool       configured;             /* WDG_Init has run at least once */
+  bool       started;                /* Counting towards a reset */
+  bool       autofeed;               /* stop(): EI handler refreshes to inhibit
+                                      * the reset (the WDG cannot be halted) */
+  xcpt_t     handler;                /* capture() pre-timeout callback */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+/* SDK fwlib WDG API (resolved from ROM).  Each call takes the WDG register
+ * base; none of them need the vendor headers.
+ */
+
+extern void WDG_StructInit(struct ameba_wdg_init_s *init);
+extern void WDG_Init(struct ameba_wdg_dev_s *wdg,
+                     struct ameba_wdg_init_s *init);
+extern void WDG_Enable(struct ameba_wdg_dev_s *wdg);
+extern void WDG_Timeout(struct ameba_wdg_dev_s *wdg, uint32_t timeout);
+extern void WDG_Refresh(struct ameba_wdg_dev_s *wdg);
+extern void WDG_INTConfig(struct ameba_wdg_dev_s *wdg, uint32_t wdg_it,
+                          uint32_t newstate);
+extern void WDG_ClearINT(struct ameba_wdg_dev_s *wdg, uint32_t intrbit);
+
+static int  ameba_wdg_interrupt(int irq, void *context, void *arg);
+
+/* Watchdog lower-half operations. */
+
+static int  ameba_wdg_start(struct watchdog_lowerhalf_s *lower);
+static int  ameba_wdg_stop(struct watchdog_lowerhalf_s *lower);
+static int  ameba_wdg_keepalive(struct watchdog_lowerhalf_s *lower);
+static int  ameba_wdg_getstatus(struct watchdog_lowerhalf_s *lower,
+                                struct watchdog_status_s *status);
+static int  ameba_wdg_settimeout(struct watchdog_lowerhalf_s *lower,
+                                 uint32_t timeout);
+static xcpt_t ameba_wdg_capture(struct watchdog_lowerhalf_s *lower,
+                                xcpt_t handler);
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct watchdog_ops_s g_wdg_ops =
+{
+  .start      = ameba_wdg_start,
+  .stop       = ameba_wdg_stop,
+  .keepalive  = ameba_wdg_keepalive,
+  .getstatus  = ameba_wdg_getstatus,
+  .settimeout = ameba_wdg_settimeout,
+  .capture    = ameba_wdg_capture,
+  .ioctl      = NULL,
+};
+
+static struct ameba_wdg_lowerhalf_s g_wdg_lowerhalf =
+{
+  .ops  = &g_wdg_ops,
+  .lock = SP_UNLOCKED,
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_wdg_hwinit
+ *
+ * Description:
+ *   Program the fwlib WDG for a timeout (in ms) with the early interrupt
+ *   configured but left masked.  Only valid before the WDG is enabled; once
+ *   running, settimeout() uses WDG_Timeout() instead.
+ *
+ ****************************************************************************/
+
+static void ameba_wdg_hwinit(struct ameba_wdg_lowerhalf_s *priv,
+                             uint32_t timeout)
+{
+  struct ameba_wdg_init_s init;
+
+  WDG_StructInit(&init);
+  init.timeout = (uint16_t)timeout;
+  init.eicnt   = (uint16_t)AMEBA_WDG_EICNT(timeout);
+
+  /* The early-interrupt channel (and its EICNT threshold) is only actually
+   * armed by WDG_Init when EIMOD is enabled -- the vendor HAL always inits
+   * with EIMOD=ENABLE when it wants the EI.  So arm it here, then gate the
+   * delivery off with the EIE bit; stop()/capture() flip EIE at run time
+   * (which is valid after WDG_Enable) without ever re-initialising.
+   */
+
+  init.eimod   = AMEBA_ENABLE;
+  WDG_Init(AMEBA_WDG, &init);
+  WDG_INTConfig(AMEBA_WDG, AMEBA_WDG_BIT_EIE, AMEBA_DISABLE);
+
+  priv->timeout    = timeout;
+  priv->configured = true;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_ei
+ *
+ * Description:
+ *   Enable or disable delivery of the early interrupt via the CR EIE bit.
+ *   The IRQ itself is attached once in ameba_wdg_initialize(); the NVIC line
+ *   stays enabled and this bit gates whether the WDG actually raises it.
+ *   Called with the driver lock held.
+ *
+ ****************************************************************************/
+
+static void ameba_wdg_ei(bool enable)
+{
+  /* Clear a possibly-pending flag before (un)masking.  The WDG runs off a
+   * slow clock, so the vendor examples clear the EI flag twice.
+   */
+
+  WDG_ClearINT(AMEBA_WDG, AMEBA_WDG_BIT_EIC);
+  WDG_ClearINT(AMEBA_WDG, AMEBA_WDG_BIT_EIC);
+  WDG_INTConfig(AMEBA_WDG, AMEBA_WDG_BIT_EIE,
+                enable ? AMEBA_ENABLE : AMEBA_DISABLE);
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_interrupt
+ *
+ * Description:
+ *   WDG early-interrupt handler, fired AMEBA_WDG_EICNT() ms before the reset
+ *   threshold.  A registered capture handler gets first refusal; otherwise,
+ *   when a stop() has requested it, the counter is refreshed to inhibit the
+ *   reset (the hardware cannot be halted any other way).
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_interrupt(int irq, void *context, void *arg)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)arg;
+
+  if (priv->handler != NULL)
+    {
+      priv->handler(irq, context, arg);
+    }
+
+  if (priv->autofeed)
+    {
+      /* stop() asked us to keep the counter fed so the reset never lands. */
+
+      WDG_Refresh(AMEBA_WDG);
+    }
+  else if (priv->handler != NULL)
+    {
+      /* Pure capture: the handler has had its pre-timeout notification and
+       * we deliberately do NOT feed, so the reset must follow.  The EI is
+       * level-based -- with the counter left past the EI threshold it would
+       * re-assert the moment EIC is cleared and storm the CPU, starving
+       * everything else until the reset.  Mask EIE so this fires exactly
+       * once and the WDG runs cleanly on to the timeout reset.
+       */
+
+      WDG_INTConfig(AMEBA_WDG, AMEBA_WDG_BIT_EIE, AMEBA_DISABLE);
+    }
+
+  /* Clear the EI flag twice -- the WDG's slow clock can miss a single
+   * write-1-to-clear, which would re-enter this handler immediately.
+   */
+
+  WDG_ClearINT(AMEBA_WDG, AMEBA_WDG_BIT_EIC);
+  WDG_ClearINT(AMEBA_WDG, AMEBA_WDG_BIT_EIC);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_start
+ *
+ * Description:
+ *   Start (enable) the watchdog timer.
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_start(struct watchdog_lowerhalf_s *lower)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  /* If start() precedes any settimeout(), come up on a sane default. */
+
+  if (!priv->configured)
+    {
+      ameba_wdg_hwinit(priv, AMEBA_WDG_DEFAULT_MS);
+    }
+
+  /* A restart clears any prior stop() auto-feed. */
+
+  priv->autofeed = false;
+
+  WDG_Enable(AMEBA_WDG);
+
+  /* hwinit() leaves the EI gated off, so re-open it here iff a capture
+   * handler is registered (capture() may have run before this start()).
+   * The EIE bit only takes effect after WDG_Enable(), so this must run
+   * *after* the enable above -- gating it earlier is silently ignored.
+   */
+
+  ameba_wdg_ei(priv->handler != NULL);
+
+  priv->started   = true;
+  priv->lastreset = clock_systime_ticks();
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_stop
+ *
+ * Description:
+ *   Stop the watchdog timer.  The Ameba WDG cannot actually be halted once
+ *   enabled, so this arms the early interrupt to refresh the counter
+ *   forever, which prevents the reset from ever being reached.
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_stop(struct watchdog_lowerhalf_s *lower)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  if (priv->started)
+    {
+      /* Hardware cannot be disabled; inhibit the reset via the early
+       * interrupt (unless a capture handler is already driving it).
+       */
+
+      priv->autofeed = true;
+      if (priv->handler == NULL)
+        {
+          ameba_wdg_ei(true);
+        }
+
+      WDG_Refresh(AMEBA_WDG);
+      priv->started = false;
+    }
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_keepalive
+ *
+ * Description:
+ *   Reset ("feed") the watchdog timer.
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_keepalive(struct watchdog_lowerhalf_s *lower)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  flags = spin_lock_irqsave(&priv->lock);
+  WDG_Refresh(AMEBA_WDG);
+  priv->lastreset = clock_systime_ticks();
+  spin_unlock_irqrestore(&priv->lock, flags);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_getstatus
+ *
+ * Description:
+ *   Return the current watchdog timer status.
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_getstatus(struct watchdog_lowerhalf_s *lower,
+                               struct watchdog_status_s *status)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+  uint32_t elapsed;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  status->flags = 0;
+  if (priv->started)
+    {
+      status->flags |= WDFLAGS_ACTIVE;
+    }
+
+  if (priv->handler != NULL)
+    {
+      status->flags |= WDFLAGS_CAPTURE;
+    }
+  else if (!priv->autofeed)
+    {
+      status->flags |= WDFLAGS_RESET;
+    }
+
+  status->timeout = priv->timeout;
+
+  /* Time left = timeout - time since the last feed/start (clamped at 0). */
+
+  elapsed = TICK2MSEC(clock_systime_ticks() - priv->lastreset);
+  status->timeleft = (elapsed >= priv->timeout) ? 0 :
+                     (priv->timeout - elapsed);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_settimeout
+ *
+ * Description:
+ *   Set a new timeout value (in milliseconds) and reset the watchdog.
+ *
+ ****************************************************************************/
+
+static int ameba_wdg_settimeout(struct watchdog_lowerhalf_s *lower,
+                                uint32_t timeout)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+
+  if (timeout == 0 || timeout > AMEBA_WDG_TIMEOUT_MAX)
+    {
+      return -EINVAL;
+    }
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  if (priv->started)
+    {
+      /* Already counting: RLR reconfiguration is not allowed, so use the
+       * fwlib run-time timeout update and re-feed.
+       */
+
+      WDG_Timeout(AMEBA_WDG, timeout);
+      WDG_Refresh(AMEBA_WDG);
+      priv->timeout = timeout;
+    }
+  else
+    {
+      ameba_wdg_hwinit(priv, timeout);
+    }
+
+  priv->lastreset = clock_systime_ticks();
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return OK;
+}
+
+/****************************************************************************
+ * Name: ameba_wdg_capture
+ *
+ * Description:
+ *   Register a pre-timeout ("early interrupt") handler in place of the reset
+ *   behaviour, or restore the reset behaviour when handler is NULL.  Returns
+ *   the previous handler.
+ *
+ ****************************************************************************/
+
+static xcpt_t ameba_wdg_capture(struct watchdog_lowerhalf_s *lower,
+                                xcpt_t handler)
+{
+  struct ameba_wdg_lowerhalf_s *priv =
+    (struct ameba_wdg_lowerhalf_s *)lower;
+  irqstate_t flags;
+  xcpt_t oldhandler;
+
+  flags = spin_lock_irqsave(&priv->lock);
+
+  oldhandler    = priv->handler;
+  priv->handler = handler;
+
+  /* Keep the early interrupt on while either a capture handler or a stop()
+   * auto-feed needs it; otherwise mask it and let the WDG reset on timeout.
+   */
+
+  ameba_wdg_ei(handler != NULL || priv->autofeed);
+
+  spin_unlock_irqrestore(&priv->lock, flags);
+  return oldhandler;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: ameba_wdg_initialize
+ ****************************************************************************/
+
+int ameba_wdg_initialize(void)
+{
+  struct ameba_wdg_lowerhalf_s *priv = &g_wdg_lowerhalf;
+
+  /* Attach the early interrupt once.  The NVIC line stays enabled for the
+   * lifetime of the driver; the CR EIE bit (flipped by stop()/capture()) is
+   * what actually gates whether the WDG raises it, and it comes up masked.
+   */
+
+  irq_attach(AMEBA_WDG_IRQ, ameba_wdg_interrupt, priv);
+  up_enable_irq(AMEBA_WDG_IRQ);
+
+  /* Register the watchdog in the stopped state; nothing counts until the
+   * application programs a timeout and starts it.
+   */
+
+  if (watchdog_register(CONFIG_WATCHDOG_DEVPATH,
+                        (struct watchdog_lowerhalf_s *)priv) == NULL)
+    {
+      up_disable_irq(AMEBA_WDG_IRQ);
+      irq_detach(AMEBA_WDG_IRQ);
+      return -ENODEV;
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_WATCHDOG */
diff --git a/arch/arm/src/common/ameba/ameba_wdg.h 
b/arch/arm/src/common/ameba/ameba_wdg.h
new file mode 100644
index 00000000000..4224acaa450
--- /dev/null
+++ b/arch/arm/src/common/ameba/ameba_wdg.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+ * arch/arm/src/common/ameba/ameba_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_COMMON_AMEBA_AMEBA_WDG_H
+#define __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_WDG_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: ameba_wdg_initialize
+ *
+ * Description:
+ *   Instantiate the Ameba watchdog lower half and bind it to the NuttX
+ *   watchdog character driver at CONFIG_WATCHDOG_DEVPATH ("/dev/watchdog0"
+ *   by default).  General usage:
+ *
+ *     #include "ameba_wdg.h"
+ *     ameba_wdg_initialize();
+ *
+ *   The watchdog is registered in the stopped state (nothing counts until
+ *   the application issues WDIOC_SETTIMEOUT + WDIOC_START).
+ *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure.
+ *
+ ****************************************************************************/
+
+int ameba_wdg_initialize(void);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ARCH_ARM_SRC_COMMON_AMEBA_AMEBA_WDG_H */
diff --git a/arch/arm/src/rtl8721dx/CMakeLists.txt 
b/arch/arm/src/rtl8721dx/CMakeLists.txt
index 0fadd6e5190..5e2dda71e14 100644
--- a/arch/arm/src/rtl8721dx/CMakeLists.txt
+++ b/arch/arm/src/rtl8721dx/CMakeLists.txt
@@ -68,6 +68,10 @@ if(CONFIG_AMEBA_RTC)
   list(APPEND SRCS ${AMEBA_COMMON}/ameba_rtc.c)
 endif()
 
+if(CONFIG_AMEBA_WDG)
+  list(APPEND SRCS ${AMEBA_COMMON}/ameba_wdg.c)
+endif()
+
 target_include_directories(arch PRIVATE ${AMEBA_COMMON})
 target_sources(arch PRIVATE ${SRCS})
 
diff --git a/arch/arm/src/rtl8721dx/Make.defs b/arch/arm/src/rtl8721dx/Make.defs
index 113622164d8..0503fce09fb 100644
--- a/arch/arm/src/rtl8721dx/Make.defs
+++ b/arch/arm/src/rtl8721dx/Make.defs
@@ -78,6 +78,10 @@ ifeq ($(CONFIG_AMEBA_RTC),y)
 CHIP_CSRCS += ameba_rtc.c
 endif
 
+ifeq ($(CONFIG_AMEBA_WDG),y)
+CHIP_CSRCS += ameba_wdg.c
+endif
+
 ############################################################################
 # Realtek RTL8721Dx SDK integration
 #
diff --git a/arch/arm/src/rtl8721dx/ameba_wdg_chip.h 
b/arch/arm/src/rtl8721dx/ameba_wdg_chip.h
new file mode 100644
index 00000000000..d2925ea8934
--- /dev/null
+++ b/arch/arm/src/rtl8721dx/ameba_wdg_chip.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+ * arch/arm/src/rtl8721dx/ameba_wdg_chip.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_RTL8721DX_AMEBA_WDG_CHIP_H
+#define __ARCH_ARM_SRC_RTL8721DX_AMEBA_WDG_CHIP_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+#include <nuttx/irq.h>
+
+#include <stdint.h>
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Per-chip watchdog wiring for RTL8721DX (amebadplus).  The shared driver
+ * (arch/arm/src/common/ameba/ameba_wdg.c) includes this header to learn
+ * which watchdog instance to drive and its interrupt line.  A port to
+ * another Ameba chip supplies a same-named header on the chip include path;
+ * the shared driver is never edited -- it reads only the two macros below.
+ *
+ * The Ameba SoCs carry several watchdog instances (an always-on IWDG plus
+ * one "system" WDG per CPU, each with a secure and a non-secure alias).
+ * This NuttX port runs on the KM4 core, and -- matching the vendor HAL
+ * wdt_api.c, which selects KM4_NS_WDG_DEV / KM4_NS_WDG_IRQ for
+ * CONFIG_ARM_CORE_CM4 -- we drive the KM4 non-secure system watchdog.  As
+ * with the I2C/SPI drivers the non-secure register alias (0x41xxxxxx) is the
+ * one actually reachable from this core, so that is the base used here.
+ *
+ * BOTH the base address and the interrupt vector are chip-specific.  The WDG
+ * register block, structures, magic keys and the ms-based timeout are the
+ * same on every current Ameba chip, so they live in the shared driver; only
+ * the two values below move per chip (verified against each SoC's
+ * hal_platform.h and ameba_vector_table.h):
+ *
+ *   chip         KM4/CPU non-secure system WDG base   NuttX IRQ
+ *   -----------  -----------------------------------  ---------------------
+ *   amebadplus   WDG2_REG_BASE            0x41008D80  KM4_NS_WDG_IRQ    = 65
+ *   amebalite    WDG2_REG_BASE            0x4101F040  (see its vector table)
+ *   amebasmart   WDG2_REG_BASE            0x41000440  (see its vector table)
+ *   amebagreen2  WDG2_REG_BASE            0x4080AD80  CPU0_NS_WDG_IRQ   = 69
+ *   RTL8720F     WDG2_REG_BASE            0x40801D80  KM4NS_WDG_IRQ     = 49
+ *
+ * A new chip only edits this header: AMEBA_WDG_BASE is the register base of
+ * its non-secure system WDG (cast to the fwlib WDG_TypeDef * by the shared
+ * driver) and AMEBA_WDG_IRQ carries the NuttX IRQ number of that WDG's
+ * early-interrupt line -- see the ADC/RTC chip headers for the same
+ * data-driven, never-computed pattern.
+ */
+
+#define AMEBA_WDG_BASE            0x41008d80ul
+#define AMEBA_WDG_IRQ             RTL8721DX_IRQ_KM4_NS_WDG
+
+#endif /* __ARCH_ARM_SRC_RTL8721DX_AMEBA_WDG_CHIP_H */
diff --git a/boards/arm/rtl8721dx/pke8721daf/configs/wdg/defconfig 
b/boards/arm/rtl8721dx/pke8721daf/configs/wdg/defconfig
new file mode 100644
index 00000000000..43c9f7405ab
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/configs/wdg/defconfig
@@ -0,0 +1,47 @@
+#
+# This file is autogenerated: PLEASE DO NOT EDIT IT.
+#
+# You can use "make menuconfig" to make any modifications to the installed 
.config file.
+# You can then do "make savedefconfig" to generate a new defconfig file that 
includes your
+# modifications.
+#
+CONFIG_AMEBA_WDG=y
+CONFIG_ARCH="arm"
+CONFIG_ARCH_BOARD="pke8721daf"
+CONFIG_ARCH_BOARD_PKE8721DAF=y
+CONFIG_ARCH_CHIP="rtl8721dx"
+CONFIG_ARCH_CHIP_RTL8721DX=y
+CONFIG_ARCH_INTERRUPTSTACK=2048
+CONFIG_ARCH_STACKDUMP=y
+CONFIG_ARMV8M_SYSTICK=y
+CONFIG_BUILTIN=y
+CONFIG_DEBUG_ASSERTIONS=y
+CONFIG_DEBUG_FEATURES=y
+CONFIG_DEBUG_FULLOPT=y
+CONFIG_DEBUG_SYMBOLS=y
+CONFIG_DEFAULT_TASK_STACKSIZE=4096
+CONFIG_EXAMPLES_HELLO=y
+CONFIG_EXAMPLES_WATCHDOG=y
+CONFIG_FS_PROCFS=y
+CONFIG_FS_TMPFS=y
+CONFIG_IDLETHREAD_STACKSIZE=4096
+CONFIG_INIT_ENTRYPOINT="nsh_main"
+CONFIG_LIBC_MEMFD_ERROR=y
+CONFIG_MM_DEFAULT_ALIGNMENT=32
+CONFIG_NSH_BUILTIN_APPS=y
+CONFIG_NSH_FILEIOSIZE=512
+CONFIG_NSH_READLINE=y
+CONFIG_PREALLOC_TIMERS=4
+CONFIG_RAM_SIZE=294912
+CONFIG_RAM_START=0x20020000
+CONFIG_RR_INTERVAL=200
+CONFIG_RTL8721DX_FLASH_FS=y
+CONFIG_SCHED_HPWORK=y
+CONFIG_SCHED_HPWORKPRIORITY=192
+CONFIG_SCHED_LPWORK=y
+CONFIG_STACK_COLORATION=y
+CONFIG_SYSTEM_NSH=y
+CONFIG_SYSTEM_NSH_STACKSIZE=2500
+CONFIG_TIMER=y
+CONFIG_TIMER_ARCH=y
+CONFIG_USEC_PER_TICK=1000
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt 
b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
index e47cd695f39..b0e12365ffe 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
+++ b/boards/arm/rtl8721dx/pke8721daf/src/CMakeLists.txt
@@ -50,6 +50,10 @@ if(CONFIG_AMEBA_RTC)
   list(APPEND SRCS rtl8721dx_rtc.c)
 endif()
 
+if(CONFIG_AMEBA_WDG)
+  list(APPEND SRCS rtl8721dx_wdg.c)
+endif()
+
 target_sources(board PRIVATE ${SRCS})
 
 if(CONFIG_AMEBA_GPIO
@@ -58,7 +62,8 @@ if(CONFIG_AMEBA_GPIO
    OR CONFIG_AMEBA_SPI
    OR CONFIG_AMEBA_PWM
    OR CONFIG_AMEBA_ADC
-   OR CONFIG_AMEBA_RTC)
+   OR CONFIG_AMEBA_RTC
+   OR CONFIG_AMEBA_WDG)
   # The board pin/UART tables pull in the shared driver's public headers from
   # arch/arm/src/common/ameba/, not on the default board include path.
   target_include_directories(board
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/Makefile 
b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
index c857ec97da6..2fd83e815f3 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/Makefile
+++ b/boards/arm/rtl8721dx/pke8721daf/src/Makefile
@@ -87,4 +87,13 @@ CSRCS += rtl8721dx_rtc.c
 CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
 endif
 
+ifeq ($(CONFIG_AMEBA_WDG),y)
+CSRCS += rtl8721dx_wdg.c
+
+# The board WDG bring-up pulls in the shared driver's public header from
+# arch/arm/src/common/ameba/, which is not on the default board include path.
+
+CFLAGS += 
${INCDIR_PREFIX}$(TOPDIR)$(DELIM)arch$(DELIM)arm$(DELIM)src$(DELIM)common$(DELIM)ameba
+endif
+
 include $(TOPDIR)/boards/Board.mk
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
index 85d69f261ed..d32f19c2062 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_bringup.c
@@ -178,6 +178,16 @@ int rtl8721dx_bringup(void)
     }
 #endif
 
+#ifdef CONFIG_AMEBA_WDG
+  /* Register the board's watchdog at /dev/watchdog0. */
+
+  ret = rtl8721dx_wdg_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR, "ERROR: rtl8721dx_wdg_initialize failed: %d\n", ret);
+    }
+#endif
+
   /* Install the inter-core HW IPC-semaphore RTOS hooks LAST -- after all the
    * flash / WHC bring-up above, and just before this (board_late_initialize)
    * path returns and nx_start() hands off to the init task.
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
index 174499a2d26..94a3934b73d 100644
--- a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_pke8721daf.h
@@ -160,6 +160,19 @@ int rtl8721dx_adc_initialize(void);
 int rtl8721dx_rtc_initialize(void);
 #endif
 
+#ifdef CONFIG_AMEBA_WDG
+/****************************************************************************
+ * Name: rtl8721dx_wdg_initialize
+ *
+ * Description:
+ *   Register the board's watchdog at /dev/watchdog0
+ *   (boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c).
+ *
+ ****************************************************************************/
+
+int rtl8721dx_wdg_initialize(void);
+#endif
+
 #ifdef CONFIG_RTL8721DX_FLASH_FS
 /****************************************************************************
  * Name: ameba_flash_fs_initialize
diff --git a/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c 
b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c
new file mode 100644
index 00000000000..75656503b43
--- /dev/null
+++ b/boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c
@@ -0,0 +1,67 @@
+/****************************************************************************
+ * boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.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 <syslog.h>
+#include <errno.h>
+
+#include "ameba_wdg.h"
+#include "rtl8721dx_pke8721daf.h"
+
+#ifdef CONFIG_AMEBA_WDG
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: rtl8721dx_wdg_initialize
+ *
+ * Description:
+ *   Register the on-chip watchdog at /dev/watchdog0.  The WDG has no board
+ *   wiring (it is an internal timer), so this simply defers to the shared
+ *   driver.
+ *
+ ****************************************************************************/
+
+int rtl8721dx_wdg_initialize(void)
+{
+  int ret;
+
+  ret = ameba_wdg_initialize();
+  if (ret < 0)
+    {
+      syslog(LOG_ERR,
+             "ERROR: ameba_wdg_initialize(/dev/watchdog0) failed: %d\n",
+             ret);
+      return ret;
+    }
+
+  return OK;
+}
+
+#endif /* CONFIG_AMEBA_WDG */
diff --git a/tools/nxstyle.c b/tools/nxstyle.c
index a4671e79add..498e1e1b681 100644
--- a/tools/nxstyle.c
+++ b/tools/nxstyle.c
@@ -299,6 +299,7 @@ static const char *g_white_prefix[] =
   "SYSTIMER_",
   "SYS_PLL_",         /* SYS_PLL_ClkGet — RTL8720F SPI ip_clk query */
   "UART_",
+  "WDG_",             /* WDG_Init, WDG_Refresh, WDG_INTConfig, etc. */
   "SystemCoreClock",  /* SystemCoreClock, SystemCoreClockUpdate */
   "cmse_",            /* ARM CMSE TrustZone intrinsics (arm_cmse.h) */
   "MQTTErrors",       /* apps/tools/netutils/mqttc/MQTT-C/include/mqtt.h */

Reply via email to