/*
 * Copyright (c) 2016, Freescale Semiconductor, Inc.
 * Copyright 2016-2019 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_sctimer.h"

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

/* Component ID definition, used by tools. */
#ifndef FSL_COMPONENT_ID
#define FSL_COMPONENT_ID "platform.drivers.sctimer"
#endif

/*! @brief Typedef for interrupt handler. */
typedef void (*sctimer_isr_t)(SCT_Type *base);

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
/*!
 * @brief Gets the instance from the base address
 *
 * @param base SCTimer peripheral base address
 *
 * @return The SCTimer instance
 */
static uint32_t SCTIMER_GetInstance(SCT_Type *base);

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*! @brief Pointers to SCT bases for each instance. */
static SCT_Type *const s_sctBases[] = SCT_BASE_PTRS;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
/*! @brief Pointers to SCT clocks for each instance. */
static const clock_ip_name_t s_sctClocks[] = SCT_CLOCKS;
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
#if defined(FSL_FEATURE_SCT_WRITE_ZERO_ASSERT_RESET) && FSL_FEATURE_SCT_WRITE_ZERO_ASSERT_RESET
/*! @brief Pointers to SCT resets for each instance, writing a zero asserts the reset */
static const reset_ip_name_t s_sctResets[] = SCT_RSTS_N;
#else
/*! @brief Pointers to SCT resets for each instance, writing a one asserts the reset */
static const reset_ip_name_t s_sctResets[] = SCT_RSTS;
#endif
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */

/*!< @brief SCTimer event Callback function. */
static sctimer_event_callback_t s_eventCallback[FSL_FEATURE_SCT_NUMBER_OF_EVENTS];

/*!< @brief Keep track of SCTimer event number */
static uint32_t s_currentEvent;

/*!< @brief Keep track of SCTimer state number */
static uint32_t s_currentState;

/*!< @brief Keep track of SCTimer match/capture register number */
static uint32_t s_currentMatch;

/*! @brief Pointer to SCTimer IRQ handler */
static sctimer_isr_t s_sctimerIsr;

/*******************************************************************************
 * Code
 ******************************************************************************/
static uint32_t SCTIMER_GetInstance(SCT_Type *base)
{
    uint32_t instance;
    uint32_t sctArrayCount = (sizeof(s_sctBases) / sizeof(s_sctBases[0]));

    /* Find the instance index from base address mappings. */
    for (instance = 0; instance < sctArrayCount; instance++)
    {
        if (s_sctBases[instance] == base)
        {
            break;
        }
    }

    assert(instance < sctArrayCount);

    return instance;
}

/*!
 * brief Ungates the SCTimer clock and configures the peripheral for basic operation.
 *
 * note This API should be called at the beginning of the application using the SCTimer driver.
 *
 * param base   SCTimer peripheral base address
 * param config Pointer to the user configuration structure.
 *
 * return kStatus_Success indicates success; Else indicates failure.
 */
status_t SCTIMER_Init(SCT_Type *base, const sctimer_config_t *config)
{
    assert(NULL != config);

    uint32_t i;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Enable the SCTimer clock*/
    CLOCK_EnableClock(s_sctClocks[SCTIMER_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

#if !(defined(FSL_SDK_DISABLE_DRIVER_RESET_CONTROL) && FSL_SDK_DISABLE_DRIVER_RESET_CONTROL)
    /* Reset the module. */
    RESET_PeripheralReset(s_sctResets[SCTIMER_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_RESET_CONTROL */

    /* Setup the counter operation. For Current Driver interface SCTIMER_Init don't know detail
     * frequency of input clock, but User know it. So the INSYNC have to set by user level. */
    base->CONFIG = SCT_CONFIG_CKSEL(config->clockSelect) | SCT_CONFIG_CLKMODE(config->clockMode) |
                   SCT_CONFIG_UNIFY(config->enableCounterUnify) | SCT_CONFIG_INSYNC(config->inputsync);

    /* Write to the control register, clear the counter and keep the counters halted */
    base->CTRL = SCT_CTRL_BIDIR_L(config->enableBidirection_l) | SCT_CTRL_PRE_L(config->prescale_l) |
                 SCT_CTRL_CLRCTR_L_MASK | SCT_CTRL_HALT_L_MASK;

    if (!(config->enableCounterUnify))
    {
        base->CTRL |= SCT_CTRL_BIDIR_H(config->enableBidirection_h) | SCT_CTRL_PRE_H(config->prescale_h) |
                      SCT_CTRL_CLRCTR_H_MASK | SCT_CTRL_HALT_H_MASK;
    }

    /* Initial state of channel output */
    base->OUTPUT = config->outInitState;

    /* Clear the global variables */
    s_currentEvent = 0;
    s_currentState = 0;
    s_currentMatch = 0;

    /* Clear the callback array */
    for (i = 0; i < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS; i++)
    {
        s_eventCallback[i] = NULL;
    }

    /* Save interrupt handler */
    s_sctimerIsr = SCTIMER_EventHandleIRQ;

    return kStatus_Success;
}

/*!
 * brief Gates the SCTimer clock.
 *
 * param base SCTimer peripheral base address
 */
void SCTIMER_Deinit(SCT_Type *base)
{
    /* Halt the counters */
    base->CTRL |= (SCT_CTRL_HALT_L_MASK | SCT_CTRL_HALT_H_MASK);

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Disable the SCTimer clock*/
    CLOCK_DisableClock(s_sctClocks[SCTIMER_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

/*!
 * brief  Fills in the SCTimer configuration structure with the default settings.
 *
 * The default values are:
 * code
 *  config->enableCounterUnify = true;
 *  config->clockMode = kSCTIMER_System_ClockMode;
 *  config->clockSelect = kSCTIMER_Clock_On_Rise_Input_0;
 *  config->enableBidirection_l = false;
 *  config->enableBidirection_h = false;
 *  config->prescale_l = 0U;
 *  config->prescale_h = 0U;
 *  config->outInitState = 0U;
 *  config->inputsync  = 0xFU;
 * endcode
 * param config Pointer to the user configuration structure.
 */
void SCTIMER_GetDefaultConfig(sctimer_config_t *config)
{
    assert(NULL != config);

    /* Initializes the configure structure to zero. */
    (void)memset(config, 0, sizeof(*config));

    /* SCT operates as a unified 32-bit counter */
    config->enableCounterUnify = true;
    /* System clock clocks the entire SCT module */
    config->clockMode = kSCTIMER_System_ClockMode;
    /* This is used only by certain clock modes */
    config->clockSelect = kSCTIMER_Clock_On_Rise_Input_0;
    /* Up count mode only for the unified counter */
    config->enableBidirection_l = false;
    /* Up count mode only for Counte_H */
    config->enableBidirection_h = false;
    /* Prescale factor of 1 */
    config->prescale_l = 0U;
    /* Prescale factor of 1 for Counter_H*/
    config->prescale_h = 0U;
    /* Clear outputs */
    config->outInitState = 0U;
    /* Default value is 0xFU, it can be clear as 0 when speical conditions met.
     * Condition can be clear as 0: (for all Clock Modes):
     * (1) The corresponding input is already synchronous to the SCTimer/PWM clock.
     * (2) The SCTimer/PWM clock frequency does not exceed 100 MHz.
     * Note: The SCTimer/PWM clock is the bus/system clock for CKMODE 0-2 or asynchronous input
     * clock for CKMODE3.
     * Another condition can be clear as 0: (for CKMODE2 only)
     * (1) The corresponding input is synchronous to the designated CKMODE2 input clock.
     * (2) The CKMODE2 input clock frequency is less than one-third the frequency of the bus/system clock.
     * Default value set as 0U, input0~input3 are set as bypasses. */
    config->inputsync = 0xFU;
}

/*!
 * brief Configures the PWM signal parameters.
 *
 * Call this function to configure the PWM signal period, mode, duty cycle, and edge. This
 * function will create 2 events; one of the events will trigger on match with the pulse value
 * and the other will trigger when the counter matches the PWM period. The PWM period event is
 * also used as a limit event to reset the counter or change direction. Both events are enabled
 * for the same state. The state number can be retrieved by calling the function
 * SCTIMER_GetCurrentStateNumber().
 * The counter is set to operate as one 32-bit counter (unify bit is set to 1).
 * The counter operates in bi-directional mode when generating a center-aligned PWM.
 *
 * note When setting PWM output from multiple output pins, they all should use the same PWM mode
 * i.e all PWM's should be either edge-aligned or center-aligned.
 * When using this API, the PWM signal frequency of all the initialized channels must be the same.
 * Otherwise all the initialized channels' PWM signal frequency is equal to the last call to the
 * API's pwmFreq_Hz.
 *
 * param base        SCTimer peripheral base address
 * param pwmParams   PWM parameters to configure the output
 * param mode        PWM operation mode, options available in enumeration ::sctimer_pwm_mode_t
 * param pwmFreq_Hz  PWM signal frequency in Hz
 * param srcClock_Hz SCTimer counter clock in Hz
 * param event       Pointer to a variable where the PWM period event number is stored
 *
 * return kStatus_Success on success
 *         kStatus_Fail If we have hit the limit in terms of number of events created or if
 *                      an incorrect PWM dutycylce is passed in.
 */
status_t SCTIMER_SetupPwm(SCT_Type *base,
                          const sctimer_pwm_signal_param_t *pwmParams,
                          sctimer_pwm_mode_t mode,
                          uint32_t pwmFreq_Hz,
                          uint32_t srcClock_Hz,
                          uint32_t *event)
{
    assert(NULL != pwmParams);
    assert(0U != srcClock_Hz);
    assert(0U != pwmFreq_Hz);
    assert((uint32_t)pwmParams->output < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);

    status_t status;
    uint32_t period, pulsePeriod = 0;
    uint32_t sctClock    = srcClock_Hz / (((base->CTRL & SCT_CTRL_PRE_L_MASK) >> SCT_CTRL_PRE_L_SHIFT) + 1U);
    uint32_t periodEvent = 0, pulseEvent = 0;
    uint32_t reg;

    if ((0U != pwmParams->dutyCyclePercent) && ((s_currentEvent + 2U) <= (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS))
    {
        /* Set unify bit to operate in 32-bit counter mode */
        base->CONFIG |= SCT_CONFIG_UNIFY_MASK;

        /* Use bi-directional mode for center-aligned PWM */
        if (mode == kSCTIMER_CenterAlignedPwm)
        {
            base->CTRL |= SCT_CTRL_BIDIR_L_MASK;
        }

        /* Calculate PWM period match value */
        if (mode == kSCTIMER_EdgeAlignedPwm)
        {
            period = (sctClock / pwmFreq_Hz) - 1U;
        }
        else
        {
            period = sctClock / (pwmFreq_Hz * 2U);
        }

        /* Calculate pulse width match value */
        pulsePeriod = (period * pwmParams->dutyCyclePercent) / 100U;

        /* For 100% dutycyle, make pulse period greater than period so the event will never occur */
        if (pwmParams->dutyCyclePercent >= 100U)
        {
            pulsePeriod = period + 2U;
        }

        /* Schedule an event when we reach the PWM period */
        (void)SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, period, 0, kSCTIMER_Counter_L,
                                             &periodEvent);

        /* Schedule an event when we reach the pulse width */
        (void)SCTIMER_CreateAndScheduleEvent(base, kSCTIMER_MatchEventOnly, pulsePeriod, 0, kSCTIMER_Counter_L,
                                             &pulseEvent);

        /* Reset the counter when we reach the PWM period */
        SCTIMER_SetupCounterLimitAction(base, kSCTIMER_Counter_L, periodEvent);

        /* Return the period event to the user */
        *event = periodEvent;

        /* For high-true level */
        if ((uint32_t)pwmParams->level == (uint32_t)kSCTIMER_HighTrue)
        {
            /* Set the initial output level to low which is the inactive state */
            base->OUTPUT &= ~(1UL << (uint32_t)pwmParams->output);

            if (mode == kSCTIMER_EdgeAlignedPwm)
            {
                /* Set the output when we reach the PWM period */
                SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, periodEvent);
                /* Clear the output when we reach the PWM pulse value */
                SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, pulseEvent);
            }
            else
            {
                /* Clear the output when we reach the PWM pulse event */
                SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, pulseEvent);
                /* Reverse output when down counting */
                reg = base->OUTPUTDIRCTRL;
                reg &= ~((uint32_t)SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2U * (uint32_t)pwmParams->output));
                reg |= (1UL << (2U * (uint32_t)pwmParams->output));
                base->OUTPUTDIRCTRL = reg;
            }
        }
        /* For low-true level */
        else
        {
            /* Set the initial output level to high which is the inactive state */
            base->OUTPUT |= (1UL << (uint32_t)pwmParams->output);

            if (mode == kSCTIMER_EdgeAlignedPwm)
            {
                /* Clear the output when we reach the PWM period */
                SCTIMER_SetupOutputClearAction(base, (uint32_t)pwmParams->output, periodEvent);
                /* Set the output when we reach the PWM pulse value */
                SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, pulseEvent);
            }
            else
            {
                /* Set the output when we reach the PWM pulse event */
                SCTIMER_SetupOutputSetAction(base, (uint32_t)pwmParams->output, pulseEvent);
                /* Reverse output when down counting */
                reg = base->OUTPUTDIRCTRL;
                reg &= ~((uint32_t)SCT_OUTPUTDIRCTRL_SETCLR0_MASK << (2U * (uint32_t)pwmParams->output));
                reg |= (1UL << (2U * (uint32_t)pwmParams->output));
                base->OUTPUTDIRCTRL = reg;
            }
        }

        status = kStatus_Success;
    }
    else
    {
        /* if we do not have enough events available (this function will create two events)
         * or the value of dutyCyclePercent is zero, the function will return fail.
         */
        status = kStatus_Fail;
    }

    return status;
}

/*!
 * brief Updates the duty cycle of an active PWM signal.
 *
 * param base              SCTimer peripheral base address
 * param output            The output to configure
 * param dutyCyclePercent  New PWM pulse width; the value should be between 1 to 100
 * param event             Event number associated with this PWM signal. This was returned to the user by the
 *                          function SCTIMER_SetupPwm().
 */
void SCTIMER_UpdatePwmDutycycle(SCT_Type *base, sctimer_out_t output, uint8_t dutyCyclePercent, uint32_t event)

{
    assert(dutyCyclePercent > 0U);
    assert((uint32_t)output < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);

    uint32_t periodMatchReg, pulseMatchReg;
    uint32_t pulsePeriod = 0, period;

    /* Retrieve the match register number for the PWM period */
    periodMatchReg = base->EV[event].CTRL & SCT_EV_CTRL_MATCHSEL_MASK;

    /* Retrieve the match register number for the PWM pulse period */
    pulseMatchReg = base->EV[event + 1U].CTRL & SCT_EV_CTRL_MATCHSEL_MASK;

    period = base->MATCH[periodMatchReg];

    /* Calculate pulse width match value */
    pulsePeriod = (period * dutyCyclePercent) / 100U;

    /* For 100% dutycyle, make pulse period greater than period so the event will never occur */
    if (dutyCyclePercent >= 100U)
    {
        pulsePeriod = period + 2U;
    }

    /* Stop the counter before updating match register */
    SCTIMER_StopTimer(base, kSCTIMER_Counter_L);

    /* Update dutycycle */
    base->MATCH[pulseMatchReg]    = SCT_MATCH_MATCHn_L(pulsePeriod);
    base->MATCHREL[pulseMatchReg] = SCT_MATCHREL_RELOADn_L(pulsePeriod);

    /* Restart the counter */
    SCTIMER_StartTimer(base, kSCTIMER_Counter_L);
}

/*!
 * brief Create an event that is triggered on a match or IO and schedule in current state.
 *
 * This function will configure an event using the options provided by the user. If the event type uses
 * the counter match, then the function will set the user provided match value into a match register
 * and put this match register number into the event control register.
 * The event is enabled for the current state and the event number is increased by one at the end.
 * The function returns the event number; this event number can be used to configure actions to be
 * done when this event is triggered.
 *
 * param base         SCTimer peripheral base address
 * param howToMonitor Event type; options are available in the enumeration ::sctimer_interrupt_enable_t
 * param matchValue   The match value that will be programmed to a match register
 * param whichIO      The input or output that will be involved in event triggering. This field
 *                     is ignored if the event type is "match only"
 * param whichCounter SCTimer counter to use when operating in 16-bit mode. In 32-bit mode, this
 *                     field has no meaning as we have only 1 unified counter; hence ignored.
 * param event        Pointer to a variable where the new event number is stored
 *
 * return kStatus_Success on success
 *         kStatus_Error if we have hit the limit in terms of number of events created or
                         if we have reached the limit in terms of number of match registers
 */
status_t SCTIMER_CreateAndScheduleEvent(SCT_Type *base,
                                        sctimer_event_t howToMonitor,
                                        uint32_t matchValue,
                                        uint32_t whichIO,
                                        sctimer_counter_t whichCounter,
                                        uint32_t *event)
{
    uint32_t combMode       = (((uint32_t)howToMonitor & SCT_EV_CTRL_COMBMODE_MASK) >> SCT_EV_CTRL_COMBMODE_SHIFT);
    uint32_t currentCtrlVal = (uint32_t)howToMonitor;
    status_t status         = kStatus_Success;

    if (s_currentEvent < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS)
    {
        /* IO only mode */
        if (combMode == 0x2U)
        {
            base->EV[s_currentEvent].CTRL = currentCtrlVal | SCT_EV_CTRL_IOSEL(whichIO);
        }
        /* Match mode only */
        else if (combMode == 0x1U)
        {
            if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
            {
                currentCtrlVal |= SCT_EV_CTRL_MATCHSEL(s_currentMatch);
                /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */
                if (((base->CONFIG & SCT_CONFIG_UNIFY_MASK) != 0U) || (whichCounter == kSCTIMER_Counter_L))
                {
                    base->MATCH[s_currentMatch]    = SCT_MATCH_MATCHn_L(matchValue);
                    base->MATCHREL[s_currentMatch] = SCT_MATCHREL_RELOADn_L(matchValue);
                }
                else
                {
                    /* Select the counter, no need for this if operating in 32-bit mode */
                    currentCtrlVal |= SCT_EV_CTRL_HEVENT(whichCounter);
                    base->MATCH[s_currentMatch]    = SCT_MATCH_MATCHn_H(matchValue);
                    base->MATCHREL[s_currentMatch] = SCT_MATCHREL_RELOADn_H(matchValue);
                }
                base->EV[s_currentEvent].CTRL = currentCtrlVal;
                /* Increment the match register number */
                s_currentMatch++;
            }
            else
            {
                /* An error would occur if we have hit the limit in terms of number of match registers */
                status = kStatus_Fail;
            }
        }
        /* Use both Match & IO */
        else
        {
            if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
            {
                currentCtrlVal |= SCT_EV_CTRL_MATCHSEL(s_currentMatch) | SCT_EV_CTRL_IOSEL(whichIO);
                /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */
                if (((base->CONFIG & SCT_CONFIG_UNIFY_MASK) != 0U) || (whichCounter == kSCTIMER_Counter_L))
                {
                    base->MATCH[s_currentMatch]    = SCT_MATCH_MATCHn_L(matchValue);
                    base->MATCHREL[s_currentMatch] = SCT_MATCHREL_RELOADn_L(matchValue);
                }
                else
                {
                    /* Select the counter, no need for this if operating in 32-bit mode */
                    currentCtrlVal |= SCT_EV_CTRL_HEVENT(whichCounter);
                    base->MATCH[s_currentMatch]    = SCT_MATCH_MATCHn_H(matchValue);
                    base->MATCHREL[s_currentMatch] = SCT_MATCHREL_RELOADn_H(matchValue);
                }
                base->EV[s_currentEvent].CTRL = currentCtrlVal;
                /* Increment the match register number */
                s_currentMatch++;
            }
            else
            {
                /* An error would occur if we have hit the limit in terms of number of match registers */
                status = kStatus_Fail;
            }
        }

        if (kStatus_Success == status)
        {
            /* Enable the event in the current state */
            base->EV[s_currentEvent].STATE = (1UL << s_currentState);

            /* Return the event number */
            *event = s_currentEvent;

            /* Increment the event number */
            s_currentEvent++;
        }
    }
    else
    {
        /* An error would occur if we have hit the limit in terms of number of events created */
        status = kStatus_Fail;
    }

    return status;
}

/*!
 * brief Enable an event in the current state.
 *
 * This function will allow the event passed in to trigger in the current state. The event must
 * be created earlier by either calling the function SCTIMER_SetupPwm() or function
 * SCTIMER_CreateAndScheduleEvent() .
 *
 * param base  SCTimer peripheral base address
 * param event Event number to enable in the current state
 *
 */
void SCTIMER_ScheduleEvent(SCT_Type *base, uint32_t event)
{
    /* Enable event in the current state */
    base->EV[event].STATE |= (1UL << s_currentState);
}

/*!
 * brief Increase the state by 1
 *
 * All future events created by calling the function SCTIMER_ScheduleEvent() will be enabled in this new
 * state.
 *
 * param base  SCTimer peripheral base address
 *
 * return kStatus_Success on success
 *         kStatus_Error if we have hit the limit in terms of states used

 */
status_t SCTIMER_IncreaseState(SCT_Type *base)
{
    status_t status = kStatus_Success;

    /* Return an error if we have hit the limit in terms of states used */
    if (s_currentState >= (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_STATES)
    {
        status = kStatus_Fail;
    }
    else
    {
        s_currentState++;
    }

    return status;
}

/*!
 * brief Provides the current state
 *
 * User can use this to set the next state by calling the function SCTIMER_SetupNextStateAction().
 *
 * param base SCTimer peripheral base address
 *
 * return The current state
 */
uint32_t SCTIMER_GetCurrentState(SCT_Type *base)
{
    return s_currentState;
}

/*!
 * brief Toggle the output level.
 *
 * This change in the output level is triggered by the event number that is passed in by the user.
 *
 * param base    SCTimer peripheral base address
 * param whichIO The output to toggle
 * param event   Event number that will trigger the output change
 */
void SCTIMER_SetupOutputToggleAction(SCT_Type *base, uint32_t whichIO, uint32_t event)
{
    assert(whichIO < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_OUTPUTS);

    uint32_t reg;

    /* Set the same event to set and clear the output */
    base->OUT[whichIO].CLR |= (1UL << event);
    base->OUT[whichIO].SET |= (1UL << event);

    /* Set the conflict resolution to toggle output */
    reg = base->RES;
    reg &= ~(((uint32_t)SCT_RES_O0RES_MASK) << (2U * whichIO));
    reg |= ((uint32_t)(kSCTIMER_ResolveToggle)) << (2U * whichIO);
    base->RES = reg;
}

/*!
 * brief Setup capture of the counter value on trigger of a selected event
 *
 * param base            SCTimer peripheral base address
 * param whichCounter    SCTimer counter to use when operating in 16-bit mode. In 32-bit mode, this
 *                        field has no meaning as only the Counter_L bits are used.
 * param captureRegister Pointer to a variable where the capture register number will be returned. User
 *                        can read the captured value from this register when the specified event is triggered.
 * param event           Event number that will trigger the capture
 *
 * return kStatus_Success on success
 *         kStatus_Error if we have hit the limit in terms of number of match/capture registers available
 */
status_t SCTIMER_SetupCaptureAction(SCT_Type *base,
                                    sctimer_counter_t whichCounter,
                                    uint32_t *captureRegister,
                                    uint32_t event)
{
    status_t status;

    if (s_currentMatch < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_MATCH_CAPTURE)
    {
        /* Use Counter_L bits if counter is operating in 32-bit mode or user wants to setup the L counter */
        if (((base->CONFIG & SCT_CONFIG_UNIFY_MASK) != 0U) || (whichCounter == kSCTIMER_Counter_L))
        {
            /* Set the bit to enable event */
            base->CAPCTRL[s_currentMatch] |= SCT_CAPCTRL_CAPCONn_L(1UL << event);

            /* Set this resource to be a capture rather than match */
            base->REGMODE |= SCT_REGMODE_REGMOD_L(1UL << s_currentMatch);
        }
        else
        {
            /* Set bit to enable event */
            base->CAPCTRL[s_currentMatch] |= SCT_CAPCTRL_CAPCONn_H(1UL << event);

            /* Set this resource to be a capture rather than match */
            base->REGMODE |= SCT_REGMODE_REGMOD_H(1UL << s_currentMatch);
        }

        /* Return the match register number */
        *captureRegister = s_currentMatch;

        /* Increase the match register number */
        s_currentMatch++;

        status = kStatus_Success;
    }
    else
    {
        /* Return an error if we have hit the limit in terms of number of capture/match registers used */
        status = kStatus_Fail;
    }

    return status;
}

/*!
 * brief Receive noticification when the event trigger an interrupt.
 *
 * If the interrupt for the event is enabled by the user, then a callback can be registered
 * which will be invoked when the event is triggered
 *
 * param base     SCTimer peripheral base address
 * param event    Event number that will trigger the interrupt
 * param callback Function to invoke when the event is triggered
 */

void SCTIMER_SetCallback(SCT_Type *base, sctimer_event_callback_t callback, uint32_t event)
{
    s_eventCallback[event] = callback;
}

/*!
 * brief SCTimer interrupt handler.
 *
 * param base SCTimer peripheral base address.
 */
void SCTIMER_EventHandleIRQ(SCT_Type *base)
{
    uint32_t eventFlag = SCT0->EVFLAG;
    /* Only clear the flags whose interrupt field is enabled */
    uint32_t clearFlag = (eventFlag & SCT0->EVEN);
    uint32_t mask      = eventFlag;
    uint32_t i;

    /* Invoke the callback for certain events */
    for (i = 0; i < (uint32_t)FSL_FEATURE_SCT_NUMBER_OF_EVENTS; i++)
    {
        if ((mask & 0x1U) != 0U)
        {
            if (s_eventCallback[i] != NULL)
            {
                s_eventCallback[i]();
            }
        }
        mask >>= 1UL;

        if (0U == mask)
        {
            /* All events have been handled. */
            break;
        }
    }

    /* Clear event interrupt flag */
    SCT0->EVFLAG = clearFlag;
}

void SCT0_DriverIRQHandler(void)
{
    s_sctimerIsr(SCT0);
/* Add for ARM errata 838869, affects Cortex-M4, Cortex-M4F Store immediate overlapping
  exception return operation might vector to incorrect interrupt */
#if defined __CORTEX_M && (__CORTEX_M == 4U)
    __DSB();
#endif
}
