diff --git a/arch/arm/src/max326xx/Kconfig b/arch/arm/src/max326xx/Kconfig index 23fa2768590..d0d08c3ea12 100644 --- a/arch/arm/src/max326xx/Kconfig +++ b/arch/arm/src/max326xx/Kconfig @@ -50,7 +50,7 @@ config ARCH_FAMILY_MAX32620 default n select ARCH_CORTEXM4 select MAX326XX_HAVE_WWDOG - select MAX326XX_HAVE_RWDOG + select MAX326XX_HAVE_LLWDOG select MAX326XX_HAVE_WAKEUP select MAX326XX_HAVE_CRC select MAX326XX_HAVE_AES @@ -76,6 +76,7 @@ config ARCH_FAMILY_MAX32630 default n select ARCH_CORTEXM4 select MAX326XX_HAVE_WWDOG + select MAX326XX_HAVE_LLWDOG select MAX326XX_HAVE_WAKEUP select MAX326XX_HAVE_CRC select MAX326XX_HAVE_AES @@ -117,7 +118,7 @@ config MAX326XX_HAVE_WWDOG bool default n -config MAX326XX_HAVE_RWDOG +config MAX326XX_HAVE_LLWDOG bool default n @@ -225,19 +226,19 @@ config MAX326XX_WDOG depends on MAX326XX_HAVE_WDOG config MAX326XX_WWDOG0 - bool "Windowed Watchdog Timer 0" + bool "Windowed Watchdog Timer (WDT0) " default n depends on MAX326XX_HAVE_WWDOG config MAX326XX_WWDOG1 - bool "Windowed Watchdog Timer 1" + bool "Low-Level Watchdog Timer (WDT1)" default n depends on MAX326XX_HAVE_WWDOG config MAX326XX_RWDOG bool "Recovery Watchdog" default n - depends on MAX326XX_HAVE_RWDOG + depends on MAX326XX_HAVE_LLWDOG config MAX326XX_WAKEUP bool "Wakeup Timer" diff --git a/arch/arm/src/max326xx/Make.defs b/arch/arm/src/max326xx/Make.defs index 1c51d6fcba3..5f1d0059269 100644 --- a/arch/arm/src/max326xx/Make.defs +++ b/arch/arm/src/max326xx/Make.defs @@ -122,7 +122,7 @@ CHIP_CSRCS += max32660_gpioirq.c endif endif -ifeq ($(CONFIG_RTC),y) +ifeq ($(CONFIG_MAX32XX_WDT),y) ifeq ($(CONFIG_ARCH_FAMILY_MAX32660),y) CHIP_CSRCS += max32660_rtc.c endif @@ -131,7 +131,7 @@ CHIP_CSRCS += max326_rtc_lowerhalf.c endif endif -ifeq ($(CONFIG_MAX326XX_WDT),y) +ifeq ($(CONFIG_MAX326XX_WDOG),y) ifeq ($(CONFIG_ARCH_FAMILY_MAX32660),y) CHIP_CSRCS += max32660_wdt.c endif diff --git a/arch/arm/src/max326xx/hardware/max32660_wdt.h b/arch/arm/src/max326xx/hardware/max32660_wdt.h index 845705cedae..2cd2bb62182 100644 --- a/arch/arm/src/max326xx/hardware/max32660_wdt.h +++ b/arch/arm/src/max326xx/hardware/max32660_wdt.h @@ -63,40 +63,42 @@ #define WDT0_CTRL_INTPERIOD_SHIFT (0) /* Bit 0-3: Interrupt Period */ #define WDT0_CTRL_INTPERIOD_MASK (15 << WDT0_CTRL_INTPERIOD_SHIFT) -# define WDT0_CTRL_INTPERIOD_216X (15 << WDT0_CTRL_INTPERIOD_SHIFT) /* 216 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_217X (14 << WDT0_CTRL_INTPERIOD_SHIFT) /* 217 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_218X (13 << WDT0_CTRL_INTPERIOD_SHIFT) /* 218 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_219X (12 << WDT0_CTRL_INTPERIOD_SHIFT) /* 219 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_220X (11 << WDT0_CTRL_INTPERIOD_SHIFT) /* 220 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_221X (10 << WDT0_CTRL_INTPERIOD_SHIFT) /* 221 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_222X (9 << WDT0_CTRL_INTPERIOD_SHIFT) /* 222 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_223X (8 << WDT0_CTRL_INTPERIOD_SHIFT) /* 223 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_224X (7 << WDT0_CTRL_INTPERIOD_SHIFT) /* 224 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_225X (6 << WDT0_CTRL_INTPERIOD_SHIFT) /* 225 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_226X (5 << WDT0_CTRL_INTPERIOD_SHIFT) /* 226 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_227X (4 << WDT0_CTRL_INTPERIOD_SHIFT) /* 227 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_228X (3 << WDT0_CTRL_INTPERIOD_SHIFT) /* 228 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_229X (2 << WDT0_CTRL_INTPERIOD_SHIFT) /* 229 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_230X (1 << WDT0_CTRL_INTPERIOD_SHIFT) /* 230 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_INTPERIOD_231X (0 << WDT0_CTRL_INTPERIOD_SHIFT) /* 231 Γ— 𝑑𝑃𝐢𝐿𝐾 */ +# define WDT0_CTRL_INTPERIOD(n) ((uint32_t)(n) << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^16 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_216X (15 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^16 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_217X (14 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^17 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_218X (13 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^18 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_219X (12 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^19 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_220X (11 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^20 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_221X (10 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^21 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_222X (9 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^22 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_223X (8 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^23 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_224X (7 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^24 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_225X (6 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^25 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_226X (5 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^26 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_227X (4 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^27 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_228X (3 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^28 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_229X (2 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^29 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_230X (1 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^30 x Tpclk */ +# define WDT0_CTRL_INTPERIOD_231X (0 << WDT0_CTRL_INTPERIOD_SHIFT) /* 2^31 x Tpclk */ #define WDT0_CTRL_RSTPERIOD_SHIFT (4) /* Bits 4-7: Reset Period */ #define WDT0_CTRL_RSTPERIOD_MASK (15 << WDT0_CTRL_RSTPERIOD_SHIFT) -# define WDT0_CTRL_RSTPERIOD_216X (15 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 216 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_217X (14 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 217 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_218X (13 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 218 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_219X (12 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 219 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_220X (11 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 220 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_221X (10 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 221 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_222X (9 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 222 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_223X (8 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 223 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_224X (7 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 224 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_225X (6 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 225 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_226X (5 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 226 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_227X (4 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 227 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_228X (3 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 228 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_229X (2 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 229 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_230X (1 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 230 Γ— 𝑑𝑃𝐢𝐿𝐾 */ -# define WDT0_CTRL_RSTPERIOD_231X (0 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 231 Γ— 𝑑𝑃𝐢𝐿𝐾 */ +# define WDT0_CTRL_RSTPERIOD(n) ((uint32_t)(n) << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^16 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_216X (15 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^16 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_217X (14 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^17 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_218X (13 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^18 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_219X (12 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^19 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_220X (11 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^20 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_221X (10 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^21 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_222X (9 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^22 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_223X (8 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^23 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_224X (7 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^24 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_225X (6 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^25 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_226X (5 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^26 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_227X (4 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^27 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_228X (3 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^28 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_229X (2 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^29 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_230X (1 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^30 x Tpclk */ +# define WDT0_CTRL_RSTPERIOD_231X (0 << WDT0_CTRL_RSTPERIOD_SHIFT) /* 2^31 x Tpclk */ #define WDT0_CTRL_WDTEN (1 << 8) /* Bit 8: Enable */ #define WDT0_CTRL_INTFLAG (1 << 9) /* Bit 9: Interrupt Flag */ #define WDT0_CTRL_INTEN (1 << 10) /* Bit 10: Interrupt Enable */ diff --git a/arch/arm/src/max326xx/max32660/max32660_serial.c b/arch/arm/src/max326xx/max32660/max32660_serial.c index 7d9ea65661b..4c5cafcf4fb 100644 --- a/arch/arm/src/max326xx/max32660/max32660_serial.c +++ b/arch/arm/src/max326xx/max32660/max32660_serial.c @@ -1,7 +1,7 @@ /**************************************************************************** * arch/arm/src/max326xx/max326_serial.c * - * Copyright (C) 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without diff --git a/arch/arm/src/max326xx/max32660/max32660_wdt.c b/arch/arm/src/max326xx/max32660/max32660_wdt.c new file mode 100644 index 00000000000..ff4135351b3 --- /dev/null +++ b/arch/arm/src/max326xx/max32660/max32660_wdt.c @@ -0,0 +1,710 @@ +/**************************************************************************** + * arch/arm/src/max326xx/max326_wdt.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "chip.h" +#include "up_arch.h" +#include "up_internal.h" + +#include "hardware/max326_wdt.h" +#include "max326_clockconfig.h" +#include "max326_wdt.h" + +#if defined(CONFIG_WATCHDOG) && defined(CONFIG_MAX326XX_WDOG) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Clocking *****************************************************************/ + +/* The watchdog is alway running with source clock Fpclk. There are two + * timeout periods: + * + * INTPERIOD - Number of PCLK cycles until a watchdog timer interrupt is + * generated. This period must be less than the Reset Period + * Timeout for the watchdog timer interrupt to occur. + * RSTPERIOD - The number of PCLK cycles until a system reset event occurs. + * + * Tint = 2 ^ (31 - INTPERIOD) / Fpclk + * Trst = 2 ^ (31 - RSTPERIOD) / Fpclk + */ + +/* If an interrupt is selected, then the reset will follow the interrupt by + * this fixed timer period. + */ + +#define WDT_RESET_DELAY MSEC_PER_SEC /* One second in units of milliseconds */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This structure provides the private representation of the "lower-half" + * driver state structure. This structure must be cast-compatible with the + * well-known watchdog_lowerhalf_s structure. + */ + +struct max326_wdt_lowerhalf_s +{ + FAR const struct watchdog_ops_s *ops; /* Lower half operations */ + uint8_t exp; /* log12(Reset time period) */ + xcpt_t handler; /* User interrupt handler */ + clock_t lastping; /* Time of last WDT reset */ +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static inline void max326_wdog_reset(FAR struct max326_wdt_lowerhalf_s *priv); +static void max326_int_enable(FAR struct max326_wdt_lowerhalf_s *priv); +static uint32_t max326_time_left(FAR struct max326_wdt_lowerhalf_s *priv); +static uint64_t max326_exp2msec(uint32_t pclk, uint8_t exp); +static uint8_t max326_msec2exp(uint32_t msec); +static inline void max326_setexp(uint8_t intexp, uint8_t rstexp); + +/* "Lower half" driver methods **********************************************/ + +static int max326_start(FAR struct watchdog_lowerhalf_s *lower); +static int max326_stop(FAR struct watchdog_lowerhalf_s *lower); +static int max326_keepalive(FAR struct watchdog_lowerhalf_s *lower); +static int max326_getstatus(FAR struct watchdog_lowerhalf_s *lower, + FAR struct watchdog_status_s *status); +static int max326_settimeout(FAR struct watchdog_lowerhalf_s *lower, + uint32_t timeout); +static xcpt_t max326_capture(FAR struct watchdog_lowerhalf_s *lower, + xcpt_t handler); +static int max326_ioctl(FAR struct watchdog_lowerhalf_s *lower, int cmd, + unsigned long arg); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* "Lower half" driver methods */ + +static const struct watchdog_ops_s g_wdtops = +{ + .start = max326_start, + .stop = max326_stop, + .keepalive = max326_keepalive, + .getstatus = max326_getstatus, + .settimeout = max326_settimeout, + .capture = max326_capture, + .ioctl = max326_ioctl, +}; + +/* "Lower half" driver state */ + +static struct max326_wdt_lowerhalf_s g_wdtdev; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_wdog_reset + * + * Description: + * Reset the watchdog timer + * + * Input Parameters: + * priv - WDT state instance. + * + * Returned Values: + * None. + * + ****************************************************************************/ + +static inline void max326_wdog_reset(FAR struct max326_wdt_lowerhalf_s *priv) +{ + putreg32(WDT0_RST_SEQ1, MAX326_WDT0_RST); + putreg32(WDT0_RST_SEQ2, MAX326_WDT0_RST); + + priv->lastping = clock_systimer(); +} + +/**************************************************************************** + * Name: max326_int_enable + * + * Description: + * Enable reset or interrupts + * + * Input Parameters: + * priv - WDT state instance. + * + * Returned Values: + * None. + * + ****************************************************************************/ + +static void max326_int_enable(FAR struct max326_wdt_lowerhalf_s *priv) +{ + uint32_t ctrl; + + /* Set clear reset/interrupt enables */ + + ctrl = getreg32(MAX326_WDT0_CTRL); + ctrl &= ~(WDT0_CTRL_INTEN | WDT0_CTRL_RSTEN); + + /* Is there and interrupt handler attached? */ + + if (priv->handler != NULL) + { + irq_attach(MAX326_IRQ_WDT0, priv->handler, priv); + up_enable_irq(MAX326_IRQ_WDT0); + + ctrl |= WDT0_CTRL_INTEN; + } + else + { + up_disable_irq(MAX326_IRQ_WDT0); + irq_detach(MAX326_IRQ_WDT0); + + ctrl |= WDT0_CTRL_RSTEN; + } + + putreg32(ctrl, MAX326_WDT0_CTRL); +} + +/**************************************************************************** + * Name: max326_time_left + * + * Description: + * Return the time left before reset or interrupt in milliseconds. + * + * Input Parameters: + * priv - WDT state instance. + * + * Returned Values: + * None. + * + ****************************************************************************/ + +static uint32_t max326_time_left(FAR struct max326_wdt_lowerhalf_s *priv) +{ + clock_t timeleft = clock_systimer() - priv->lastping; + return TICK2MSEC(timeleft); +} + +/**************************************************************************** + * Name: max326_exp2msec + * + * Description: + * Convert a max3660 time exponent to a time period in milliseconds. + * + * Input Parameters: + * pclk - Peripheral clock frequency. + * exp - max32660 time exponent + * + * Returned Values: + * The time period in millseconds. + * + ****************************************************************************/ + +static uint64_t max326_exp2msec(uint32_t pclk, uint8_t exp) +{ + return 1000LL * ((1LL << (31 - exp)) / (uint64_t)pclk); +} + +/**************************************************************************** + * Name: max326_msec2exp + * + * Description: + * Convert a time period in milliseconds to the time exponent used by the + * max32660. + * + * Input Parameters: + * priv - WDT state instance. + * msec - Time period in milliseconds + * + * Returned Values: + * The max32660 time exponent. + * + ****************************************************************************/ + +static uint8_t max326_msec2exp(uint32_t msec) +{ + int64_t error; + uint32_t timeout; + uint32_t candidate; + uint32_t pclk; + + wdinfo("Entry: msec=%lu\n", (unsigned long)msec); + + /* Convert the timeout in millisconds to the timeut exponent value. + * + * Tsec = (2 ^ (31 - timeout)) / Fpclk. + */ + + timeout = 0; + error = INT64_MAX; + pclk = max326_pclk_frequency(); + + for (candidate = 0; candidate < 16; candidate++) + { + int64_t candmsec = max326_exp2msec(pclk, candidate); + int64_t canderr = msec - candmsec; + + if (canderr < 0) + { + canderr = -canderr; + } + + if (canderr < error) + { + error = canderr; + timeout = candidate; + } + } + + return timeout; +} + +/**************************************************************************** + * Name: max326_setexp + * + * Description: + * Set the time period exponents. + * + * Input Parameters: + * intexp - Interrupt time exponent. + * rstexp - Reset time exponent + * + * Returned Values: + * None + * + ****************************************************************************/ + +static inline void max326_setexp(uint8_t intexp, uint8_t rstexp) +{ + uint32_t ctrl; + + ctrl = getreg32(MAX326_WDT0_CTRL); + ctrl &= ~(WDT0_CTRL_INTPERIOD_MASK | WDT0_CTRL_RSTPERIOD_MASK); + ctrl |= (WDT0_CTRL_INTPERIOD(intexp) | WDT0_CTRL_RSTPERIOD(rstexp)); + putreg32(ctrl, MAX326_WDT0_CTRL); +} + +/**************************************************************************** + * Name: max326_start + * + * Description: + * Start the watchdog timer, resetting the time to the current timeout. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_start(FAR struct watchdog_lowerhalf_s *lower) +{ + FAR struct max326_wdt_lowerhalf_s *priv = + (FAR struct max326_wdt_lowerhalf_s *)lower; + irqstate_t flags; + uint32_t ctrl; + + DEBUGASSERT(priv != NULL); + + wdinfo("Entry: enabled=%s timeout=%lu\n", + (getreg32(MAX326_WDT0_CTRL) & WDT0_CTRL_WDTEN) != 0 ? "Yes" : "No", + (unsigned long)1 << (31 - priv->exp)); + + /* Perform the reset sequence */ + + flags = spin_lock_irqsave(); + max326_wdog_reset(priv); + + /* Enable reset or interrupt */ + + max326_int_enable(priv); + + /* Then enable the watchdog timer */ + + ctrl = getreg32(MAX326_WDT0_CTRL); + ctrl |= WDT0_CTRL_WDTEN; + putreg32(ctrl, MAX326_WDT0_CTRL); + + spin_unlock_irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Name: max326_stop + * + * Description: + * Stop the watchdog timer + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_stop(FAR struct watchdog_lowerhalf_s *lower) +{ + irqstate_t flags; + uint32_t ctrl; + + DEBUGASSERT(priv != NULL); + + wdinfo("Entry: enabled=%s timeout=%lu\n", + (getreg32(MAX326_WDT0_CTRL) & WDT0_CTRL_WDTEN) != 0 ? "Yes" : "No", + (unsigned long)1 << (31 - priv->exp)); + + /* Disable the watchdog timer, reset, and interrupts */ + + flags = spin_lock_irqsave(); + ctrl = getreg32(MAX326_WDT0_CTRL); + ctrl &= ~(WDT0_CTRL_WDTEN | WDT0_CTRL_INTEN | WDT0_CTRL_RSTEN); + + up_disable_irq(MAX326_IRQ_WDT0); + irq_detach(MAX326_IRQ_WDT0); + + spin_unlock_irqrestore(flags); + return OK; +} + +/**************************************************************************** + * Name: max326_keepalive + * + * Description: + * Reset the watchdog timer to the current timeout value, prevent any + * imminent watchdog timeouts. This is sometimes referred as "pinging" + * the watchdog timer or "petting the dog". + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_keepalive(FAR struct watchdog_lowerhalf_s *lower) +{ + FAR struct max326_wdt_lowerhalf_s *priv = + (FAR struct max326_wdt_lowerhalf_s *)lower; + irqstate_t flags; + + wdinfo("Ping!\n"); + + /* Reset WDT timer */ + + flags = spin_lock_irqsave(); + max326_wdog_reset(priv); + spin_unlock_irqrestore(flags); + + return OK; +} + +/**************************************************************************** + * Name: max326_getstatus + * + * Description: + * Get the current watchdog timer status + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * status - The location to return the watchdog status information. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_getstatus(FAR struct watchdog_lowerhalf_s *lower, + FAR struct watchdog_status_s *status) +{ + FAR struct max326_wdt_lowerhalf_s *priv = + (FAR struct max326_wdt_lowerhalf_s *)lower; + uint64_t msec; + + wdinfo("Entry\n"); + DEBUGASSERT(priv != NULL && status != NULL); + + /* Return the status flags */ + + status->flags = 0; + if ((getreg32(MAX326_WDT0_CTRL) & WDT0_CTRL_WDTEN) != 0) + { + status->flags |= WDFLAGS_ACTIVE; + } + + status->flags |= priv->handler != NULL ? WDFLAGS_CAPTURE : WDFLAGS_RESET; + + /* Return the timeout value in millisconds. + * + * Tsec = (2 ^ (31 - timeout)) / Fpclk. + */ + + msec = max326_exp2msec(max326_pclk_frequency(), priv->exp); + if (msec > UINT32_MAX) + { + msec = UINT32_MAX; + } + + status->timeout = (uint32_t)msec; + + /* Return the time left until the expiration */ + + status->timeleft = max326_time_left(priv); + + wdinfo("Status :\n"); + wdinfo(" flags : %08x\n", status->flags); + wdinfo(" timeout : %d\n", status->timeout); + wdinfo(" timeleft : %d\n", status->timeleft); + return OK; +} + +/**************************************************************************** + * Name: max326_settimeout + * + * Description: + * Set a new timeout value (and reset the watchdog timer) + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * timeout - The new timeout value in milliseconds. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_settimeout(FAR struct watchdog_lowerhalf_s *lower, + uint32_t timeout) +{ + FAR struct max326_wdt_lowerhalf_s *priv = + (FAR struct max326_wdt_lowerhalf_s *)lower; + uint8_t intexp; + uint8_t rstexp; + + wdinfo("Entry: timeout=%lu\n", (unsigned long)timeout); + DEBUGASSERT(priv != NULL); + + /* Convert the timeout value in milliseconds to time exponent used by the + * max32660. + */ + + intexp = max326_msec2exp(timeout); + rstexp = intexp; + + /* Interrupt? or reset? */ + + if (priv->handler) + { + rstexp = max326_msec2exp(timeout + WDT_RESET_DELAY); + } + + max326_setexp(intexp, rstexp); + return OK; +} + +/**************************************************************************** + * Name: max326_capture + * + * Description: + * Attach the user WDT interrupt handler and enable the interrupt. + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * handler - The new WDT interrupt handler. + * + * Returned Values: + * The previous WDT interrupt handler. + * + ****************************************************************************/ + +static xcpt_t max326_capture(FAR struct watchdog_lowerhalf_s *lower, + xcpt_t handler) +{ + FAR struct max326_wdt_lowerhalf_s *priv = + (FAR struct max326_wdt_lowerhalf_s *)lower; + irqstate_t flags; + xcpt_t oldhandler; + uint64_t timeout; + uint32_t ctrl; + uint32_t exp; + + DEBUGASSERT(priv != NULL) + wdinfo("Handler=%p\n", handler); + + /* Get the old handler */ + + flags = spin_lock_irqsave(); + oldhandler = priv->handler; + + /* Save the new handler */ + + priv->handler = handler; + + /* Reset the WDT timer */ + + max326_wdog_reset(priv); + + /* Are we adding, removing, or changing a handler? */ + + if (handler != NULL && oldhandler == NULL) + { + /* Adding a handler */ + /* Set a new, longer reset period */ + + ctrl = getreg32(MAX326_WDT0_CTRL); + exp = (ctrl & WDT0_CTRL_INTPERIOD_MASK) >> WDT0_CTRL_INTPERIOD_SHIFT; + + timeout = max326_exp2msec(max326_pclk_frequency(), exp); + exp = max326_msec2exp(timeout + WDT_RESET_DELAY); + + ctrl &= ~WDT0_CTRL_RSTPERIOD_MASK; + ctrl |= WDT0_CTRL_RSTPERIOD(exp); + putreg32(ctrl, MAX326_WDT0_CTRL); + + /* Disable the reset event. Enable the interrupt event */ + + max326_int_enable(priv); + } + else if (handler == NULL && oldhandler != NULL) + { + /* Removing a handler */ + /* Reset the rst period to the intperiod */ + + ctrl = getreg32(MAX326_WDT0_CTRL); + exp = (ctrl & WDT0_CTRL_INTPERIOD_MASK) >> WDT0_CTRL_INTPERIOD_SHIFT; + + ctrl &= ~WDT0_CTRL_RSTPERIOD_MASK; + ctrl |= WDT0_CTRL_RSTPERIOD(exp); + putreg32(ctrl, MAX326_WDT0_CTRL); + + /* Disable the interrupt event. Enable the reset event */ + + max326_int_enable(priv); + } + + spin_unlock_irqrestore(flags); + return oldhandler; +} + +/**************************************************************************** + * Name: max326_ioctl + * + * Description: + * Handle IOCTL commands forwarded from the upper half driver + * + * Input Parameters: + * lower - A pointer the publicly visible representation of the "lower-half" + * driver state structure. + * cmd - IOCTL command + * arg - Argument associated with the IOCTL command. + * + * Returned Values: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +static int max326_ioctl(FAR struct watchdog_lowerhalf_s *lower, int cmd, + unsigned long arg) +{ + return -ENOSYS; /* None implemented */ +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_wdt_initialize + * + * Description: + * Initialize the WDT watchdog time. The watchdog timer is initialized and + * registers as 'devpath. The initial state of the watchdog time is + * disabled. + * + * Input Parameters: + * devpath - The full path to the watchdog. This should be of the form + * /dev/watchdog0 + * + * Returned Values: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int max326_wdt_initialize(FAR const char *devpath) +{ + FAR struct max326_wdt_lowerhalf_s *priv = &g_wdtdev; + FAR void *handle; + + wdinfo("Entry: devpath=%s, mode_sleep=%d, mode_halt=%d\n", + devpath, mode_sleep, mode_halt); + + /* Initialize the driver state structure. */ + + priv->ops = &g_wdtops; + + /* Register the watchdog driver as /dev/watchdog0 */ + + handle = watchdog_register(devpath, (FAR struct watchdog_lowerhalf_s *)priv); + return (handle != NULL) ? OK : -ENODEV; +} + +#endif /* CONFIG_WATCHDOG && CONFIG_MAX326XX_WDOG */ diff --git a/arch/arm/src/max326xx/max326_wdt.h b/arch/arm/src/max326xx/max326_wdt.h new file mode 100644 index 00000000000..a08f9bf8569 --- /dev/null +++ b/arch/arm/src/max326xx/max326_wdt.h @@ -0,0 +1,92 @@ +/**************************************************************************** + * arch/arm/src/max326xx/max326_wdt.h + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +#ifndef __ARCH_ARM_SRC_MAX326XX_MAX326_WDT_H +#define __ARCH_ARM_SRC_MAX326XX_MAX326_WDT_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#ifdef CONFIG_WATCHDOG + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#undef EXTERN +#if defined(__cplusplus) +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: max326_wdt_initialize + * + * Description: + * Initialize the WDT watchdog time. The watchdog timer is initialized and + * registers as 'devpath. The initial state of the watchdog time is + * disabled. + * + * Input Parameters: + * devpath - The full path to the watchdog. This should be of the form + * /dev/watchdog0 + + * Returned Values: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_MAX326XX_WDOG +int max326_wdt_initialize(FAR const char *devpath); +#endif + +#undef EXTERN +#if defined(__cplusplus) +} +#endif + +#endif /* CONFIG_WATCHDOG */ +#endif /* __ARCH_ARM_SRC_MAX326XX_MAX326_WDT_H */