/* mbed Microcontroller Library * Copyright (c) 2017 ARM Limited * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "watchdog_api.h" #if DEVICE_WATCHDOG #include "reset_reason_api.h" #include "fsl_wdog.h" #include "fsl_clock.h" #include "platform/mbed_wait_api.h" // Platform specific watchdog definitions #define LPO_CLOCK_FREQUENCY 1000 #define MAX_PRESCALER 8 #define MAX_TIMEOUT 0xFFFFFFFFUL #define WCT_IN_BUS_CYCLES 256U // Watchdog configuration time (WCT) in bus clock cycles. // Number of decrements in the timeout register per millisecond #define TICKS_PER_MS ((LPO_CLOCK_FREQUENCY) / 1000) // Maximum timeout that can be specified in milliseconds #define MAX_TIMEOUT_MS_UINT64 (1ULL * ((MAX_TIMEOUT) / (TICKS_PER_MS)) * (MAX_PRESCALER)) #if (MAX_TIMEOUT_MS_UINT64 > UINT32_MAX) #define MAX_TIMEOUT_MS UINT32_MAX #else #define MAX_TIMEOUT_MS (MAX_TIMEOUT_MS_UINT64 & 0xFFFFFFFFUL) #endif // Maximum supported watchdog timeout for given prescaler value #define CALCULATE_MAX_TIMEOUT_MS_UINT64(scale) \ (1ULL * ((MAX_TIMEOUT) / (TICKS_PER_MS)) * (scale)) static uint32_t calculate_prescaler_value(const uint32_t timeout_ms) { if (timeout_ms > MAX_TIMEOUT_MS) { return 0; } for (uint32_t scale = 1; scale <= MAX_PRESCALER; ++scale) { if (timeout_ms <= CALCULATE_MAX_TIMEOUT_MS_UINT64(scale)) { return scale; } } return 0; } // Wait until watchdog configuration time window closes. static inline void wait_WCT(void) { uint32_t WCT_us = (WCT_IN_BUS_CYCLES) * 1000000UL / CLOCK_GetBusClkFreq(); wait_us(WCT_us); } watchdog_status_t hal_watchdog_init(const watchdog_config_t *config) { wdog_config_t cfg; cfg.enableWdog = true; cfg.clockSource = kWDOG_LpoClockSource; cfg.windowValue = 0; cfg.enableUpdate = true; cfg.enableInterrupt = false; cfg.enableWindowMode = false; cfg.workMode.enableWait = true; cfg.workMode.enableStop = true; cfg.workMode.enableDebug = true; const uint32_t prescaler = calculate_prescaler_value(config->timeout_ms); if (prescaler == 0) { return WATCHDOG_STATUS_INVALID_ARGUMENT; } cfg.prescaler = (wdog_clock_prescaler_t)(prescaler - 1); cfg.timeoutValue = (TICKS_PER_MS * config->timeout_ms) / prescaler; WDOG_Init(WDOG, &cfg); wait_WCT(); // Updates in the write-once registers take effect only after the WCT window closes. return WATCHDOG_STATUS_OK; } void hal_watchdog_kick(void) { WDOG_Refresh(WDOG); } watchdog_status_t hal_watchdog_stop(void) { WDOG_Deinit(WDOG); wait_WCT(); // Updates in the write-once registers take effect only after the WCT window closes. return WATCHDOG_STATUS_OK; } uint32_t hal_watchdog_get_reload_value(void) { const uint32_t timeout = (((WDOG->TOVALH & 0xFFFFU) << 16U) | (WDOG->TOVALL & 0xFFFFU)); const uint32_t prescaler = WDOG_PRESC_PRESCVAL(WDOG->PRESC); return ((timeout / TICKS_PER_MS) * (prescaler + 1)); } watchdog_features_t hal_watchdog_get_platform_features(void) { watchdog_features_t features; features.max_timeout = MAX_TIMEOUT_MS; features.update_config = true; features.disable_watchdog = true; features.clock_typical_frequency = 1000; features.clock_max_frequency = 1111; return features; } #endif // DEVICE_WATCHDOG