From a25fbba5a5c428c520b57df050885bacacfac03d Mon Sep 17 00:00:00 2001 From: dechao_gong Date: Tue, 21 Jul 2026 11:33:59 +0800 Subject: [PATCH] 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 Signed-off-by: dechao_gong --- .../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 +++ .../pke8721daf/configs/wdg/defconfig | 47 ++ .../rtl8721dx/pke8721daf/src/CMakeLists.txt | 7 +- boards/arm/rtl8721dx/pke8721daf/src/Makefile | 9 + .../pke8721daf/src/rtl8721dx_bringup.c | 10 + .../pke8721daf/src/rtl8721dx_pke8721daf.h | 13 + .../rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c | 67 ++ tools/nxstyle.c | 1 + 14 files changed, 912 insertions(+), 1 deletion(-) create mode 100644 arch/arm/src/common/ameba/ameba_wdg.c create mode 100644 arch/arm/src/common/ameba/ameba_wdg.h create mode 100644 arch/arm/src/rtl8721dx/ameba_wdg_chip.h create mode 100644 boards/arm/rtl8721dx/pke8721daf/configs/wdg/defconfig create mode 100644 boards/arm/rtl8721dx/pke8721daf/src/rtl8721dx_wdg.c 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 . + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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 + +/**************************************************************************** + * 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 +#include + +#include + +/**************************************************************************** + * 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 + +#include +#include + +#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 */