This is an automated email from the ASF dual-hosted git repository. acassis pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx.git
commit 56bf3e0503dbc4e0dd2635d6f1e66777be41a3c3 Author: Daniel P. Carvalho <[email protected]> AuthorDate: Sat Oct 10 18:34:49 2026 -0300 arch/mips/pic32mz: add a watchdog timer driver for the PIC32MZ-W1. Add a watchdog_ops_s lower half for the WDT, registered with pic32mz_wdt_initialize(). The time-out is selected by the device configuration (WDTPS), so it cannot be changed at run time: settimeout() accepts a time-out up to the hardware one and kicks the watchdog. When WDTEN is set in the device configuration the watchdog runs from reset and stop() fails; otherwise start() enables it by software. The reset cause flag RCON.WDTO is logged and cleared at initialization. The WDTPS field of the W1 device configuration now always follows BOARD_WD_PRESCALER, as it was only programmed when PIC32MZ_WDTENABLE was set, which left a software-enabled watchdog at the shortest time-out. The run-mode watchdog base period is not documented in the datasheet, so it is a Kconfig value (PIC32MZ_WDT_BASE_US, 1 ms by default). Assisted-by: Claude Code:claude-sonnet-5-5 Signed-off-by: Daniel P. Carvalho <[email protected]> --- arch/mips/src/pic32mz/Kconfig | 21 ++ arch/mips/src/pic32mz/Make.defs | 4 + arch/mips/src/pic32mz/hardware/pic32mz_wdt.h | 75 +++++ .../mips/src/pic32mz/hardware/pic32mzw1_features.h | 1 + arch/mips/src/pic32mz/hardware/pic32mzw1_pmuclk.h | 1 + arch/mips/src/pic32mz/pic32mz_config.h | 16 +- arch/mips/src/pic32mz/pic32mz_wdt.c | 307 +++++++++++++++++++++ arch/mips/src/pic32mz/pic32mz_wdt.h | 71 +++++ 8 files changed, 494 insertions(+), 2 deletions(-) diff --git a/arch/mips/src/pic32mz/Kconfig b/arch/mips/src/pic32mz/Kconfig index bde9d829c7f..ad359313e73 100644 --- a/arch/mips/src/pic32mz/Kconfig +++ b/arch/mips/src/pic32mz/Kconfig @@ -226,6 +226,27 @@ menu "PIC32MZ Peripheral Support" config PIC32MZ_WDT bool "Watchdog timer (WDT)" default n + depends on ARCH_CHIP_WFI32E01 + select WATCHDOG + ---help--- + Watchdog timer driver (/dev/watchdog0). The time-out is fixed + by the device configuration (BOARD_WD_PRESCALER in board.h). If + PIC32MZ_WDTENABLE is set the watchdog is running from reset and + cannot be stopped, otherwise the application starts it. + +if PIC32MZ_WDT + +config PIC32MZ_WDT_BASE_US + int "Watchdog base period (microseconds)" + default 1000 + range 1 1000000 + ---help--- + Duration of the 1:1 postscaler step of the watchdog. The time-out + is this value times BOARD_WD_PRESCALER. The datasheet does not + state it for the run-mode watchdog (LPRC based, so only accurate + to about -18%/+13%). + +endif # PIC32MZ_WDT config PIC32MZ_T2 bool "Timer 2 (T2)" diff --git a/arch/mips/src/pic32mz/Make.defs b/arch/mips/src/pic32mz/Make.defs index b0defb27e78..eb198a483c7 100644 --- a/arch/mips/src/pic32mz/Make.defs +++ b/arch/mips/src/pic32mz/Make.defs @@ -167,6 +167,10 @@ CHIP_CSRCS += pic32mz_timer_lowerhalf.c endif endif +ifeq ($(CONFIG_PIC32MZ_WDT),y) +CHIP_CSRCS += pic32mz_wdt.c +endif + ifeq ($(CONFIG_PIC32MZ_FREERUN),y) CHIP_CSRCS += pic32mz_freerun.c endif diff --git a/arch/mips/src/pic32mz/hardware/pic32mz_wdt.h b/arch/mips/src/pic32mz/hardware/pic32mz_wdt.h new file mode 100644 index 00000000000..4a0a2ebaead --- /dev/null +++ b/arch/mips/src/pic32mz/hardware/pic32mz_wdt.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * arch/mips/src/pic32mz/hardware/pic32mz_wdt.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_MIPS_SRC_PIC32MZ_HARDWARE_PIC32MZ_WDT_H +#define __ARCH_MIPS_SRC_PIC32MZ_HARDWARE_PIC32MZ_WDT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include "pic32mz_memorymap.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Register Offsets *********************************************************/ + +#define PIC32MZ_WDT_CON_OFFSET 0x0000 /* Watchdog timer control */ +#define PIC32MZ_WDT_CONCLR_OFFSET 0x0004 /* Watchdog timer control clear */ +#define PIC32MZ_WDT_CONSET_OFFSET 0x0008 /* Watchdog timer control set */ +#define PIC32MZ_WDT_CONINV_OFFSET 0x000c /* Watchdog timer control invert */ + +/* Register Addresses *******************************************************/ + +#define PIC32MZ_WDT_CON (PIC32MZ_WDT_K1BASE + PIC32MZ_WDT_CON_OFFSET) +#define PIC32MZ_WDT_CONCLR (PIC32MZ_WDT_K1BASE + PIC32MZ_WDT_CONCLR_OFFSET) +#define PIC32MZ_WDT_CONSET (PIC32MZ_WDT_K1BASE + PIC32MZ_WDT_CONSET_OFFSET) +#define PIC32MZ_WDT_CONINV (PIC32MZ_WDT_K1BASE + PIC32MZ_WDT_CONINV_OFFSET) + +/* The clear key is the upper half-word of WDTCON. It must be written with + * a single 16-bit store to WDTCON + 2. + */ + +#define PIC32MZ_WDT_CLRKEY (PIC32MZ_WDT_K1BASE + 2) + +/* Watchdog Timer Control Register Bit Definitions **************************/ + +#define WDT_CON_WDTWINEN (1 << 0) /* Bit 0: Windowed mode enable */ +#define WDT_CON_RUNDIV_SHIFT (8) /* Bits 8-12: Run-mode postscaler */ +#define WDT_CON_RUNDIV_MASK (0x1f << WDT_CON_RUNDIV_SHIFT) +#define WDT_CON_ON (1 << 15) /* Bit 15: Watchdog enable */ +#define WDT_CON_CLRKEY_SHIFT (16) /* Bits 16-31: Clear key */ +#define WDT_CON_CLRKEY_MASK (0xffff << WDT_CON_CLRKEY_SHIFT) + +#define WDT_CLRKEY_VALUE 0x5743 + +/* RUNDIV (loaded from CFGCON2.WDTPS at reset) selects a postscaler of + * 2^RUNDIV; the valid values are 0..20 (1:1 to 1:1048576). + */ + +#define WDT_RUNDIV_MAX 20 + +#endif /* __ARCH_MIPS_SRC_PIC32MZ_HARDWARE_PIC32MZ_WDT_H */ diff --git a/arch/mips/src/pic32mz/hardware/pic32mzw1_features.h b/arch/mips/src/pic32mz/hardware/pic32mzw1_features.h index 1e249f12619..66b832a829a 100644 --- a/arch/mips/src/pic32mz/hardware/pic32mzw1_features.h +++ b/arch/mips/src/pic32mz/hardware/pic32mzw1_features.h @@ -159,6 +159,7 @@ #define DEVCFG2_CKSWEN (1 << 14) /* Software clock switching */ #define DEVCFG2_FSCMEN (1 << 15) /* Fail-safe clock monitor */ #define DEVCFG2_WDTPS_SHIFT (16) /* Bits 16-20: WDT postscaler */ +#define DEVCFG2_WDTPS_MASK (0x1f) /* WDTPS field width */ #define DEVCFG2_WDTSPGM (1 << 21) /* WDT stop during flash prog. */ #define DEVCFG2_WINDIS (1 << 22) /* WDT window disable */ #define DEVCFG2_WDTEN (1 << 23) /* WDT enable */ diff --git a/arch/mips/src/pic32mz/hardware/pic32mzw1_pmuclk.h b/arch/mips/src/pic32mz/hardware/pic32mzw1_pmuclk.h index 51ed0582e50..f44b59ae075 100644 --- a/arch/mips/src/pic32mz/hardware/pic32mzw1_pmuclk.h +++ b/arch/mips/src/pic32mz/hardware/pic32mzw1_pmuclk.h @@ -136,6 +136,7 @@ #define RCON_POR (1 << 0) /* Bit 0: power-on reset */ #define RCON_BOR (1 << 1) /* Bit 1: brown-out reset */ +#define RCON_WDTO (1 << 4) /* Bit 4: watchdog time-out reset */ /* Additional PLLs and clock status (OSC block) *****************************/ diff --git a/arch/mips/src/pic32mz/pic32mz_config.h b/arch/mips/src/pic32mz/pic32mz_config.h index ce9174d4bb8..9911749bc1a 100644 --- a/arch/mips/src/pic32mz/pic32mz_config.h +++ b/arch/mips/src/pic32mz/pic32mz_config.h @@ -623,9 +623,20 @@ /* DEVCFG2. CKSWEN is required: pic32mz_wfi32_pwrclk.c switches SYSCLK in * software. DMT stays off; POSC in HS mode (40 MHz crystal). + * + * The WDT postscaler always follows BOARD_WD_PRESCALER: with WDTEN clear the + * watchdog is started by software (PIC32MZ_WDT) but its time-out still comes + * from this field. */ -#ifdef CONFIG_PIC32MZ_WDTENABLE +#ifndef BOARD_WD_PRESCALER +# ifdef CONFIG_PIC32MZ_WDT +# error "BOARD_WD_PRESCALER must be defined to use the watchdog" +# else +# define BOARD_WD_PRESCALER 1048576 +# endif +#endif + #if BOARD_WD_PRESCALER == 1 # define W1CFG_WDTPS 0 #elif BOARD_WD_PRESCALER == 2 @@ -671,9 +682,10 @@ #else # error "Unsupported BOARD_WD_PRESCALER" #endif + +#ifdef CONFIG_PIC32MZ_WDTENABLE # define W1CFG_WDTEN DEVCFG2_WDTEN #else -# define W1CFG_WDTPS 0 # define W1CFG_WDTEN 0 #endif diff --git a/arch/mips/src/pic32mz/pic32mz_wdt.c b/arch/mips/src/pic32mz/pic32mz_wdt.c new file mode 100644 index 00000000000..8107e6b0175 --- /dev/null +++ b/arch/mips/src/pic32mz/pic32mz_wdt.c @@ -0,0 +1,307 @@ +/**************************************************************************** + * arch/mips/src/pic32mz/pic32mz_wdt.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 <sys/types.h> + +#include <errno.h> +#include <inttypes.h> +#include <stdbool.h> +#include <stdint.h> +#include <syslog.h> + +#include <nuttx/arch.h> +#include <nuttx/clock.h> +#include <nuttx/debug.h> +#include <nuttx/irq.h> +#include <nuttx/timers/watchdog.h> + +#include "mips_internal.h" +#include "hardware/pic32mz_wdt.h" +#include "hardware/pic32mzw1_features.h" +#include "hardware/pic32mzw1_pmuclk.h" +#include "pic32mz_wdt.h" + +#ifdef CONFIG_PIC32MZ_WDT + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct pic32mz_wdt_lowerhalf_s +{ + const struct watchdog_ops_s *ops; /* Lower half operations */ + clock_t lastkick; /* System tick of the last kick */ + uint32_t timeout; /* Hardware time-out (ms) */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int pic32mz_wdt_start(struct watchdog_lowerhalf_s *lower); +static int pic32mz_wdt_stop(struct watchdog_lowerhalf_s *lower); +static int pic32mz_wdt_keepalive(struct watchdog_lowerhalf_s *lower); +static int pic32mz_wdt_getstatus(struct watchdog_lowerhalf_s *lower, + struct watchdog_status_s *status); +static int pic32mz_wdt_settimeout(struct watchdog_lowerhalf_s *lower, + uint32_t timeout); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct watchdog_ops_s g_wdtops = +{ + .start = pic32mz_wdt_start, + .stop = pic32mz_wdt_stop, + .keepalive = pic32mz_wdt_keepalive, + .getstatus = pic32mz_wdt_getstatus, + .settimeout = pic32mz_wdt_settimeout, +}; + +static struct pic32mz_wdt_lowerhalf_s g_wdt; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pic32mz_wdt_timeout_ms + * + * Description: + * Hardware time-out in milliseconds. The postscaler is the WDTPS field + * of CFGCON2, programmed from the device configuration; software cannot + * change it, so the time-out is fixed for a given image. (WDTCON.RUNDIV + * mirrors it, but is not relied on when the watchdog is not enabled by + * the configuration.) + * + ****************************************************************************/ + +static uint32_t pic32mz_wdt_timeout_ms(void) +{ + uint32_t rundiv; + uint64_t us; + + rundiv = (getreg32(PIC32MZ_CFGCON2) >> DEVCFG2_WDTPS_SHIFT) & + DEVCFG2_WDTPS_MASK; + if (rundiv > WDT_RUNDIV_MAX) + { + rundiv = WDT_RUNDIV_MAX; + } + + us = (uint64_t)CONFIG_PIC32MZ_WDT_BASE_US << rundiv; + return (uint32_t)((us + 999) / 1000); +} + +/**************************************************************************** + * Name: pic32mz_wdt_hwkick + * + * Description: + * Clear the watchdog counter. The datasheet requires the key to be + * written with a single 16-bit store to the upper half of WDTCON. + * + ****************************************************************************/ + +static void pic32mz_wdt_hwkick(struct pic32mz_wdt_lowerhalf_s *priv) +{ + putreg16(WDT_CLRKEY_VALUE, PIC32MZ_WDT_CLRKEY); + priv->lastkick = clock_systime_ticks(); +} + +/**************************************************************************** + * Name: pic32mz_wdt_start + * + * Description: + * Start the watchdog. When it is hardware-enabled by the device + * configuration (WDTEN) it is already running and this only kicks it. + * + ****************************************************************************/ + +static int pic32mz_wdt_start(struct watchdog_lowerhalf_s *lower) +{ + struct pic32mz_wdt_lowerhalf_s *priv = + (struct pic32mz_wdt_lowerhalf_s *)lower; + irqstate_t flags; + + flags = enter_critical_section(); + + pic32mz_wdt_hwkick(priv); + putreg32(WDT_CON_ON, PIC32MZ_WDT_CONSET); + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: pic32mz_wdt_stop + * + * Description: + * Stop the watchdog. This is not possible when WDTEN is set in the + * device configuration: the ON bit is then ignored by the hardware. + * + ****************************************************************************/ + +static int pic32mz_wdt_stop(struct watchdog_lowerhalf_s *lower) +{ + irqstate_t flags; + int ret = OK; + + flags = enter_critical_section(); + + putreg32(WDT_CON_ON, PIC32MZ_WDT_CONCLR); + + if ((getreg32(PIC32MZ_WDT_CON) & WDT_CON_ON) != 0) + { + /* Still running: hardware-enabled by the device configuration */ + + ret = -ENOTSUP; + } + + leave_critical_section(flags); + return ret; +} + +/**************************************************************************** + * Name: pic32mz_wdt_keepalive + ****************************************************************************/ + +static int pic32mz_wdt_keepalive(struct watchdog_lowerhalf_s *lower) +{ + struct pic32mz_wdt_lowerhalf_s *priv = + (struct pic32mz_wdt_lowerhalf_s *)lower; + irqstate_t flags; + + flags = enter_critical_section(); + pic32mz_wdt_hwkick(priv); + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: pic32mz_wdt_getstatus + ****************************************************************************/ + +static int pic32mz_wdt_getstatus(struct watchdog_lowerhalf_s *lower, + struct watchdog_status_s *status) +{ + struct pic32mz_wdt_lowerhalf_s *priv = + (struct pic32mz_wdt_lowerhalf_s *)lower; + uint32_t elapsed; + irqstate_t flags; + + flags = enter_critical_section(); + + status->flags = 0; + status->timeout = priv->timeout; + + if ((getreg32(PIC32MZ_WDT_CON) & WDT_CON_ON) != 0) + { + status->flags |= WDFLAGS_ACTIVE; + } + + /* The counter is not readable: derive the time left from the last kick */ + + elapsed = TICK2MSEC(clock_systime_ticks() - priv->lastkick); + status->timeleft = elapsed < priv->timeout ? priv->timeout - elapsed : 0; + + leave_critical_section(flags); + return OK; +} + +/**************************************************************************** + * Name: pic32mz_wdt_settimeout + * + * Description: + * The time-out is fixed by the device configuration. A request for a + * time-out longer than the hardware one cannot be honored and fails; + * a shorter one is accepted (the watchdog then fires later than asked, + * which is safe for a caller that keeps alive within what it asked for). + * As the method specifies, the watchdog is also kicked. + * + ****************************************************************************/ + +static int pic32mz_wdt_settimeout(struct watchdog_lowerhalf_s *lower, + uint32_t timeout) +{ + struct pic32mz_wdt_lowerhalf_s *priv = + (struct pic32mz_wdt_lowerhalf_s *)lower; + + if (timeout == 0 || timeout > priv->timeout) + { + wdwarn("time-out %" PRIu32 " ms not possible, hardware is %" PRIu32 + " ms\n", timeout, priv->timeout); + return -ERANGE; + } + + return pic32mz_wdt_keepalive(lower); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: pic32mz_wdt_initialize + ****************************************************************************/ + +int pic32mz_wdt_initialize(const char *devpath) +{ + struct pic32mz_wdt_lowerhalf_s *priv = &g_wdt; + void *handle; + + if ((getreg32(PIC32MZ_RCON) & RCON_WDTO) != 0) + { + syslog(LOG_WARNING, "Last reset was caused by the watchdog\n"); + putreg32(RCON_WDTO, PIC32MZ_RCONCLR); + } + + priv->ops = &g_wdtops; + priv->timeout = pic32mz_wdt_timeout_ms(); + priv->lastkick = clock_systime_ticks(); + + handle = watchdog_register(devpath, + (struct watchdog_lowerhalf_s *)priv); + if (handle == NULL) + { + return -EEXIST; + } + + /* Without the fuse it is stopped, and the application starts it. With + * the fuse it is running already: keep it alive until the application + * takes over. + */ + + if ((getreg32(PIC32MZ_WDT_CON) & WDT_CON_ON) != 0) + { + pic32mz_wdt_hwkick(priv); + } + + return OK; +} + +#endif /* CONFIG_PIC32MZ_WDT */ diff --git a/arch/mips/src/pic32mz/pic32mz_wdt.h b/arch/mips/src/pic32mz/pic32mz_wdt.h new file mode 100644 index 00000000000..7f2f1773daa --- /dev/null +++ b/arch/mips/src/pic32mz/pic32mz_wdt.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * arch/mips/src/pic32mz/pic32mz_wdt.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_MIPS_SRC_PIC32MZ_PIC32MZ_WDT_H +#define __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_WDT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#ifndef __ASSEMBLY__ + +#undef EXTERN +#if defined(__cplusplus) +# define EXTERN extern "C" +extern "C" +{ +#else +# define EXTERN extern +#endif + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Name: pic32mz_wdt_initialize + * + * Description: + * Initialize the watchdog timer and register it as a watchdog device. + * If the last reset was caused by the watchdog, that is logged and the + * reset cause flag is cleared. + * + * Input Parameters: + * devpath - The full path to the watchdog device, e.g. "/dev/watchdog0" + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value on failure. + * + ****************************************************************************/ + +int pic32mz_wdt_initialize(const char *devpath); + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* __ASSEMBLY__ */ +#endif /* __ARCH_MIPS_SRC_PIC32MZ_PIC32MZ_WDT_H */
