diff --git a/cpu/nrf5x_common/Makefile.features b/cpu/nrf5x_common/Makefile.features index ca0680d180..02085c0d85 100644 --- a/cpu/nrf5x_common/Makefile.features +++ b/cpu/nrf5x_common/Makefile.features @@ -6,6 +6,7 @@ FEATURES_PROVIDED += periph_gpio periph_gpio_irq FEATURES_PROVIDED += periph_hwrng FEATURES_PROVIDED += periph_temperature FEATURES_PROVIDED += periph_uart_modecfg +FEATURES_PROVIDED += periph_wdt periph_wdt_cb # Various other features (if any) FEATURES_PROVIDED += radio_nrfmin diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index 0dde3b6a4f..817fefa150 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -165,6 +165,15 @@ typedef struct { uint8_t miso; /**< MISO pin */ } spi_conf_t; +/** + * @name WDT upper and lower bound times in ms + * @{ + */ +#define NWDT_TIME_LOWER_LIMIT (1) +/* Set upper limit to the maximum possible value that could go in CRV register */ +#define NWDT_TIME_UPPER_LIMIT ((UINT32_MAX >> 15) * US_PER_MS + 1) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/nrf5x_common/periph/wdt.c b/cpu/nrf5x_common/periph/wdt.c new file mode 100644 index 0000000000..7ef31fb184 --- /dev/null +++ b/cpu/nrf5x_common/periph/wdt.c @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2019 Inria + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_nrf5x_common + * @ingroup drivers_periph_wdt + * @{ + * + * @file + * @brief Implementation of the watchdog peripheral interface + * + * @author Alexandre Abadie + * + * @} + */ + +#include "cpu.h" +#include "timex.h" +#include "periph/wdt.h" + +#include "nrf_clock.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/* By default, allow watchdog during sleep. + Warning: pausing the watchdog during sleep will deactivate it when RIOT + switches to idle thread where pm_set_lowest is called. */ +#ifndef NRF_WDT_SLEEP_MODE +#define NRF_WDT_SLEEP_MODE (WDT_CONFIG_SLEEP_Run) +#endif + +/* By default, allow watchdog during debug session. */ +#ifndef NRF_WDT_HALT_MODE +#define NRF_WDT_HALT_MODE (WDT_CONFIG_HALT_Run) +#endif + +#ifdef MODULE_PERIPH_WDT_CB +static wdt_cb_t wdt_cb; +static void *wdt_arg; +#endif + +void wdt_start(void) +{ + DEBUG("[wdt] start watchdog\n"); + + NRF_WDT->TASKS_START = 1; +} + +void wdt_stop(void) +{ + DEBUG("[wdt] stopping the watchdog is not supported\n"); + assert(0); +} + +void wdt_kick(void) +{ + assert(NRF_WDT->RUNSTATUS == WDT_RUNSTATUS_RUNSTATUS_Running); + + DEBUG("[wdt] reload the watchdog\n"); + + NRF_WDT->RR[0] = WDT_RR_RR_Reload; +} + +void wdt_setup_reboot(uint32_t min_time, uint32_t max_time) +{ + (void)min_time; + + /* Windowed wdt not supported */ + assert(min_time == 0); + + /* Check reset time limit */ + assert((max_time > NWDT_TIME_LOWER_LIMIT) || \ + (max_time < NWDT_TIME_UPPER_LIMIT)); + + /* configure watchdog behavior during sleep */ + NRF_WDT->CONFIG &= ~(WDT_CONFIG_SLEEP_Msk << WDT_CONFIG_SLEEP_Pos); + NRF_WDT->CONFIG |= (NRF_WDT_SLEEP_MODE << WDT_CONFIG_SLEEP_Pos); + + /* configure watchdog behavior during debug */ + NRF_WDT->CONFIG &= ~(WDT_CONFIG_HALT_Msk << WDT_CONFIG_HALT_Pos); + NRF_WDT->CONFIG |= (NRF_WDT_HALT_MODE << WDT_CONFIG_HALT_Pos); + + /* timeout (s) = (CRV + 1) / 32768 */ + uint32_t crv = ((max_time << 15) / 1000) - 1; + DEBUG("[wdt] setting CRV to %"PRIu32"\n", crv); + NRF_WDT->CRV = crv; + + DEBUG("[wdt] CRV configuration: %"PRIu32"\n", NRF_WDT->CRV); + + /* Enable reload requests */ + NRF_WDT->RREN = (WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos); + + DEBUG("[wdt] watchdog setup complete\n"); +} + +#ifdef MODULE_PERIPH_WDT_CB +/* The reset can't be stopped when the callback is triggered: so the MCU will + reset in any case after 2 cycles of 32.768kHz clock. This is very short so + only basic and fast operations can be perfomed in the callback function. */ +void wdt_setup_reboot_with_callback(uint32_t min_time, uint32_t max_time, + wdt_cb_t cb, void* arg) +{ + wdt_cb = cb; + wdt_arg = arg; + + /* Disable interrupt */ + NVIC_DisableIRQ(WDT_IRQn); + + if (cb) { + /* enable interrupt */ + NVIC_EnableIRQ(WDT_IRQn); + NRF_WDT->INTENSET = WDT_INTENSET_TIMEOUT_Enabled; + } + + wdt_setup_reboot(min_time, max_time); +} + +void isr_wdt(void) +{ + wdt_cb(wdt_arg); + + cortexm_isr_end(); +} +#endif /* MODULE_PERIPH_WDT_CB */