/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2017, 2020 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#ifndef _FSL_MRT_H_
#define _FSL_MRT_H_

#include "fsl_common.h"

/*!
 * @addtogroup mrt
 * @{
 */

/*! @file */

/*******************************************************************************
 * Definitions
 ******************************************************************************/

/*! @name Driver version */
/*@{*/
#define FSL_MRT_DRIVER_VERSION (MAKE_VERSION(2, 0, 3)) /*!< Version 2.0.3 */
/*@}*/

/*! @brief List of MRT channels */
typedef enum _mrt_chnl
{
    kMRT_Channel_0 = 0U, /*!< MRT channel number 0*/
    kMRT_Channel_1,      /*!< MRT channel number 1 */
    kMRT_Channel_2,      /*!< MRT channel number 2 */
    kMRT_Channel_3       /*!< MRT channel number 3 */
} mrt_chnl_t;

/*! @brief List of MRT timer modes */
typedef enum _mrt_timer_mode
{
    kMRT_RepeatMode       = (0 << MRT_CHANNEL_CTRL_MODE_SHIFT), /*!< Repeat Interrupt mode */
    kMRT_OneShotMode      = (1 << MRT_CHANNEL_CTRL_MODE_SHIFT), /*!< One-shot Interrupt mode */
    kMRT_OneShotStallMode = (2 << MRT_CHANNEL_CTRL_MODE_SHIFT)  /*!< One-shot stall mode */
} mrt_timer_mode_t;

/*! @brief List of MRT interrupts */
typedef enum _mrt_interrupt_enable
{
    kMRT_TimerInterruptEnable = MRT_CHANNEL_CTRL_INTEN_MASK /*!< Timer interrupt enable*/
} mrt_interrupt_enable_t;

/*! @brief List of MRT status flags */
typedef enum _mrt_status_flags
{
    kMRT_TimerInterruptFlag = MRT_CHANNEL_STAT_INTFLAG_MASK, /*!< Timer interrupt flag */
    kMRT_TimerRunFlag       = MRT_CHANNEL_STAT_RUN_MASK,     /*!< Indicates state of the timer */
} mrt_status_flags_t;

/*!
 * @brief MRT configuration structure
 *
 * This structure holds the configuration settings for the MRT peripheral. To initialize this
 * structure to reasonable defaults, call the MRT_GetDefaultConfig() function and pass a
 * pointer to your config structure instance.
 *
 * The config struct can be made const so it resides in flash
 */
typedef struct _mrt_config
{
    bool enableMultiTask; /*!< true: Timers run in multi-task mode; false: Timers run in hardware status mode */
} mrt_config_t;

/*******************************************************************************
 * API
 ******************************************************************************/

#if defined(__cplusplus)
extern "C" {
#endif

/*!
 * @name Initialization and deinitialization
 * @{
 */

/*!
 * @brief Ungates the MRT clock and configures the peripheral for basic operation.
 *
 * @note This API should be called at the beginning of the application using the MRT driver.
 *
 * @param base   Multi-Rate timer peripheral base address
 * @param config Pointer to user's MRT config structure. If MRT has  MULTITASK bit field in
 *               MODCFG reigster, param config is useless.
 */
void MRT_Init(MRT_Type *base, const mrt_config_t *config);

/*!
 * @brief Gate the MRT clock
 *
 * @param base Multi-Rate timer peripheral base address
 */
void MRT_Deinit(MRT_Type *base);

/*!
 * @brief Fill in the MRT config struct with the default settings
 *
 * The default values are:
 * @code
 *     config->enableMultiTask = false;
 * @endcode
 * @param config Pointer to user's MRT config structure.
 */
static inline void MRT_GetDefaultConfig(mrt_config_t *config)
{
    assert(config != NULL);
#if !(defined(FSL_FEATURE_MRT_HAS_NO_MODCFG_MULTITASK) && FSL_FEATURE_MRT_HAS_NO_MODCFG_MULTITASK)
    /* Use hardware status operating mode */
    config->enableMultiTask = false;
#endif
}

/*!
 * @brief Sets up an MRT channel mode.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Channel that is being configured.
 * @param mode    Timer mode to use for the channel.
 */
static inline void MRT_SetupChannelMode(MRT_Type *base, mrt_chnl_t channel, const mrt_timer_mode_t mode)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);

    uint32_t reg = base->CHANNEL[channel].CTRL;

    /* Clear old value */
    reg &= ~MRT_CHANNEL_CTRL_MODE_MASK;
    /* Add the new mode */
    reg |= (uint32_t)mode;

    base->CHANNEL[channel].CTRL = reg;
}

/*! @}*/

/*!
 * @name Interrupt Interface
 * @{
 */

/*!
 * @brief Enables the MRT interrupt.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 * @param mask    The interrupts to enable. This is a logical OR of members of the
 *                enumeration ::mrt_interrupt_enable_t
 */
static inline void MRT_EnableInterrupts(MRT_Type *base, mrt_chnl_t channel, uint32_t mask)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    base->CHANNEL[channel].CTRL |= mask;
}

/*!
 * @brief Disables the selected MRT interrupt.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 * @param mask    The interrupts to disable. This is a logical OR of members of the
 *                enumeration ::mrt_interrupt_enable_t
 */
static inline void MRT_DisableInterrupts(MRT_Type *base, mrt_chnl_t channel, uint32_t mask)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    base->CHANNEL[channel].CTRL &= ~mask;
}

/*!
 * @brief Gets the enabled MRT interrupts.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 *
 * @return The enabled interrupts. This is the logical OR of members of the
 *         enumeration ::mrt_interrupt_enable_t
 */
static inline uint32_t MRT_GetEnabledInterrupts(MRT_Type *base, mrt_chnl_t channel)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    return (base->CHANNEL[channel].CTRL & MRT_CHANNEL_CTRL_INTEN_MASK);
}

/*! @}*/

/*!
 * @name Status Interface
 * @{
 */

/*!
 * @brief Gets the MRT status flags
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 *
 * @return The status flags. This is the logical OR of members of the
 *         enumeration ::mrt_status_flags_t
 */
static inline uint32_t MRT_GetStatusFlags(MRT_Type *base, mrt_chnl_t channel)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    return (base->CHANNEL[channel].STAT & (MRT_CHANNEL_STAT_INTFLAG_MASK | MRT_CHANNEL_STAT_RUN_MASK));
}

/*!
 * @brief Clears the MRT status flags.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 * @param mask    The status flags to clear. This is a logical OR of members of the
 *                enumeration ::mrt_status_flags_t
 */
static inline void MRT_ClearStatusFlags(MRT_Type *base, mrt_chnl_t channel, uint32_t mask)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    base->CHANNEL[channel].STAT = (mask & MRT_CHANNEL_STAT_INTFLAG_MASK);
}

/*! @}*/

/*!
 * @name Read and Write the timer period
 * @{
 */

/*!
 * @brief Used to update the timer period in units of count.
 *
 * The new value will be immediately loaded or will be loaded at the end of the current time
 * interval. For one-shot interrupt mode the new value will be immediately loaded.
 *
 * @note User can call the utility macros provided in fsl_common.h to convert to ticks
 *
 * @param base          Multi-Rate timer peripheral base address
 * @param channel       Timer channel number
 * @param count         Timer period in units of ticks
 * @param immediateLoad true: Load the new value immediately into the TIMER register;
 *                      false: Load the new value at the end of current timer interval
 */
void MRT_UpdateTimerPeriod(MRT_Type *base, mrt_chnl_t channel, uint32_t count, bool immediateLoad);

/*!
 * @brief Reads the current timer counting value.
 *
 * This function returns the real-time timer counting value, in a range from 0 to a
 * timer period.
 *
 * @note User can call the utility macros provided in fsl_common.h to convert ticks to usec or msec
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number
 *
 * @return Current timer counting value in ticks
 */
static inline uint32_t MRT_GetCurrentTimerCount(MRT_Type *base, mrt_chnl_t channel)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    return base->CHANNEL[channel].TIMER;
}

/*! @}*/

/*!
 * @name Timer Start and Stop
 * @{
 */

/*!
 * @brief Starts the timer counting.
 *
 * After calling this function, timers load period value, counts down to 0 and
 * depending on the timer mode it will either load the respective start value again or stop.
 *
 * @note User can call the utility macros provided in fsl_common.h to convert to ticks
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number.
 * @param count   Timer period in units of ticks. Count can contain the LOAD bit, which control the force load feature.
 */
static inline void MRT_StartTimer(MRT_Type *base, mrt_chnl_t channel, uint32_t count)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    assert((uint32_t)(count & ~MRT_CHANNEL_INTVAL_LOAD_MASK) <= (uint32_t)MRT_CHANNEL_INTVAL_IVALUE_MASK);
    /* Write the timer interval value */
    base->CHANNEL[channel].INTVAL = count;
}

/*!
 * @brief Stops the timer counting.
 *
 * This function stops the timer from counting.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number.
 */
static inline void MRT_StopTimer(MRT_Type *base, mrt_chnl_t channel)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);
    /* Stop the timer immediately */
    base->CHANNEL[channel].INTVAL = MRT_CHANNEL_INTVAL_LOAD_MASK;
}

/*! @}*/

/*!
 * @name Get & release channel
 * @{
 */

/*!
 * @brief Find the available channel.
 *
 * This function returns the lowest available channel number.
 *
 * @param base Multi-Rate timer peripheral base address
 */
static inline uint32_t MRT_GetIdleChannel(MRT_Type *base)
{
    return base->IDLE_CH;
}

#if !(defined(FSL_FEATURE_MRT_HAS_NO_CHANNEL_STAT_INUSE) && FSL_FEATURE_MRT_HAS_NO_CHANNEL_STAT_INUSE)
/*!
 * @brief Release the channel when the timer is using the multi-task mode.
 *
 * In multi-task mode, the INUSE flags allow more control over when MRT channels are released for
 * further use. The user can hold on to a channel acquired by calling MRT_GetIdleChannel() for as
 * long as it is needed and release it by calling this function. This removes the need to ask for
 * an available channel for every use.
 *
 * @param base    Multi-Rate timer peripheral base address
 * @param channel Timer channel number.
 */
static inline void MRT_ReleaseChannel(MRT_Type *base, mrt_chnl_t channel)
{
    assert((uint8_t)channel < (uint8_t)FSL_FEATURE_MRT_NUMBER_OF_CHANNELS);

    uint32_t reg = base->CHANNEL[channel].STAT;

    /* Clear flag bits to prevent accidentally clearing anything when writing back */
    reg = ~MRT_CHANNEL_STAT_INTFLAG_MASK;
    reg |= MRT_CHANNEL_STAT_INUSE_MASK;

    base->CHANNEL[channel].STAT = reg;
}
#endif

/*! @}*/

#if defined(__cplusplus)
}
#endif

/*! @}*/

#endif /* _FSL_MRT_H_ */
