/*
 * Copyright 2019-2022 NXP.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include "fsl_wuu.h"

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

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

#define WUU_PE_REG_BIT_FIELD_MASK  0x03UL
#define WUU_PDC_REG_BIT_FIELD_MASK 0x03UL
#define WUU_PMC_REG_BIT_FIELD_MASK 0x01UL

#define WUU_ME_REG_WUME_FIELD_MASK 0x01UL
#define WUU_DE_REG_WUME_FIELD_MASK 0x01UL

#define WUU_FILT_REG_FILTE_FIELD_MASK   0x60U
#define WUU_FILT_REG_FILTSET_FIELD_MASK 0x1FU
#define WUU_FDC_REG_FILTC_FIELD_MASK    0x3U
#define WUU_FMC_REG_FILTM_FIELD_MASK    0x1U

#define WUU_CLEAR_BIT_FIELD_IN_REG(mask, offset) (~((uint32_t)(mask) << (offset)))
#define WUU_SET_BIT_FIELD_IN_REG(val, offset)    ((uint32_t)(val) << (offset))
/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

/*******************************************************************************
 * Code
 ******************************************************************************/

/*!
 * brief Enables and Configs External WakeUp Pins.
 *
 * This function enables/disables the external pin as wakeup input. What's more this
 * function configs pins options, including edge detection wakeup event and operate mode.
 *
 * param base     MUU peripheral base address.
 * param pinIndex The index of the external input pin. See Reference Manual for the details.
 * param config   Pointer to wuu_external_wakeup_pin_config_t structure.
 */
void WUU_SetExternalWakeUpPinsConfig(WUU_Type *base, uint8_t pinIndex, const wuu_external_wakeup_pin_config_t *config)
{
    assert(config != NULL);

    volatile uint32_t *edgeRegBase  = NULL;
    volatile uint32_t *eventRegBase = NULL;
    uint32_t edgeReg;
    uint32_t eventReg;
    uint32_t modeReg;
    uint8_t offset;

    /* Calculate offset. */
    offset = 2U * (pinIndex & 0xFU);

    if (config->edge != kWUU_ExternalPinDisable)
    {
        /* Based on pin index, get register base address. */
        if ((pinIndex >> 4U) != 0U)
        {
            edgeRegBase  = &base->PE2;
            eventRegBase = &base->PDC2;
        }
        else
        {
            edgeRegBase  = &base->PE1;
            eventRegBase = &base->PDC1;
        }

        /* Enable and config the edge detection. */
        edgeReg = *edgeRegBase;
        edgeReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_PE_REG_BIT_FIELD_MASK, offset);
        edgeReg |= WUU_SET_BIT_FIELD_IN_REG(config->edge, offset);
        *edgeRegBase = edgeReg;

        /* Config the wakeup event. */
        eventReg = *eventRegBase;
        eventReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_PDC_REG_BIT_FIELD_MASK, offset);
        eventReg |= WUU_SET_BIT_FIELD_IN_REG(config->event, offset);
        *eventRegBase = eventReg;

        /* Config operate mode. */
        modeReg = base->PMC;
        modeReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_PMC_REG_BIT_FIELD_MASK, pinIndex);
        modeReg |= WUU_SET_BIT_FIELD_IN_REG(config->mode, pinIndex);

        base->PMC = modeReg;
    }
    else
    {
        /* Based on pin index, get register base address. */
        if ((pinIndex >> 4U) != 0U)
        {
            edgeRegBase = &base->PE2;
        }
        else
        {
            edgeRegBase = &base->PE1;
        }

        edgeReg = *edgeRegBase;
        edgeReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_PE_REG_BIT_FIELD_MASK, offset);
        *edgeRegBase = edgeReg;
    }
}

/*!
 * brief Config Internal modules' event as the wake up soures.
 *
 * This function configs the internal modules event as the wake up sources.
 *
 * param base WUU peripheral base address.
 * param moduleIndex The selected internal module. See the Reference Manual for the details.
 * param event Select interrupt or DMA/Trigger of the internal module as the wake up source.
 */
void WUU_SetInternalWakeUpModulesConfig(WUU_Type *base, uint8_t moduleIndex, wuu_internal_wakeup_module_event_t event)
{
    switch (event)
    {
        case kWUU_InternalModuleInterrupt:
            base->ME |= WUU_SET_BIT_FIELD_IN_REG(WUU_ME_REG_WUME_FIELD_MASK, moduleIndex);
            break;
        case kWUU_InternalModuleDMATrigger:
            base->DE |= WUU_SET_BIT_FIELD_IN_REG(WUU_DE_REG_WUME_FIELD_MASK, moduleIndex);
            break;
        default:
            assert(false);
            break;
    }
}

/*!
 * brief Configs and Enables Pin filters.
 *
 * This function configs Pin filter, including pin select, filer operate mode
 * filer wakeup event and filter edge detection.
 *
 * param base WUU peripheral base address.
 * param filterIndex The index of the pin filer.
 * param config Pointer to wuu_pin_filter_config_t structure.
 */
void WUU_SetPinFilterConfig(WUU_Type *base, uint8_t filterIndex, const wuu_pin_filter_config_t *config)
{
    assert(config != NULL);

    uint8_t shift;
    uint32_t filterReg;
    uint32_t eventReg;
    uint32_t modeReg;

    shift     = (filterIndex - 1U) * 8U;
    filterReg = base->FILT;
    filterReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_FILT_REG_FILTE_FIELD_MASK, shift);
    filterReg |= WUU_SET_BIT_FIELD_IN_REG(config->edge, shift);

    if (config->edge != kWUU_FilterDisabled)
    {
        filterReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_FILT_REG_FILTSET_FIELD_MASK, shift);
        filterReg |= WUU_SET_BIT_FIELD_IN_REG(config->pinIndex, shift);
        shift = (filterIndex - 1U) * 2U;

        /* Config wake up event. */
        eventReg = base->FDC;
        eventReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_FDC_REG_FILTC_FIELD_MASK, shift);
        eventReg |= WUU_SET_BIT_FIELD_IN_REG(config->event, shift);
        base->FDC = eventReg;

        /* Config operate mode. */
        shift   = (filterIndex - 1U) * 1U;
        modeReg = base->FMC;
        modeReg &= WUU_CLEAR_BIT_FIELD_IN_REG(WUU_FMC_REG_FILTM_FIELD_MASK, shift);
        modeReg |= WUU_SET_BIT_FIELD_IN_REG(config->mode, shift);
        base->FMC = modeReg;
    }

    base->FILT = filterReg;
}

/*!
 * brief Gets the pin filter configuration.
 *
 * This function gets the pin filter flag.
 *
 * param base WUU peripheral base address.
 * param filterIndex A pin filter index, which starts from 1.
 * return True if the flag is a source of the existing low-leakage power mode.
 */
bool WUU_GetPinFilterFlag(WUU_Type *base, uint8_t filterIndex)
{
    bool ret = false;

    switch (filterIndex)
    {
        case 1:
            ret = ((base->FILT & WUU_FILT_FILTE1_MASK) != 0U);
            break;
        case 2:
            ret = ((base->FILT & WUU_FILT_FILTE2_MASK) != 0U);
            break;
        default:
            ret = false;
            break;
    }

    return ret;
}

/*!
 * brief Clears the pin filter configuration.
 *
 * This function clears the pin filter flag.
 *
 * param base WUU peripheral base address.
 * param filterIndex A pin filter index to clear the flag, starting from 1.
 */
void WUU_ClearPinFilterFlag(WUU_Type *base, uint8_t filterIndex)
{
    uint32_t reg;

    reg = base->FILT;
    /* Clean the W1C bits, in case the flags are cleared by mistake. */
    reg &= ~(WUU_FILT_FILTF1_MASK | WUU_FILT_FILTF2_MASK);

    reg |= WUU_SET_BIT_FIELD_IN_REG(WUU_FILT_FILTF1_MASK, ((filterIndex - 1U) * 8U));

    base->FILT = reg;
}

/*!
 * brief Gets the external wakeup source flag.
 *
 * This function checks the external pin flag to detect whether the MCU is
 * woken up by the specific pin.
 *
 * param base WUU peripheral base address.
 * param pinIndex     A pin index, which starts from 0.
 * return True if the specific pin is a wakeup source.
 */
bool WUU_GetExternalWakeupPinFlag(WUU_Type *base, uint32_t pinIndex)
{
    return (0U != (base->PF & (1UL << pinIndex)));
}

/*!
 * brief Clears the external wakeup source flag.
 *
 * This function clears the external wakeup source flag for a specific pin.
 *
 * param base WUU peripheral base address.
 * param pinIndex A pin index, which starts from 0.
 */
void WUU_ClearExternalWakeupPinFlag(WUU_Type *base, uint32_t pinIndex)
{
    base->PF = (1UL << pinIndex);
}
