/* Copyright 2012 The Chromium OS Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ /* Watchdog driver */ #include "common.h" #include "gpio.h" #include "hooks.h" #include "hwtimer.h" #include "registers.h" #include "task.h" #include "timer.h" #include "util.h" #include "watchdog.h" /* * LSI oscillator frequency is typically 38 kHz, but it may be between 28-56 * kHz and we don't calibrate it to know. Use 56 kHz so that we pick a counter * value large enough that we reload before the worst-case watchdog delay * (fastest LSI clock). */ #ifdef CHIP_FAMILY_STM32L4 #define LSI_CLOCK 34000 #else #define LSI_CLOCK 56000 #endif /* The timeout value is multiplied by 1000 to be converted into ms */ #define HAL_IWDG_DEFAULT_TIMEOUT ((6UL * 256UL * 1000UL) / LSI_CLOCK) /* * Use largest prescaler divider = /256. This gives a worst-case watchdog * clock of 56000/256 = 218 Hz, and a maximum timeout period of (4095/218 Hz) = * 18.7 sec. * * For STM32L4, Max LSI is 34000. Watchdog clock is 34000 / 256 = 132Hz, * Max timeout = 4095 / 132 = 31 sec. */ #define IWDG_PRESCALER 6 #define IWDG_PRESCALER_DIV (4 << IWDG_PRESCALER) void watchdog_reload(void) { /* Reload the watchdog */ STM32_IWDG_KR = STM32_IWDG_KR_RELOAD; #ifdef CONFIG_WATCHDOG_HELP hwtimer_reset_watchdog(); #endif } DECLARE_HOOK(HOOK_TICK, watchdog_reload, HOOK_PRIO_DEFAULT); int watchdog_init(void) { #ifdef CHIP_FAMILY_STM32L4 timestamp_t tickstart, ticknow; /* Enable watchdog registers */ STM32_IWDG_KR = STM32_IWDG_KR_START; #endif /* Unlock watchdog registers */ STM32_IWDG_KR = STM32_IWDG_KR_UNLOCK; /* Set the prescaler between the LSI clock and the watchdog counter */ STM32_IWDG_PR = IWDG_PRESCALER & 7; /* Set the reload value of the watchdog counter */ STM32_IWDG_RLR = MIN(STM32_IWDG_RLR_MAX, CONFIG_WATCHDOG_PERIOD_MS * (LSI_CLOCK / IWDG_PRESCALER_DIV) / 1000); #ifdef CHIP_FAMILY_STM32L4 tickstart = get_time(); /* Wait for SR */ while (STM32_IWDG_SR != 0x00u) { ticknow = get_time(); if ((ticknow.val - tickstart.val) > HAL_IWDG_DEFAULT_TIMEOUT * 1000) { return EC_ERROR_TIMEOUT; } } /* Reload the watchdog */ STM32_IWDG_KR = STM32_IWDG_KR_RELOAD; #else /* Start the watchdog (and re-lock registers) */ STM32_IWDG_KR = STM32_IWDG_KR_START; /* * We should really wait for IWDG_PR and IWDG_RLR value to be updated * but updating those registers can take about 48ms (found * empirically, it's 6 LSI cycles at 32kHz). Such a big delay is not * desired during system init. * * However documentation allows us to continue code execution, but * we should wait for RVU bit to be clear before updating IWDG_RLR * once again (hard reboot for STM32H7 and STM32F4). * * RM0433 Rev 7 * Section 45.4.4 Page 1920 * https://www.st.com/resource/en/reference_manual/dm00314099.pdf * If several reload, prescaler, or window values are used by the * application, it is mandatory to wait until RVU bit is reset before * changing the reload value, to wait until PVU bit is reset before * changing the prescaler value, and to wait until WVU bit is reset * before changing the window value. However, after updating the * prescaler and/or the reload/window value it is not necessary to wait * until RVU or PVU or WVU is reset before continuing code execution * except in case of low-power mode entry. */ #endif #ifdef CONFIG_WATCHDOG_HELP /* Use a harder timer to warn about an impending watchdog reset */ hwtimer_setup_watchdog(); #endif return EC_SUCCESS; }