/***************************************************************************//**
 * @file
 * @brief System Real Time Counter (SYSRTC) peripheral API
 *******************************************************************************
 * # License
 * <b>Copyright 2024 Silicon Laboratories Inc. www.silabs.com</b>
 *******************************************************************************
 *
 * SPDX-License-Identifier: Zlib
 *
 * The licensor of this software is Silicon Laboratories Inc.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event will the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Permission is granted to anyone to use this software for any purpose,
 * including commercial applications, and to alter it and redistribute it
 * freely, subject to the following restrictions:
 *
 * 1. The origin of this software must not be misrepresented; you must not
 *    claim that you wrote the original software. If you use this software
 *    in a product, an acknowledgment in the product documentation would be
 *    appreciated but is not required.
 * 2. Altered source versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.
 * 3. This notice may not be removed or altered from any source distribution.
 *
 ******************************************************************************/

#ifndef SL_HAL_SYSRTC_H
#define SL_HAL_SYSRTC_H

#include "em_device.h"

#if defined(SYSRTC_COUNT) && (SYSRTC_COUNT > 0)
#include <stdbool.h>
#include "sl_code_classification.h"

#ifdef __cplusplus
extern "C" {
#endif

#include <stdbool.h>
#include "sl_hal_sysrtc_compat.h"
#include "sl_enum.h"

/***************************************************************************//**
 * @addtogroup sysrtc
 * @{
 ******************************************************************************/

/*******************************************************************************
 *******************************   DEFINES   ***********************************
 ******************************************************************************/

/// Minimum compare channels for SYSRTC group.
#define SYSRTC_GROUP_MIN_CHANNEL_COMPARE  1u
/// Maximum compare channels for SYSRTC group.
#define SYSRTC_GROUP_MAX_CHANNEL_COMPARE  2u

/// Minimum capture channels for SYSRTC group.
#define SYSRTC_GROUP_MIN_CHANNEL_CAPTURE  0u
/// Maximum capture channels for SYSRTC group.
#define SYSRTC_GROUP_MAX_CHANNEL_CAPTURE  1u

/// Sysrtc group number.
#if !defined(SYSRTC_GROUP_NUMBER)
#define SYSRTC_GROUP_NUMBER   1u
#endif

/// Validation of valid SYSRTC group for assert statements.
#define SYSRTC_GROUP_VALID(group)    ((unsigned)(group) < SYSRTC_GROUP_NUMBER)

/*******************************************************************************
 *********************************   ENUM   ************************************
 ******************************************************************************/

/// Capture input edge select.
SL_ENUM(sl_hal_sysrtc_capture_edge_t) {
  SL_HAL_SYSRTC_CAPTURE_EDGE_RISING = 0,  ///< Rising edges detected.
  SL_HAL_SYSRTC_CAPTURE_EDGE_FALLING,     ///< Falling edges detected.
  SL_HAL_SYSRTC_CAPTURE_EDGE_BOTH         ///< Both edges detected.
};

/// Compare match output action mode.
SL_ENUM(sl_hal_sysrtc_compare_match_out_action_t) {
  SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_CLEAR = 0, ///< Clear output.
  SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_SET,       ///< Set output.
  SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_PULSE,     ///< Generate a pulse.
  SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_TOGGLE,    ///< Toggle output.
  SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_CMPIF      ///< Export CMPIF.
};

/*******************************************************************************
 *******************************   STRUCTS   ***********************************
 ******************************************************************************/

/// SYSRTC configuration structure.
typedef struct {
  bool enable_debug_run;      ///< Counter shall keep running during debug halt.
} sl_hal_sysrtc_config_t;

/// Suggested default values for SYSRTC configuration structure.
#define SYSRTC_CONFIG_DEFAULT                        \
  {                                                  \
    false, /* Disable updating during debug halt. */ \
  }

/// Compare channel configuration structure.
typedef struct {
  sl_hal_sysrtc_compare_match_out_action_t  compare_match_out_action; ///< Compare mode channel match output action.
} sl_hal_sysrtc_group_channel_compare_config_t;

/// Capture channel configuration structure.
typedef struct {
  sl_hal_sysrtc_capture_edge_t  capture_input_edge; ///< Capture mode channel input edge.
} sl_hal_sysrtc_group_channel_capture_config_t;

/// Group configuration structure.
typedef struct {
  bool compare_channel0_enable;                                                  ///< Enable/Disable compare channel 0
  bool compare_channel1_enable;                                                  ///< Enable/Disable compare channel 1
  bool capture_channel0_enable;                                                  ///< Enable/Disable capture channel 0
  sl_hal_sysrtc_group_channel_compare_config_t const *p_compare_channel0_config; ///< Pointer to compare channel 0 config
  sl_hal_sysrtc_group_channel_compare_config_t const *p_compare_channel1_config; ///< Pointer to compare channel 1 config
  sl_hal_sysrtc_group_channel_capture_config_t const *p_capture_channel0_config; ///< Pointer to capture channel 0 config
} sl_hal_sysrtc_group_config_t;

/// Suggested default values for compare channel configuration structure.
#define SYSRTC_GROUP_CHANNEL_COMPARE_CONFIG_DEFAULT \
  {                                                 \
    SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_PULSE    \
  }

/// Compare channel configuration for starting HFXO using PRS.
#define SYSRTC_GROUP_CHANNEL_COMPARE_CONFIG_EARLY_WAKEUP \
  {                                                      \
    SL_HAL_SYSRTC_COMPARE_MATCH_OUT_ACTION_CMPIF         \
  }

/// Suggested default values for capture channel configuration structure.
#define SYSRTC_GROUP_CHANNEL_CAPTURE_CONFIG_DEFAULT \
  {                                                 \
    SL_HAL_SYSRTC_CAPTURE_EDGE_RISING               \
  }

/// Suggested default values for SYSRTC group configuration structure.
#define SYSRTC_GROUP_CONFIG_DEFAULT                                          \
  {                                                                          \
    true, /* Enable compare channel 0. */                                    \
    false, /* Disable compare channel 1. */                                  \
    false, /* Disable capture channel 0. */                                  \
    NULL, /* NULL Pointer to configuration structure for compare channel 0*/ \
    NULL, /* NULL Pointer to configuration structure for compare channel 1*/ \
    NULL /* NULL Pointer to configuration structure for capture channel 0*/  \
  }

/*******************************************************************************
 *****************************   PROTOTYPES   **********************************
 ******************************************************************************/

/***************************************************************************//**
 * Initializes SYSRTC module.
 *
 * Note that the compare values must be set separately with
 * (sl_hal_sysrtc_set_group_compare_channel_value()), which should probably be
 * done prior to the use of this function if configuring the SYSRTC to start
 * when initialization is completed.
 *
 * @param[in] p_config  A pointer to the SYSRTC initialization structure
 *                      variable.
 ******************************************************************************/
void sl_hal_sysrtc_init(const sl_hal_sysrtc_config_t *p_config);

/***************************************************************************//**
 * Enables SYSRTC counting.
 ******************************************************************************/
void sl_hal_sysrtc_enable(void);

/***************************************************************************//**
 * Disables SYSRTC counting.
 ******************************************************************************/
void sl_hal_sysrtc_disable(void);

/***************************************************************************//**
 * Waits for the SYSRTC to complete all synchronization of register changes
 * and commands.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
__INLINE void sl_hal_sysrtc_wait_sync(void)
{
  while ((SYSRTC0->EN & SYSRTC_EN_EN) && (SYSRTC0->SYNCBUSY != 0U)) {
    // Wait for all synchronizations to finish
  }
}

/***************************************************************************//**
 * Waits for the SYSRTC to complete reseting or disabling procedure.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_wait_ready(void)
{
  while ((SYSRTC0->SWRST & _SYSRTC_SWRST_RESETTING_MASK) || (SYSRTC0->EN & _SYSRTC_EN_DISABLING_MASK) || (SYSRTC0->SYNCBUSY != 0U)) {
    // Wait for all synchronizations to finish
  }
}

/***************************************************************************//**
 * Starts SYSRTC counter.
 *
 * This function will send a start command to the SYSRTC peripheral. The SYSRTC
 * peripheral will use some LF clock ticks before the command is executed.
 * The sl_hal_sysrtc_wait_sync() function can be used to wait for the start
 * command to be executed.
 *
 * @note  This function requires the SYSRTC to be enabled.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_start(void)
{
  sl_hal_sysrtc_wait_sync();
  SYSRTC0->CMD = SYSRTC_CMD_START;
}

/***************************************************************************//**
 * Stops the SYSRTC counter.
 *
 * This function will send a stop command to the SYSRTC peripheral. The SYSRTC
 * peripheral will use some LF clock ticks before the command is executed.
 * The sl_hal_sysrtc_wait_sync() function can be used to wait for the stop
 * command to be executed.
 *
 * @note  This function requires the SYSRTC to be enabled.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_stop(void)
{
  sl_hal_sysrtc_wait_sync();
  SYSRTC0->CMD = SYSRTC_CMD_STOP;
}

/***************************************************************************//**
 * Restores SYSRTC to its reset state.
 ******************************************************************************/
void sl_hal_sysrtc_reset(void);

/***************************************************************************//**
 * Gets SYSRTC STATUS register value.
 *
 * @return  Current STATUS register value.
 ******************************************************************************/
__INLINE uint32_t sl_hal_sysrtc_get_status(void)
{
  return SYSRTC0->STATUS;
}

/***************************************************************************//**
 * Locks SYSRTC registers.
 *
 * @note  When SYSRTC registers are locked SYSRTC_EN, SYSRTC_CFG, SYSRTC_CMD,
 *        SYSRTC_SWRST, SYSRTC_CNT and SYSRTC_TOPCNT registers cannot be written
 *        to.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_lock(void)
{
  SYSRTC0->LOCK = ~SYSRTC_LOCK_LOCKKEY_UNLOCK;
}

/***************************************************************************//**
 * Unlocks SYSRTC registers.
 *
 * @note  When SYSRTC registers are locked SYSRTC_EN, SYSRTC_CFG, SYSRTC_CMD,
 *        SYSRTC_SWRST, SYSRTC_CNT and SYSRTC_TOPCNT registers cannot be written
 *        to.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_unlock(void)
{
  SYSRTC0->LOCK = SYSRTC_LOCK_LOCKKEY_UNLOCK;
}

/***************************************************************************//**
 * Gets SYSRTC counter value.
 *
 * @return  Current SYSRTC counter value.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
__INLINE uint32_t sl_hal_sysrtc_get_counter(void)
{
  // Wait for Counter to synchronize before getting value
  sl_hal_sysrtc_wait_sync();

  return SYSRTC0->CNT;
}

/***************************************************************************//**
 * Sets the SYSRTC counter value.
 *
 * @param[in] value   The new SYSRTC counter value.
 ******************************************************************************/
__INLINE void sl_hal_sysrtc_set_counter(uint32_t value)
{
  // Wait for Counter to synchronize before getting value
  sl_hal_sysrtc_wait_sync();

  SYSRTC0->CNT = value;
}

/***************************************************************************//**
 * Initializes the selected SYSRTC group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] p_group_config  Pointer to group configuration structure
 *                            variable.
 ******************************************************************************/
void sl_hal_sysrtc_init_group(uint8_t group_number,
                              sl_hal_sysrtc_group_config_t const *p_group_config);

/***************************************************************************//**
 * Enables one or more SYSRTC interrupts for the given group.
 *
 * @note  Depending on the use, a pending interrupt may already be set prior to
 *        enabling the interrupt. To ignore a pending interrupt, consider using
 *        sl_hal_sysrtc_clear_group_interrupts() prior to enabling the interrupt.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] flags   SYSRTC interrupt sources to enable.
 *                    Use a set of interrupt flags OR-ed together to set
 *                    multiple interrupt sources for the given SYSRTC group.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
void sl_hal_sysrtc_enable_group_interrupts(uint8_t group_number,
                                           uint32_t flags);

/***************************************************************************//**
 * Disables one or more SYSRTC interrupts for the given group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] flags   SYSRTC interrupt sources to disable.
 *                    Use a set of interrupt flags OR-ed together to disable
 *                    multiple interrupt sources for the given SYSRTC group.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
void sl_hal_sysrtc_disable_group_interrupts(uint8_t group_number,
                                            uint32_t flags);

/***************************************************************************//**
 * Clears one or more pending SYSRTC interrupts for the given group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] flags   SYSRTC interrupt sources to clear.
 *                    Use a set of interrupt flags OR-ed together to clear
 *                    multiple interrupt sources for the given SYSRTC group.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
void sl_hal_sysrtc_clear_group_interrupts(uint8_t group_number,
                                          uint32_t flags);

/***************************************************************************//**
 * Gets pending SYSRTC interrupt flags for the given group.
 *
 * @note  Event bits are not cleared by using this function.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @return  Pending SYSRTC interrupt sources.
 *          Returns a set of interrupt flags OR-ed together for multiple
 *          interrupt sources in the SYSRTC group.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
uint32_t sl_hal_sysrtc_get_group_interrupts(uint8_t group_number);

/***************************************************************************//**
 * Gets enabled and pending SYSRTC interrupt flags.
 * Useful for handling more interrupt sources in the same interrupt handler.
 *
 * @note  Interrupt flags are not cleared by using this function.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @return  Pending and enabled SYSRTC interrupt sources.
 *          The return value is the bitwise AND of
 *          - the enabled interrupt sources in SYSRTC_GRPx_IEN and
 *          - the pending interrupt flags SYSRTC_GRPx_IF.
 ******************************************************************************/
uint32_t sl_hal_sysrtc_get_group_enabled_interrupts(uint8_t group_number);

/***************************************************************************//**
 * Sets one or more pending SYSRTC interrupts for the given group from Software.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] flags   SYSRTC interrupt sources to set to pending.
 *                    Use a set of interrupt flags OR-ed together to set
 *                    multiple interrupt sources for the SYSRTC group.
 ******************************************************************************/
void sl_hal_sysrtc_set_group_interrupts(uint8_t group_number,
                                        uint32_t flags);

/***************************************************************************//**
 * Gets SYSRTC compare register value for selected channel of given group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] channel   Channel selector.
 *
 * @return  Compare register value.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
uint32_t sl_hal_sysrtc_get_group_compare_channel_value(uint8_t group_number,
                                                       uint8_t channel);

/***************************************************************************//**
 * Sets SYSRTC compare register value for selected channel of given group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @param[in] channel   Channel selector.
 *
 * @param[in] value   Compare register value.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
void sl_hal_sysrtc_set_group_compare_channel_value(uint8_t group_number,
                                                   uint8_t channel,
                                                   uint32_t value);

/***************************************************************************//**
 * Gets SYSRTC input capture register value for capture channel of given group.
 *
 * @param[in] group_number  SYSRTC group number to use.
 *
 * @return  Capture register value.
 ******************************************************************************/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_HAL_SYSRTC, SL_CODE_CLASS_TIME_CRITICAL)
uint32_t sl_hal_sysrtc_get_group_capture_channel_value(uint8_t group_number);

/** @} (end addtogroup sysrtc) */

#ifdef __cplusplus
}
#endif

#endif /* defined(SYSRTC_COUNT) && (SYSRTC_COUNT > 0) */
#endif /* SL_HAL_SYSRTC_H */
