1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/sam0_common/periph/wdt.c
Dylan Laduranty 25a04e6757 cpu/sam0/periph: remove bitfield usage in watchdog driver
Signed-off-by: Dylan Laduranty <dylan.laduranty@mesotic.com>
2024-06-25 16:13:17 +02:00

234 lines
4.8 KiB
C

/*
* Copyright (C) 2019 ML!PA Consulting GmbH
*
* 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_sam0_common
* @ingroup drivers_periph_wdt
* @{
*
* @file wdt.c
* @brief Low-level WDT driver implementation
*
* @author Benjamin Valentin <benjamin.valentin@ml-pa.com>
*
* @}
*/
#include <stdint.h>
#include "periph/wdt.h"
#include "pm_layered.h"
#include "board.h"
#include "debug.h"
#ifndef WDT_CLOCK_HZ
#define WDT_CLOCK_HZ 1024
#endif
/* work around inconsistency in header files */
#ifndef WDT_CONFIG_PER_8_Val
#define WDT_CONFIG_PER_8_Val WDT_CONFIG_PER_CYC8_Val
#endif
#ifndef WDT_CONFIG_PER_8K_Val
#define WDT_CONFIG_PER_8K_Val WDT_CONFIG_PER_CYC8192_Val
#endif
#ifndef WDT_CONFIG_PER_16K_Val
#define WDT_CONFIG_PER_16K_Val WDT_CONFIG_PER_CYC16384_Val
#endif
static inline void _wdt_enable(void)
{
/* work around strange watchdog behaviour if IDLE2 is used on samd21 */
#ifdef CPU_COMMON_SAMD21
pm_block(1);
#endif
#ifdef WDT_CTRLA_ENABLE
WDT->CTRLA.reg |= WDT_CTRLA_ENABLE;
#else
WDT->CTRL.reg |= WDT_CTRL_ENABLE;
#endif
}
static inline void _wdt_disable(void)
{
#ifdef WDT_CTRLA_ENABLE
WDT->CTRLA.reg &= ~WDT_CTRLA_ENABLE;
#else
WDT->CTRL.reg &= ~WDT_CTRL_ENABLE;
#endif
}
static inline void _wait_syncbusy(void)
{
#ifdef WDT_STATUS_SYNCBUSY
while (WDT->STATUS.reg & WDT_STATUS_SYNCBUSY) {}
#else
while (WDT->SYNCBUSY.reg) {}
#endif
}
static uint32_t ms_to_per(uint32_t ms)
{
const uint32_t cycles = (ms * WDT_CLOCK_HZ) / 1024;
/* Minimum WDT period is 8 clock cycles (register value 0) */
if (cycles <= 8) {
return 0;
}
/* Round up to next pow2 and calculate the register value */
return 29 - __builtin_clz(cycles - 1);
}
#ifdef CPU_COMMON_SAMD21
static void _wdt_clock_setup(void)
{
/* Connect to GCLK3 (~1.024 kHz) */
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT
| GCLK_CLKCTRL_GEN(SAM0_GCLK_1KHZ)
| GCLK_CLKCTRL_CLKEN;
}
#else
static void _wdt_clock_setup(void)
{
/* nothing to do here */
}
#endif
void wdt_init(void)
{
_wdt_clock_setup();
#ifdef MCLK
MCLK->APBAMASK.reg |= MCLK_APBAMASK_WDT;
#else
PM->APBAMASK.reg |= PM_APBAMASK_WDT;
#endif
_wdt_disable();
NVIC_EnableIRQ(WDT_IRQn);
}
void wdt_setup_reboot(uint32_t min_time, uint32_t max_time)
{
uint32_t per, win;
if (max_time == 0) {
DEBUG("invalid period: max_time = %"PRIu32"\n", max_time);
return;
}
per = ms_to_per(max_time);
if (per > WDT_CONFIG_PER_16K_Val) {
DEBUG("invalid period: max_time = %"PRIu32"\n", max_time);
return;
}
if (min_time) {
win = ms_to_per(min_time);
if (win > WDT_CONFIG_PER_8K_Val) {
DEBUG("invalid period: min_time = %"PRIu32"\n", min_time);
return;
}
if (per < win) {
per = win + 1;
}
#ifdef WDT_CTRLA_WEN
WDT->CTRLA.reg |= WDT_CTRLA_WEN;
#else
WDT->CTRL.reg |= WDT_CTRL_WEN;
#endif
} else {
win = 0;
#ifdef WDT_CTRLA_WEN
WDT->CTRLA.reg &= ~WDT_CTRLA_WEN;
#else
WDT->CTRL.reg &= ~WDT_CTRL_WEN;
#endif
}
WDT->INTFLAG.reg = WDT_INTFLAG_EW;
DEBUG("watchdog window: %"PRIu32", period: %"PRIu32"\n", win, per);
WDT->CONFIG.reg = WDT_CONFIG_WINDOW(win) | WDT_CONFIG_PER(per);
_wait_syncbusy();
}
void wdt_stop(void)
{
_wdt_disable();
_wait_syncbusy();
}
void wdt_start(void)
{
_wdt_enable();
_wait_syncbusy();
}
void wdt_kick(void)
{
WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY_Val;
}
#ifdef MODULE_PERIPH_WDT_CB
static wdt_cb_t cb;
static void* cb_arg;
void wdt_setup_reboot_with_callback(uint32_t min_time, uint32_t max_time,
wdt_cb_t wdt_cb, void *arg)
{
uint32_t per = ms_to_per(max_time);
if (per == WDT_CONFIG_PER_8_Val && wdt_cb) {
DEBUG("period too short for early warning\n");
return;
}
cb = wdt_cb;
cb_arg = arg;
if (cb != NULL) {
uint32_t warning_offset = ms_to_per(CONFIG_WDT_WARNING_PERIOD);
if (warning_offset == 0) {
warning_offset = 1;
}
if (warning_offset >= per) {
warning_offset = per - 1;
}
WDT->INTENSET.reg = WDT_INTENSET_EW;
uint32_t reg = WDT->EWCTRL.reg;
WDT->EWCTRL.reg = (reg & ~WDT_EWCTRL_EWOFFSET_Msk)
| WDT_EWCTRL_EWOFFSET(per - warning_offset);
} else {
WDT->INTENCLR.reg = WDT_INTENCLR_EW;
}
wdt_setup_reboot(min_time, max_time);
}
void isr_wdt(void)
{
WDT->INTFLAG.reg = WDT_INTFLAG_EW;
if (cb != NULL) {
cb(cb_arg);
}
cortexm_isr_end();
}
#endif /* MODULE_PERIPH_WDT_CB */