Newer
Older
mbed-os / targets / TARGET_Freescale / TARGET_MCUXpresso_MCUS / TARGET_MCU_K64F / watchdog_api.c
@Harrison Mutai Harrison Mutai on 15 Oct 2020 3 KB Add SPDX license identifier to Arm files
/* 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