From 3e2303d30ca3820e565fc46daf644e8763e280ab Mon Sep 17 00:00:00 2001 From: Bas Stottelaar Date: Sun, 12 Jan 2020 22:55:50 +0100 Subject: [PATCH] cpu/efm32: add watchdog peripheral --- cpu/efm32/Kconfig | 8 ++ cpu/efm32/Makefile.dep | 4 + cpu/efm32/Makefile.features | 5 + cpu/efm32/families/efm32gg/Kconfig | 1 + cpu/efm32/families/efm32lg/Kconfig | 1 + cpu/efm32/families/efm32pg12b/Kconfig | 1 + cpu/efm32/families/efm32pg1b/Kconfig | 1 + cpu/efm32/families/efm32zg/Kconfig | 1 + cpu/efm32/families/efr32mg12p/Kconfig | 1 + cpu/efm32/families/efr32mg1p/Kconfig | 1 + cpu/efm32/include/periph_cpu.h | 18 +++ cpu/efm32/periph/wdt_series0.c | 83 ++++++++++++ cpu/efm32/periph/wdt_series1.c | 181 ++++++++++++++++++++++++++ 13 files changed, 306 insertions(+) create mode 100644 cpu/efm32/periph/wdt_series0.c create mode 100644 cpu/efm32/periph/wdt_series1.c diff --git a/cpu/efm32/Kconfig b/cpu/efm32/Kconfig index 93aebb4bec..03f45d03ef 100644 --- a/cpu/efm32/Kconfig +++ b/cpu/efm32/Kconfig @@ -13,6 +13,14 @@ config CPU_COMMON_EFM32 select HAS_PERIPH_FLASHPAGE_RAW select HAS_PERIPH_GPIO select HAS_PERIPH_GPIO_IRQ + select HAS_PERIPH_WDT + +config CPU_EFM32_SERIES0 + bool + +config CPU_EFM32_SERIES1 + bool + select HAS_PERIPH_WDT_CB ## Definition of specific features config HAS_ARCH_EFM32 diff --git a/cpu/efm32/Makefile.dep b/cpu/efm32/Makefile.dep index 49c79d26d6..fc8804802e 100644 --- a/cpu/efm32/Makefile.dep +++ b/cpu/efm32/Makefile.dep @@ -6,6 +6,10 @@ ifneq (,$(filter periph_rtt,$(USEMODULE))) USEMODULE += periph_rtt_series$(EFM32_SERIES) endif +ifneq (,$(filter periph_wdt,$(USEMODULE))) + USEMODULE += periph_wdt_series$(EFM32_SERIES) +endif + # include Gecko SDK package USEPKG += gecko_sdk diff --git a/cpu/efm32/Makefile.features b/cpu/efm32/Makefile.features index 7146f330f2..f49e25c7ec 100644 --- a/cpu/efm32/Makefile.features +++ b/cpu/efm32/Makefile.features @@ -13,6 +13,7 @@ FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_raw FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_wdt FEATURES_CONFLICT += periph_rtc:periph_rtt FEATURES_CONFLICT_MSG += "On the EFM32, the RTC and RTT map to the same hardware peripheral." @@ -21,6 +22,10 @@ ifeq (1,$(EFM32_TRNG)) FEATURES_PROVIDED += periph_hwrng endif +ifeq (1,$(EFM32_SERIES)) + FEATURES_PROVIDED += periph_wdt_cb +endif + ifeq (1,$(EFM32_LEUART_ENABLED)) CFLAGS += -DEFM32_LEUART_ENABLED=1 endif diff --git a/cpu/efm32/families/efm32gg/Kconfig b/cpu/efm32/families/efm32gg/Kconfig index fbe94b2473..6fa14e93ac 100644 --- a/cpu/efm32/families/efm32gg/Kconfig +++ b/cpu/efm32/families/efm32gg/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFM32GG bool select CPU_CORE_CORTEX_M3 select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES0 select HAS_CORTEXM_MPU ## CPU Models diff --git a/cpu/efm32/families/efm32lg/Kconfig b/cpu/efm32/families/efm32lg/Kconfig index ccc70f09f3..baace37b23 100644 --- a/cpu/efm32/families/efm32lg/Kconfig +++ b/cpu/efm32/families/efm32lg/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFM32LG bool select CPU_CORE_CORTEX_M3 select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES0 select HAS_CORTEXM_MPU ## CPU Models diff --git a/cpu/efm32/families/efm32pg12b/Kconfig b/cpu/efm32/families/efm32pg12b/Kconfig index e859a77a50..fe18fee100 100644 --- a/cpu/efm32/families/efm32pg12b/Kconfig +++ b/cpu/efm32/families/efm32pg12b/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFM32PG12B bool select CPU_CORE_CORTEX_M4F select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES1 select HAS_PERIPH_HWRNG select HAS_CORTEXM_MPU diff --git a/cpu/efm32/families/efm32pg1b/Kconfig b/cpu/efm32/families/efm32pg1b/Kconfig index 2ae113c8c3..ee7b5f7f18 100644 --- a/cpu/efm32/families/efm32pg1b/Kconfig +++ b/cpu/efm32/families/efm32pg1b/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFM32PG1B bool select CPU_CORE_CORTEX_M4F select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES1 select HAS_CORTEXM_MPU ## CPU Models diff --git a/cpu/efm32/families/efm32zg/Kconfig b/cpu/efm32/families/efm32zg/Kconfig index 63f65639b3..d7fb299370 100644 --- a/cpu/efm32/families/efm32zg/Kconfig +++ b/cpu/efm32/families/efm32zg/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFM32ZG bool select CPU_CORE_CORTEX_M0PLUS select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES0 ## CPU Models config CPU_MODEL_EFM32ZG222F16 diff --git a/cpu/efm32/families/efr32mg12p/Kconfig b/cpu/efm32/families/efr32mg12p/Kconfig index 4a5cfa8fda..f6c2d5dcec 100644 --- a/cpu/efm32/families/efr32mg12p/Kconfig +++ b/cpu/efm32/families/efr32mg12p/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFR32MG12P bool select CPU_CORE_CORTEX_M4F select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES1 select HAS_PERIPH_HWRNG select HAS_CORTEXM_MPU diff --git a/cpu/efm32/families/efr32mg1p/Kconfig b/cpu/efm32/families/efr32mg1p/Kconfig index 5e56bf677b..34bade8859 100644 --- a/cpu/efm32/families/efr32mg1p/Kconfig +++ b/cpu/efm32/families/efr32mg1p/Kconfig @@ -8,6 +8,7 @@ config CPU_FAM_EFR32MG1P bool select CPU_CORE_CORTEX_M4F select CPU_COMMON_EFM32 + select CPU_EFM32_SERIES1 select HAS_CORTEXM_MPU ## CPU Models diff --git a/cpu/efm32/include/periph_cpu.h b/cpu/efm32/include/periph_cpu.h index b402247806..864a61e5e2 100644 --- a/cpu/efm32/include/periph_cpu.h +++ b/cpu/efm32/include/periph_cpu.h @@ -31,6 +31,7 @@ #include "em_gpio.h" #include "em_timer.h" #include "em_usart.h" +#include "em_wdog.h" #if defined(_SILICON_LABS_32B_SERIES_0) #include "em_dac.h" #endif @@ -437,6 +438,23 @@ typedef struct { */ #define PM_NUM_MODES (2U) +/** + * @name Watchdog timer (WDT) configuration + * @{ + */ +#define WDT_CLOCK_HZ (1000U) + +#define NWDT_TIME_LOWER_LIMIT ((1U << (3U + wdogPeriod_9)) + 1U) +#define NWDT_TIME_UPPER_LIMIT ((1U << (3U + wdogPeriod_256k)) + 1U) + +#ifdef _SILICON_LABS_32B_SERIES_1 +#define WDT_TIME_LOWER_LIMIT NWDT_TIME_LOWER_LIMIT +#define WDT_TIME_UPPER_LIMIT NWDT_TIME_UPPER_LIMIT +#endif + +#define WDT_HAS_STOP (1U) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/efm32/periph/wdt_series0.c b/cpu/efm32/periph/wdt_series0.c new file mode 100644 index 0000000000..8bbf5d2020 --- /dev/null +++ b/cpu/efm32/periph/wdt_series0.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 Bas Stottelaar + * + * 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_efm32 + * @ingroup drivers_periph_wdt + * @{ + * + * @file + * @brief Watchdog timer peripheral driver implementation for + * EFM32 Series 0 MCUs + * + * @author Bas Stottelaar + * @} + */ + +#include +#include + +#include "cpu.h" + +#include "periph/wdt.h" + +#include "em_cmu.h" +#include "em_wdog.h" + +#include "timex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static WDOG_PeriodSel_TypeDef _get_prescaler(uint32_t max_time) +{ + const uint32_t cycles = (max_time * WDT_CLOCK_HZ) / 1000; + + DEBUG("[wdt_series0] _get_prescaler: cycles=%" PRIu32 "\n", cycles); + + return (WDOG_PeriodSel_TypeDef) (32 - __builtin_clz(cycles - 1) - 3); +} + +void wdt_kick(void) +{ + WDOGn_Feed(WDOG); +} + +void wdt_start(void) +{ + WDOGn_Enable(WDOG, true); +} + +void wdt_stop(void) +{ + WDOGn_Enable(WDOG, false); +} + +void wdt_setup_reboot(uint32_t min_time, uint32_t max_time) +{ + /* assert timings */ + assert(min_time == 0); + assert(max_time > NWDT_TIME_LOWER_LIMIT || + max_time < NWDT_TIME_UPPER_LIMIT); + + /* initialize clock */ + CMU_ClockEnable(cmuClock_HFLE, true); + + /* initialize watchdog */ + WDOG_Init_TypeDef init = WDOG_INIT_DEFAULT; + + init.enable = false; + init.clkSel = wdogClkSelULFRCO; + init.perSel = _get_prescaler(max_time); + + DEBUG("[wdt_series0] wdt_setup_reboot: prescaler=%d calculated_time=%" PRIu32 "\n", + (int) init.perSel, + ((1 << (3 + (int)init.perSel)) + 1 ) / WDT_CLOCK_HZ * MS_PER_SEC); + + WDOGn_Init(WDOG, &init); +} diff --git a/cpu/efm32/periph/wdt_series1.c b/cpu/efm32/periph/wdt_series1.c new file mode 100644 index 0000000000..81d6ccc56f --- /dev/null +++ b/cpu/efm32/periph/wdt_series1.c @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2020 Bas Stottelaar + * + * 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_efm32 + * @ingroup drivers_periph_wdt + * @{ + * + * @file + * @brief Watchdog timer peripheral driver implementation for + * EFM32 Series 1 MCUs + * + * @author Bas Stottelaar + * @} + */ + +#include +#include +#include + +#include "cpu.h" + +#include "periph/pm.h" +#include "periph/wdt.h" + +#include "em_cmu.h" +#include "em_wdog.h" + +#include "timex.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#ifdef MODULE_PERIPH_WDT_CB +static wdt_cb_t wdt_cb; +static void *wdt_arg; +#endif + +static uint32_t _get_calculated_time(WDOG_PeriodSel_TypeDef period) +{ + return ((1 << (3 + (int)period)) + 1) / WDT_CLOCK_HZ * MS_PER_SEC; +} + +static WDOG_PeriodSel_TypeDef _get_period(uint32_t max_time) +{ + const uint32_t cycles = (max_time * WDT_CLOCK_HZ) / MS_PER_SEC; + + DEBUG("[wdt_series1] _get_period: cycles=%" PRIu32 "\n", cycles); + + return (WDOG_PeriodSel_TypeDef) (32 - __builtin_clz(cycles - 1) - 3); +} + +static WDOG_WinSel_TypeDef _get_illegal_window(uint32_t min_time, uint32_t calculated_time) +{ + if (min_time == 0) { + return wdogIllegalWindowDisable; + } + + uint32_t slice = calculated_time / 8; + + for (unsigned i = 1; i < 8; i++) { + if (min_time < (i * slice)) { + return (WDOG_WinSel_TypeDef) i; + } + } + + return wdogIllegalWindowDisable; +} + +static void _init(uint32_t min_time, uint32_t max_time, bool warn) +{ +#ifndef MODULE_PERIPH_WDT_CB + (void)warn; +#endif + + /* assert timings */ + if (min_time == 0) { + assert(max_time > NWDT_TIME_LOWER_LIMIT || + max_time < NWDT_TIME_UPPER_LIMIT); + } + else { + assert(max_time > WDT_TIME_LOWER_LIMIT || + max_time < WDT_TIME_UPPER_LIMIT); + } + + /* initialize clock */ + CMU_ClockEnable(cmuClock_HFLE, true); + + /* initialize watchdog */ + WDOG_Init_TypeDef init = WDOG_INIT_DEFAULT; + + init.enable = false; + init.em2Run = true; + init.clkSel = wdogClkSelULFRCO; + init.perSel = _get_period(max_time); + + uint32_t calculated_time = _get_calculated_time(init.perSel); + + init.winSel = _get_illegal_window(min_time, calculated_time); + + DEBUG("[wdt_series1] _init: prescaler=%d, winsel=%d, calculated=%" PRIu32 "\n", + (int) init.perSel, (int) init.winSel, calculated_time); + +#ifdef MODULE_PERIPH_WDT_CB + if (warn) { + init.warnSel = wdogWarnTime50pct; + } +#endif + + WDOGn_Init(WDOG0, &init); + + /* Configure interrupts */ + WDOGn_IntEnable(WDOG0, WDOG_IEN_WIN); + +#ifdef MODULE_PERIPH_WDT_CB + if (warn) { + WDOGn_IntEnable(WDOG0, WDOG_IEN_WARN); + } +#endif + + NVIC_EnableIRQ(WDOG0_IRQn); +} + +void wdt_kick(void) +{ + WDOGn_Feed(WDOG0); +} + +void wdt_start(void) +{ + WDOGn_Enable(WDOG0, true); +} + +void wdt_stop(void) +{ + WDOGn_Enable(WDOG0, false); +} + +void wdt_setup_reboot(uint32_t min_time, uint32_t max_time) +{ + _init(min_time, max_time, false); +} + +#if defined(MODULE_PERIPH_WDT_CB) +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; + + _init(min_time, max_time, true); +} +#endif + +void isr_wdog0(void) +{ + uint32_t flags = WDOGn_IntGet(WDOG0); + + if (flags & WDOG_IEN_WIN) { + WDOGn_IntClear(WDOG0, WDOG_IEN_WIN); + + pm_reboot(); + } + +#if defined(MODULE_PERIPH_WDT_CB) + if (flags & WDOG_IEN_WARN) { + WDOGn_IntClear(WDOG0, WDOG_IEN_WARN); + + if (wdt_cb) { + wdt_cb(wdt_arg); + } + } +#endif + + cortexm_isr_end(); +}