/**
  **********************************************************************************************************************
  * @file    stm32c5xx_hal_wwdg.c
  * @brief   WWDG HAL module driver.
  *          This file provides firmware functions to manage the following
  *          functionalities of the Window Watchdog (WWDG) peripheral:
  *           + Initialization and configuration functions
  *           + I/O operation functions
  *
  **********************************************************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  **********************************************************************************************************************
  */

/* Includes ----------------------------------------------------------------------------------------------------------*/
#include "stm32_hal.h"

/** @addtogroup STM32C5xx_HAL_Driver
  * @{
  */
#if defined (WWDG)
#if defined(USE_HAL_WWDG_MODULE) && (USE_HAL_WWDG_MODULE == 1)

/** @addtogroup WWDG
  * @{
  */
/** @defgroup WWDG_Introduction WWDG Introduction
  * @{

  The WWDG hardware abstraction layer provides a set of APIs to interface with the WWDG peripheral
  on STM32 microcontrollers.

  The system window watchdog (WWDG) is used to detect the occurrence of a software fault, usually generated by
  external interference or by unforeseen logical conditions, which causes the application program to abandon
  its normal sequence.

  The WWDG clock is prescaled from the APB clock and has a configurable time-window that can be programmed to detect
  abnormally late or early application behavior.\n

  An Early Wakeup Interrupt can be generated before a reset happens to perform a system recovery or manage certain
  actions before a system restart.

  Once the WWDG is enabled, it can only be disabled by a device reset.

  The WWDG is best suited for applications requiring the watchdog to react within an accurate timing window.

  This abstraction layer guarantees portability and ease of use across different STM32 series.
  */
/**
  * @}
  */

/** @defgroup WWDG_How_To_Use WWDG How To Use
  * @{

## Main features
  - The WWDG can be started by either software or hardware (configurable through option byte).

  - Once enabled, the WWDG generates a system reset on expiry of a programmed time period, unless the program refreshes
    the counter (T[6;0] downcounter) before reaching 0x3F value (i.e. a reset is generated when the counter
    value rolls down from 0x40 to 0x3F).

  - An MCU reset is also generated if the counter value is refreshed before the counter has reached the refresh window
    value. This implies that the counter is refreshed within a limited window.

  - Once enabled, the WWDG cannot be disabled except by a system reset.

  - If required by the application, an Early Wakeup Interrupt can be triggered to provide warning before WWDG
    expiration.
    The Early Wakeup Interrupt (EWI) can be used if specific safety operations or data logging must be performed
    before the actual reset is generated. When the downcounter reaches 0x40, an interrupt occurs.
    This mechanism requires the WWDG interrupt line to be enabled in NVIC. Once enabled, the EWI interrupt cannot be
    disabled except by a system reset.

  - The WWDG counter input clock is derived from the APB clock divided by a programmable prescaler.

  - WWDG clock (Hz) = PCLK / (4096 * Prescaler)

  - WWDG timeout (ms) = 1000 * (T[5;0] + 1) / WWDG clock (Hz), where T[5;0] are the lowest 6 bits of the counter.

  - WWDG counter refresh is allowed between the following limits:
    - min time (ms) = 1000 * (Counter - Window) / WWDG clock  (The minimum time represents the earliest time before
                                   refresh is allowed)
    - max time (ms) = 1000 * (Counter - 0x40) / WWDG clock    (The maximum time represents the latest time before reset)

  - Typical values:
    - Counter min (T[5;0] = 0x00) at 56MHz (PCLK) with one prescaler:
      - WWDG step: approximately 73.14us    (The WWDG step represents the WWDG counter period)
      - max timeout before reset: 4.681ms

    - Counter max (T[5;0] = 0x3F) at 56MHz (PCLK) with prescaler dividing by 128:
      - max timeout before reset: approximately 599.18ms

## How to use
  Use the WWDG HAL driver as follows:
  - Enable the WWDG interface clock if USE_HAL_WWDG_CLK_ENABLE_MODEL = HAL_CLK_ENABLE_NO. Otherwise, it is enabled
    in HAL_WWDG_Init().

  - Configure the allowed refresh period (minimum and maximum time values) and early interrupt status using
    HAL_WWDG_Start(). The WWDG is automatically enabled and its downcounter is started.

      - HAL_WWDG_Start() computes and initializes prescaler, reload and window registers to values corresponding
        to the nearest achievable minimum and maximum time inputs.

      - Use HAL_WWDG_GetMaxTime() and HAL_WWDG_GetMinTime() to retrieve the times actually set.
        Use HAL_WWDG_GetStep_us() and HAL_WWDG_SetMinTime() to tune the refresh time.

      - Provide a maximum time value greater than 0 to prevent immediate reset generation.

      - If the Early Wakeup Interrupt (EWI) feature is enabled, an interrupt is generated when the counter reaches
        0x40. When HAL_WWDG_IRQHandler() is triggered by the interrupt service routine, the Early Wakeup flag is
        automatically cleared and the HAL_WWDG_EarlyWakeupCallback() callback is executed. Add custom code by
        implementing the HAL_WWDG_EarlyWakeupCallback() callback.

  - Refresh the WWDG counter at regular intervals during normal operation to prevent an MCU reset by calling
    HAL_WWDG_Refresh(). Perform this operation only when the counter is lower than the refresh window value already
    programmed.

### Callback registration:
  - Use the compilation flag USE_HAL_WWDG_REGISTER_CALLBACKS to configure the driver callbacks dynamically.

  - Use HAL_WWDG_RegisterEarlyWakeupCallback() to register the WWDG Early Wakeup callback.\n

  - This function takes the HAL peripheral handle and a pointer to the callback function as parameters.

  - When calling HAL_WWDG_Init(), callbacks are reset to the corresponding legacy weak function,
    HAL_WWDG_EarlyWakeupCallback(), only if it has not been registered before.

  - When the compilation define USE_HAL_WWDG_REGISTER_CALLBACKS is set to 0 or not defined, the callback registration
    feature is not available and weak callbacks are used.

## Allowed Maximum time ranges:
  - Select the prescaler as follows: as long as the requested maximum time value is lower than the
    maximum time of a time range n, the algorithm keeps the same prescaler n. Once it exceeds the maximum time of
    range n, the algorithm switches to the prescaler of the range n+1.\n

  - The next table describes the possible maximum time ranges for each prescaler at a high frequency (144 MHz):\n

Note:\n
  - The theoretical floating-point values presented in the following tables are rounded to the nearest integer values
    as only integers are used as arguments and return values for WWDG APIs.

  - For a "Not supported" value in seconds, switch to the milliseconds or microseconds unit.
    Similarly, for "Not supported" values in microseconds, switch to the milliseconds or seconds unit.\n

  - To cover all ranges, a static time unit configuration is provided
  and can be expressed in microseconds, milliseconds, or seconds.
  Prescaler  |   Step(us) |   Max(us)   |   Max(ms)  |   Max(s)
  -----------|------------|-------------|------------|----------------
    1        |   28.44    |   1820.44   |   1.82     |   Not supported
    2        |   56.88    |   3640.89   |   3.64     |   Not supported
    4        |   113.77   |   7281.78   |   7.28     |   Not supported
    8        |   227.55   |   14563.6   |   14.56    |   Not supported
    16       |   455.11   |   29127.1   |   29.12    |   Not supported
    32       |   910.22   |   58254.2   |   58.25    |   Not supported
    64       |   1820.44  |   116508    |   116.5    |   Not supported
    128      |   3640.89  |   233017    |   233.01   |   Not supported

  And the possible maximum time ranges at a low frequency (17kHz), are described in next table:

  Prescaler  |   Step(us)  |   Max(us)     |   Max(ms)   |   Max(s)
  -----------|-------------|---------------|-------------|----------
    1        |  240941.17  | 15420235.29   |  15420.23   |  15.42
    2        |  481882.35  | 30840470.59   |  30840.47   |  30.84
    4        |  963764.7   | 61680941.18   |  61680.94   |  61.68
    8        |  1927529.41 | 123361882.4   |  123361.88  |  123.36
    16       |  3855058.82 | 246723764.7   |  246723.76  |  246.72
    32       |  7710117.65 | 493447529.4   |  493447.52  |  493.44
    64       |  15420235.3 | 986895058.8   |  986895.05  |  986.89
    128      |  30840470.6 | 1973790118    |  1973790.11 |  1973.79
  */
/**
  * @}
  */

/** @defgroup WWDG_Configuration_Table WWDG Configuration Table
  * @{
## Configuration inside the WWDG driver:

 |Config defines                 |Where           |Default value        |Note                                          |
 |-------------------------------|----------------|---------------------|----------------------------------------------|
 |USE_HAL_WWDG_MODULE            |hal_conf.h      |     1               |Enable the HAL WWDG module.                   |
 |USE_HAL_WWDG_REGISTER_CALLBACKS|hal_conf.h      |     0               |Enable the register callbacks assert          |
 |USE_HAL_CHECK_PARAM            |hal_conf.h      |     0               |Enable checking of vital parameters at runtime|
 |USE_HAL_WWDG_HARDWARE_START    |hal_conf.h      |     0               |WWDG driver starts in HW mode                 |
 |USE_HAL_WWDG_CLK_ENABLE_MODEL  |hal_conf.h      |HAL_CLK_ENABLE_NO    |Clock activation                              |
 |USE_HAL_WWDG_TIME_UNIT (*)     |hal_conf.h      |HAL_WWDG_TIME_UNIT_MS|Time unit to be used for WWDG driver          |
 |USE_HAL_WWDG_USER_DATA         |hal_conf.h      |     0               |Enable the set/get user data                  |
 |USE_ASSERT_DBG_PARAM           |PreProcessor env|     None            |Enable the params assert                      |
 |USE_ASSERT_DBG_STATE           |PreProcessor env|     None            |Enable the state assert                       |

(*) Select the time unit value with the USE_HAL_WWDG_TIME_UNIT define:

- HAL_WWDG_TIME_UNIT_US: WWDG driver time unit in microseconds.
- HAL_WWDG_TIME_UNIT_MS: WWDG driver time unit in milliseconds.
- HAL_WWDG_TIME_UNIT_S:  WWDG driver time unit in seconds.<br>

The default time unit is milliseconds if not set.\n\n
  */
/**
  * @}
  */

/* Private constants -------------------------------------------------------------------------------------------------*/
/** @defgroup WWDG_Private_Constants WWDG Private Constants
  * @{
  */
#define WWDG_INTERNAL_DIVIDER        4096U          /*!< WWDG Internal Divider                */
#define WWDG_MAX_STEP_NUMBER         0x40U          /*!< WWDG Max step number                 */
#define WWDG_TIME_CONVERSION         1000U          /*!< Time units conversion factor         */
#define WWDG_MAX_PRESCALER           128U           /*!< WWDG Max Prescaler                   */
/**
  * @}
  */

/* Private macros ----------------------------------------------------------------------------------------------------*/
/** @defgroup WWDG_Private_Macros WWDG Private Macros
  * @{
  */

/**
  * @brief  Retrieve the WWDG instance.
  * @param  handle  WWDG handle.
  */
#define WWDG_GET_INSTANCE(handle)  ((WWDG_TypeDef *)((uint32_t)(handle)->instance))

/**
  * @brief  WWDG maximum time for maximum prescaler and Time unit in microseconds, milliseconds and seconds.
  * @param  freq  PCLK frequency.
  */
#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)

#define WWDG_ALLOWED_MAX_TIME(freq) \
  (((freq) < 10000000U) ? \
   ((WWDG_INTERNAL_DIVIDER * WWDG_MAX_PRESCALER * WWDG_MAX_STEP_NUMBER * 100U) / ((freq) / 100U) * 100U) : \
   ((WWDG_INTERNAL_DIVIDER * WWDG_MAX_PRESCALER * WWDG_MAX_STEP_NUMBER * 100U) / ((freq) / 1000U) * 10U))

#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)

#define WWDG_ALLOWED_MAX_TIME(freq) \
  ((WWDG_INTERNAL_DIVIDER * WWDG_MAX_PRESCALER * WWDG_MAX_STEP_NUMBER * 100U) / ((freq) / 10U))

#else /* USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S */

#define WWDG_ALLOWED_MAX_TIME(freq) \
  ((WWDG_INTERNAL_DIVIDER * WWDG_MAX_PRESCALER * WWDG_MAX_STEP_NUMBER) / (freq))

#endif /* USE_HAL_WWDG_TIME_UNIT */


/**
  * @brief  Check WWDG maximum time value.
  * @param  max_time  Maximum value time before a WWDG reset.
  * @param  freq      PCLK frequency.
  * @warning  max_time must not exceed WWDG_ALLOWED_MAX_TIME based on the frequency and time unit values
  *           selected in configuration.
  */
#define IS_WWDG_MAX_TIME(max_time, freq) ((max_time) <= (WWDG_ALLOWED_MAX_TIME(freq)))

/**
  * @brief  Check WWDG minimum time value.
  * @param  min_time  Minimum value time before refreshing is allowed.
  * @param  max_time  Maximum value time before a WWDG reset.
  */
#define IS_WWDG_MIN_TIME(min_time, max_time) ((min_time) <= (max_time))

/**
  * @brief  Check WWDG early wakeup enable value.
  * @param  ewi_status  WWDG early wakeup interrupt status.
  */
#define IS_WWDG_EWI_STATUS(ewi_status) ((ewi_status) == 1U) || ((ewi_status) == 0U)
/**
  * @}
  */

/* Private variables -------------------------------------------------------------------------------------------------*/
/* Private function prototypes ---------------------------------------------------------------------------------------*/
/** @defgroup WWDG_Private_Functions WWDG Private Functions
  * @{
  */
static uint8_t  WWDG_CalculatePrescaler(const hal_wwdg_handle_t *hwwdg, uint32_t max_time);
static uint16_t WWDG_CalculateReload(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint32_t max_time);
static uint16_t WWDG_CalculateWindow(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint32_t min_time);
static uint32_t WWDG_CalculateMaxTime(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler);
static uint32_t WWDG_CalculateMinTime(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint16_t window);
/**
  * @}
  */

/* Exported functions ------------------------------------------------------------------------------------------------*/
/** @addtogroup WWDG_Exported_Functions
  * @{
  */

/** @addtogroup WWDG_Exported_Functions_Group1 Initialization and Start functions
  * @{
This subsection provides a set of functions to initialize and start the WWDG peripheral:
  - Call the function HAL_WWDG_Init() to initialize the WWDG handle and associate an instance.
  - Call the function HAL_WWDG_Start() to start the WWDG with the provided parameters.
  */

/**
  * @brief   Initialize the WWDG according to the associated handle.
  * @param   hwwdg    Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                   the specified WWDG module.
  * @param   instance WWDG instance.
  *
  * @warning In case of starting WWDG in Hardware mode, make sure that USE_HAL_WWDG_HARDWARE_START is aligned with
  *          the WWDG_SW option byte.
  *
  * @retval  HAL_OK             HAL operation completed successfully.
  * @retval  HAL_INVALID_PARAM  HAL invalid parameter.
  */
hal_status_t HAL_WWDG_Init(hal_wwdg_handle_t *hwwdg, hal_wwdg_t instance)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_PARAM(IS_WWDG_ALL_INSTANCE((WWDG_TypeDef *)(uint32_t)instance));

#if defined(USE_HAL_CHECK_PARAM) && (USE_HAL_CHECK_PARAM == 1U)
  if (hwwdg == NULL)
  {
    return HAL_INVALID_PARAM;
  }
#endif /* USE_HAL_CHECK_PARAM */

  hwwdg->instance = instance;

#if defined(USE_HAL_WWDG_REGISTER_CALLBACKS) && (USE_HAL_WWDG_REGISTER_CALLBACKS == 1U)
  hwwdg->p_early_wakeup_cb = HAL_WWDG_EarlyWakeupCallback;
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */

#if defined (USE_HAL_WWDG_USER_DATA) && (USE_HAL_WWDG_USER_DATA == 1U)
  hwwdg->p_user_data = NULL;
#endif /* USE_HAL_WWDG_USER_DATA */

#if !defined(USE_HAL_WWDG_HARDWARE_START) || (USE_HAL_WWDG_HARDWARE_START != 1UL)
  if (LL_WWDG_IsEnabled(WWDG_GET_INSTANCE(hwwdg)) == 0U)
  {
#if defined(USE_HAL_WWDG_CLK_ENABLE_MODEL) && (USE_HAL_WWDG_CLK_ENABLE_MODEL >= HAL_CLK_ENABLE_PERIPH_ONLY)
    HAL_RCC_WWDG_EnableClock();
#endif /* USE_HAL_WWDG_CLK_ENABLE_MODEL */

    hwwdg->global_state = HAL_WWDG_STATE_IDLE;
  }
  else
#endif /* !USE_HAL_WWDG_HARDWARE_START */
  {
    hwwdg->global_state = HAL_WWDG_STATE_ACTIVE;
  }

  return HAL_OK;
}

/**
  * @brief   Start the WWDG. Before exiting the function, the watchdog counter is refreshed to have a correct time base.
  * @param   hwwdg      Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                     the specified WWDG module.
  * @param   min_time   Minimum time value before refreshing is allowed.
  * @param   max_time   Maximum time value before a WWDG reset.
  * @param   ewi_status Early Wakeup Interrupt activation.
  *
  * @note    The default time unit is milliseconds if not specified through USE_HAL_WWDG_TIME_UNIT define.
  *
  * @warning The max_time and min_time are used to define the reload and the window values, the unit for these
  *          parameters depends on the switch USE_HAL_WWDG_TIME_UNIT.
  * @warning Set ewi_status to zero when the Early Wakeup Interrupt feature is not used.
  *
  * @retval  HAL_OK  HAL operation completed successfully.
  */
hal_status_t HAL_WWDG_Start(hal_wwdg_handle_t *hwwdg, uint32_t min_time, uint32_t max_time, uint32_t ewi_status)
{
  uint32_t frequency;
  uint16_t window;
  uint8_t  prescaler;

  ASSERT_DBG_PARAM(hwwdg != NULL);

  frequency = HAL_RCC_GetPCLK1Freq();
  hwwdg->pclk_frequency_hz = frequency;

  ASSERT_DBG_PARAM(IS_WWDG_MAX_TIME(max_time, frequency));
  ASSERT_DBG_PARAM(IS_WWDG_MIN_TIME(min_time, max_time));
  ASSERT_DBG_PARAM(IS_WWDG_EWI_STATUS(ewi_status));

#if defined(USE_HAL_WWDG_HARDWARE_START) && (USE_HAL_WWDG_HARDWARE_START == 1U)
  ASSERT_DBG_STATE(hwwdg->global_state, (uint32_t)HAL_WWDG_STATE_ACTIVE);

#else
  ASSERT_DBG_STATE(hwwdg->global_state, (uint32_t)HAL_WWDG_STATE_IDLE | (uint32_t)HAL_WWDG_STATE_ACTIVE);

  if (LL_WWDG_IsEnabled(WWDG_GET_INSTANCE(hwwdg)) == 0U)
  {
    HAL_CHECK_UPDATE_STATE(hwwdg, global_state, (uint32_t)HAL_WWDG_STATE_IDLE, HAL_WWDG_STATE_ACTIVE);
  }

#endif /* USE_HAL_WWDG_HARDWARE_START */

  prescaler = WWDG_CalculatePrescaler(hwwdg, max_time);

  hwwdg->reload = WWDG_CalculateReload(hwwdg, prescaler, max_time);

  window = WWDG_CalculateWindow(hwwdg, prescaler, min_time);

  if (ewi_status != 0U)
  {
    LL_WWDG_EnableIT_EWKUP(WWDG_GET_INSTANCE(hwwdg));
  }

  /* Enable the WWDG driver and set the reload value to work with */
  LL_WWDG_SetControl(WWDG_GET_INSTANCE(hwwdg), hwwdg->reload << WWDG_CR_T_Pos);

  /* Write to WWDG CFR register the Prescaler and Window values to work with */
  LL_WWDG_SetConfig(WWDG_GET_INSTANCE(hwwdg), ((uint32_t)prescaler << WWDG_CFR_WDGTB_Pos),
                    ((uint32_t)window << WWDG_CFR_W_Pos));

  return HAL_OK;
}
/**
  * @}
  */

/** @addtogroup WWDG_Exported_Functions_Group2 I/O operation function
  * @{
This subsection provides a function to manage the refresh of the WWDG counter:
  - Call the function HAL_WWDG_Refresh() to reload WWDG counter with value defined in the reload register.
  */

/**
  * @brief   Refresh the WWDG counter.
  * @param   hwwdg   Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                  the specified WWDG module.
  * @retval  HAL_OK  HAL operation completed successfully.
  */
hal_status_t HAL_WWDG_Refresh(hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_STATE(hwwdg->global_state, HAL_WWDG_STATE_ACTIVE);

  LL_WWDG_SetCounter(WWDG_GET_INSTANCE(hwwdg), hwwdg->reload << WWDG_CR_T_Pos);

  return HAL_OK;
}
/**
  * @}
  */

/** @addtogroup WWDG_Exported_Functions_Group3 State function
  * @{
This subsection provides a function to retrieve the state value:
  - Call the function HAL_WWDG_GetState() to retrieve the WWDG handle state.
  */

/**
  * @brief  Return the WWDG handle state.
  * @param  hwwdg  Pointer to a hal_wwdg_handle_t structure that contains
  *                the configuration information for the specified WWDG module.
  * @retval HAL_WWDG_STATE_RESET  WWDG driver not initialized and not started.
  * @retval HAL_WWDG_STATE_IDLE   WWDG driver initialized and not started.
  * @retval HAL_WWDG_STATE_ACTIVE WWDG driver initialized and started.
  */
hal_wwdg_state_t HAL_WWDG_GetState(const hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);

  return hwwdg->global_state;
}
/**
  * @}
  */

/** @addtogroup WWDG_Exported_Functions_Group4 Set/Get item functions
  * @{
This subsection provides a set of functions to set and retrieve configuration items separately for the WWDG driver:
  - Call the function HAL_WWDG_GetMaxTime() to retrieve the current maximum time value.
  - Call the function HAL_WWDG_GetStep_us() to retrieve the current counter period.
  - Call the function HAL_WWDG_SetMinTime() to set only the Window time value.
  - Call the function HAL_WWDG_GetMinTime() to retrieve the current Window time value.

@note The prescaler is calculated from maximum time once in the HAL_WWDG_Start() function. To avoid
  a change of prescaler, no function is provided to set the maximum time because it can modify the prescaler
  and thus require recalculating the Window.
  To modify the maximum time, call HAL_WWDG_Start().
  */

/**
  * @brief   Get the maximum time value according to the handle registers.
  * @param   hwwdg    Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                   the specified WWDG module.
  * @retval uint32_t  Current maximum time value.
  */
uint32_t HAL_WWDG_GetMaxTime(const hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_STATE(hwwdg->global_state, HAL_WWDG_STATE_ACTIVE);

  return WWDG_CalculateMaxTime(hwwdg, (uint8_t)(LL_WWDG_GetPrescaler(WWDG_GET_INSTANCE(hwwdg)) >> WWDG_CFR_WDGTB_Pos));
}

/**
  * @brief  Get the WWDG counter period in microseconds.
  * @param  hwwdg  Pointer to a hal_wwdg_handle_t structure that contains
  *                the configuration information for the specified WWDG module.
  *
  * @note   HAL_WWDG_GetStep_us is provided to allow calculation of max_time and min_time
  *                           to avoid rounded values.
  *
  * @retval uint32_t  Current counter period value in us.
  */
uint32_t HAL_WWDG_GetStep_us(const hal_wwdg_handle_t *hwwdg)
{
  uint64_t freq_khz;
  uint64_t clk_divider;
  uint64_t computed_step = 0U;

  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_STATE(hwwdg->global_state, HAL_WWDG_STATE_ACTIVE);

  clk_divider = (((uint64_t)1) << (LL_WWDG_GetPrescaler(WWDG_GET_INSTANCE(hwwdg)) >> WWDG_CFR_WDGTB_Pos)) *
                WWDG_INTERNAL_DIVIDER;

  freq_khz = (uint64_t)hwwdg->pclk_frequency_hz / 1000U;

  if (freq_khz > 0U)
  {
    computed_step = (clk_divider * 1000U) / freq_khz;

    if (((clk_divider * 1000U) % freq_khz) >= (freq_khz / (uint64_t)2))
    {
      computed_step += 1U;
    }
  }

  return (uint32_t)computed_step;
}

/**
  * @brief   Set the Window time value.
  * @param   hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                    the specified WWDG module.
  * @param   min_time  Minimum time value before refreshing is allowed.
  *
  * @warning Refresh the WWDG counter before calling this function.
  *
  * @retval HAL_OK  HAL operation completed successfully.
  */
hal_status_t HAL_WWDG_SetMinTime(hal_wwdg_handle_t *hwwdg, uint32_t min_time)
{
  uint8_t prescaler;
  uint16_t window;

  ASSERT_DBG_PARAM(hwwdg != NULL);

  prescaler = (uint8_t)(LL_WWDG_GetPrescaler(WWDG_GET_INSTANCE(hwwdg)) >> WWDG_CFR_WDGTB_Pos);

  ASSERT_DBG_PARAM(IS_WWDG_MIN_TIME(min_time, WWDG_CalculateMaxTime(hwwdg, prescaler)));
  ASSERT_DBG_STATE(hwwdg->global_state, HAL_WWDG_STATE_ACTIVE);

  window = WWDG_CalculateWindow(hwwdg, prescaler, min_time);

  LL_WWDG_SetWindow(WWDG_GET_INSTANCE(hwwdg), (uint32_t)window << WWDG_CFR_W_Pos);

  return HAL_OK;
}

/**
  * @brief  Get the Window time value according to the handle registers.
  * @param  hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                   the specified WWDG module.
  * @retval uint32_t  Current Window time value.
  */
uint32_t HAL_WWDG_GetMinTime(const hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_STATE(hwwdg->global_state, HAL_WWDG_STATE_ACTIVE);

  return WWDG_CalculateMinTime(hwwdg, (uint8_t)(LL_WWDG_GetPrescaler(WWDG_GET_INSTANCE(hwwdg)) >> WWDG_CFR_WDGTB_Pos),
                               (uint16_t)(LL_WWDG_GetWindow(WWDG_GET_INSTANCE(hwwdg)) >> WWDG_CFR_W_Pos));
}
/**
  * @}
  */

/** @addtogroup WWDG_Exported_Functions_Group5 IRQ Handler/Callbacks/Register Callbacks functions
  * @{
This subsection provides a set of functions to register the WWDG process and callbacks:

  - Call the function HAL_WWDG_IRQHandler() to handle WWDG interrupts.

There are two ways to use callbacks:\n\n
Override the weak callback function. Call HAL_WWDG_EarlyWakeupCallback() to indicate
that an early interrupt is pending.\n
Or register callbacks. Call HAL_WWDG_RegisterEarlyWakeupCallback() to register
the Early Wakeup callback.
  */

/**
  * @brief  Handle WWDG interrupt request.
  * @param  hwwdg  Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                the specified WWDG module.
  *
  * @note   The Early Wakeup Interrupt (EWI) can be used if specific safety operations or data logging must be
  *         performed before the actual reset is generated. The EWI interrupt is enabled by calling HAL_WWDG_Start()
  *         function with an ewi_status equal to 1. When the downcounter reaches the value 0x40,
  *         an EWI interrupt is generated and the corresponding Interrupt Service Routine (ISR) can be used to trigger
  *         specific actions (such as communications or data logging) before resetting the device.
  */
void HAL_WWDG_IRQHandler(hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);

  if (LL_WWDG_IsEnabledIT_EWKUP(WWDG_GET_INSTANCE(hwwdg)) != 0U)
  {
    if (LL_WWDG_IsActiveFlag_EWKUP(WWDG_GET_INSTANCE(hwwdg)) != 0U)
    {
      LL_WWDG_ClearFlag_EWKUP(WWDG_GET_INSTANCE(hwwdg));

#if defined(USE_HAL_WWDG_REGISTER_CALLBACKS) && (USE_HAL_WWDG_REGISTER_CALLBACKS == 1U)
      hwwdg->p_early_wakeup_cb(hwwdg);
#else
      HAL_WWDG_EarlyWakeupCallback(hwwdg);
#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
    }
  }
}

/**
  * @brief  WWDG Early Wakeup callback.
  * @param  hwwdg  Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                the specified WWDG module.
  */
__WEAK void HAL_WWDG_EarlyWakeupCallback(hal_wwdg_handle_t *hwwdg)
{
  /* Prevent unused argument(s) compilation warning */
  STM32_UNUSED(hwwdg);

  /*! <b>WARNING:</b> Do not modify this function. Implement HAL_WWDG_EarlyWakeupCallback()
                      in the application file when a callback is needed. */
}

#if defined(USE_HAL_WWDG_REGISTER_CALLBACKS) && (USE_HAL_WWDG_REGISTER_CALLBACKS == 1U)
/**
  * @brief   Register the WWDG Early Wakeup callback.
  * @param   hwwdg      Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                     the specified WWDG module.
  * @param   p_callback Pointer to the hal_wwdg_cb_t callback function.
  * @retval  HAL_OK             HAL operation completed successfully.
  * @retval  HAL_INVALID_PARAM  HAL invalid parameter.
  */
hal_status_t HAL_WWDG_RegisterEarlyWakeupCallback(hal_wwdg_handle_t *hwwdg, hal_wwdg_cb_t p_callback)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);
  ASSERT_DBG_PARAM(p_callback != NULL);

#if defined(USE_HAL_CHECK_PARAM) && (USE_HAL_CHECK_PARAM == 1U)
  if (p_callback == NULL)
  {
    return HAL_INVALID_PARAM;
  }
#endif /* USE_HAL_CHECK_PARAM */

  hwwdg->p_early_wakeup_cb = p_callback;

  return HAL_OK;
}

#endif /* USE_HAL_WWDG_REGISTER_CALLBACKS */
/**
  * @}
  */

#if defined (USE_HAL_WWDG_USER_DATA) && (USE_HAL_WWDG_USER_DATA == 1U)

/** @addtogroup WWDG_Exported_Functions_Group6 User Data Function
  * @{
This subsection provides functions to set user-specific data for a WWDG instance:
  - HAL_WWDG_SetUserData(): Set user data in the handle.
  - HAL_WWDG_GetUserData(): Get user data from the handle.
  */

/**
  * @brief Store the user data pointer in the handle.
  * @param hwwdg       Pointer to a \ref hal_wwdg_handle_t structure which contains the WWDG instance.
  * @param p_user_data Pointer to the user data.
  */
void HAL_WWDG_SetUserData(hal_wwdg_handle_t *hwwdg, const void *p_user_data)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);

  hwwdg->p_user_data = p_user_data;
}

/**
  * @brief  Retrieve the user data pointer from the handle.
  * @param  hwwdg Pointer to a \ref hal_wwdg_handle_t structure which contains the WWDG instance.
  * @retval Pointer to the user data.
  */
const void *HAL_WWDG_GetUserData(const hal_wwdg_handle_t *hwwdg)
{
  ASSERT_DBG_PARAM(hwwdg != NULL);

  return (hwwdg->p_user_data);
}
/**
  * @}
  */

#endif /* USE_HAL_WWDG_USER_DATA */
/**
  * @}
  */

/* Private functions -------------------------------------------------------------------------------------------------*/
/** @addtogroup WWDG_Private_Functions WWDG Private Functions
  * @{
  */

/**
  * @brief   Calculate the WWDG prescaler from the configured maximum time.
  * @param   hwwdg    Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                   the specified WWDG module.
  * @param   max_time Maximum time value before a WWDG reset.
  * @retval  0 for a prescaler = 1.
  * @retval  1 for a prescaler = 2.
  * @retval  2 for a prescaler = 4.
  * @retval  3 for a prescaler = 8.
  * @retval  4 for a prescaler = 16.
  * @retval  5 for a prescaler = 32.
  * @retval  6 for a prescaler = 64.
  * @retval  7 for a prescaler = 128.
  */
static uint8_t WWDG_CalculatePrescaler(const hal_wwdg_handle_t *hwwdg, uint32_t max_time)
{
  uint32_t max_period;

#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)
  max_period  = ((max_time * (hwwdg->pclk_frequency_hz / (WWDG_MAX_STEP_NUMBER * WWDG_INTERNAL_DIVIDER))) /
                 1000000UL);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)
  max_period  = (((max_time * (hwwdg->pclk_frequency_hz / WWDG_MAX_STEP_NUMBER)) / WWDG_INTERNAL_DIVIDER) /
                 1000UL);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S)
  max_period  = ((max_time * (hwwdg->pclk_frequency_hz / WWDG_MAX_STEP_NUMBER)) / WWDG_INTERNAL_DIVIDER);
#endif /* USE_HAL_WWDG_TIME_UNIT */

  /* Return the calculated prescaler */
  return (uint8_t)(32U - __CLZ(max_period));
}

/**
  * @brief   Extract the WWDG reload parameter from the configured maximum time.
  * @param   hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                    the specified WWDG module.
  * @param   prescaler WWDG prescaler.
  * @param   max_time  Maximum time value before a WWDG reset.
  * @retval  uint16_t  Current reload parameter.
  */
static uint16_t WWDG_CalculateReload(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint32_t max_time)
{
  uint32_t reload;
  uint32_t clk_divider;

  clk_divider = (uint32_t)(1UL << (prescaler & 0x7U));

#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)
  reload  = ((max_time * (hwwdg->pclk_frequency_hz / (clk_divider * WWDG_INTERNAL_DIVIDER))) / 1000000UL) +
            WWDG_MAX_STEP_NUMBER;
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)
  reload  = (((max_time * (hwwdg->pclk_frequency_hz / clk_divider)) / WWDG_INTERNAL_DIVIDER) / 1000UL) +
            WWDG_MAX_STEP_NUMBER;
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S)
  reload  = ((max_time * (hwwdg->pclk_frequency_hz / clk_divider)) / WWDG_INTERNAL_DIVIDER) + WWDG_MAX_STEP_NUMBER;
#endif /* USE_HAL_WWDG_TIME_UNIT */

  return (uint16_t)reload;
}

/**
  * @brief   Extract the WWDG window parameter from the configured minimum time.
  * @param   hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                    the specified WWDG module.
  * @param   prescaler WWDG prescaler.
  * @param   min_time  Minimum time value before refreshing is allowed.
  * @retval  uint16_t  Current Window parameter.
  */
static uint16_t WWDG_CalculateWindow(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint32_t min_time)
{
  uint32_t window;
  uint32_t clk_divider;

  clk_divider = (uint32_t)(1UL << (prescaler & 0x7U));

#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)
  window = hwwdg->reload - ((min_time * (hwwdg->pclk_frequency_hz / (clk_divider * WWDG_INTERNAL_DIVIDER)))
                            / 1000000UL);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)
  window = hwwdg->reload - ((min_time * (hwwdg->pclk_frequency_hz / (clk_divider * WWDG_INTERNAL_DIVIDER)))
                            / 1000UL);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S)
  window = hwwdg->reload - ((min_time * (hwwdg->pclk_frequency_hz / clk_divider)) / WWDG_INTERNAL_DIVIDER);
#endif /* USE_HAL_WWDG_TIME_UNIT */

  return (uint16_t)window;
}

/**
  * @brief   Calculate the WWDG maximum time value before a WWDG reset from the reload value.
  * @param   hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                    the specified WWDG module.
  * @param   prescaler WWDG prescaler.
  * @retval  uint32_t  Current maximum time.
  */
static uint32_t WWDG_CalculateMaxTime(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler)
{
  uint64_t max_time = 0U;
  uint64_t tmp;
  uint64_t freq_khz;

  tmp = (((uint64_t)hwwdg->reload - (uint64_t)WWDG_MAX_STEP_NUMBER) * (uint64_t)WWDG_INTERNAL_DIVIDER) *
        ((uint64_t)1 << prescaler);

  freq_khz = (uint64_t)hwwdg->pclk_frequency_hz / 1000U;

  if (freq_khz > 0U)
  {
    max_time = (tmp * 1000U) / freq_khz;

#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)
    max_time = (uint32_t)(((max_time * WWDG_TIME_CONVERSION) + 500U) / 1000U);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)
    max_time = (uint32_t)((max_time + 500U) / 1000U);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S)
    max_time = (uint32_t)((max_time + (WWDG_TIME_CONVERSION * 1000U / 2U)) / (WWDG_TIME_CONVERSION * 1000U));
#endif /* USE_HAL_WWDG_TIME_UNIT */
  }

  return (uint32_t)max_time;
}

/**
  * @brief   Calculate the WWDG minimum time value before refreshing is allowed from the window value.
  * @param   hwwdg     Pointer to a hal_wwdg_handle_t structure that contains the configuration information for
  *                    the specified WWDG module.
  * @param   prescaler WWDG prescaler.
  * @param   window    Corresponding window parameter.
  * @retval  uint32_t  Current minimum time.
  */
static uint32_t WWDG_CalculateMinTime(const hal_wwdg_handle_t *hwwdg, uint8_t prescaler, uint16_t window)
{
  uint64_t min_time = 0U;
  uint64_t tmp;
  uint64_t freq_khz;

  tmp = ((uint64_t)hwwdg->reload - (uint64_t)window) * (uint64_t)WWDG_INTERNAL_DIVIDER * ((uint64_t)1 << prescaler);

  freq_khz = (uint64_t)hwwdg->pclk_frequency_hz / 1000U;

  if (freq_khz > 0U)
  {
    min_time = (tmp * 1000U) / freq_khz;

#if (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_US)
    min_time = (uint32_t)(((min_time * WWDG_TIME_CONVERSION) + 500U) / 1000U);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_MS)
    min_time = (uint32_t)((min_time + 500U) / 1000U);
#elif (USE_HAL_WWDG_TIME_UNIT == HAL_WWDG_TIME_UNIT_S)
    min_time = (uint32_t)((min_time + ((WWDG_TIME_CONVERSION * 1000U) / 2U)) / (WWDG_TIME_CONVERSION * 1000U));
#endif /* USE_HAL_WWDG_TIME_UNIT */
  }

  return (uint32_t)min_time;
}
/**
  * @}
  */

/**
  * @}
  */

#endif /* USE_HAL_WWDG_MODULE */
#endif /* WWDG */
/**
  * @}
  */

