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

#include "fsl_gpc.h"

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

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

/*******************************************************************************
 * Variables
 ******************************************************************************/
/*! @brief GPC submodule step registers offset */
static uint32_t const s_cmRegOffset[] = GPC_CM_STEP_REG_OFFSET;

static uint32_t const s_spRegOffset[] = GPC_SP_STEP_REG_OFFSET;

static uint32_t const s_stbyRegOffset[] = GPC_STBY_STEP_REG_OFFSET;

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

/*!
 * brief Enable IRQ wakeup request.
 *
 * This function enables the IRQ request which can wakeup the CPU platform.
 *
 * param base GPC CPU module base address.
 * param irqId ID of the IRQ, accessible range is 0-255.
 * param enable Enable the IRQ request or not.
 */
void GPC_CM_EnableIrqWakeup(GPC_CPU_MODE_CTRL_Type *base, uint32_t irqId, bool enable)
{
    assert(irqId < (GPC_CPU_MODE_CTRL_CM_IRQ_WAKEUP_MASK_COUNT * 32UL));

    uint32_t irqGroup = irqId / 32UL;
    uint32_t irqMask  = irqId % 32UL;

    if (true == enable)
    {
        base->CM_IRQ_WAKEUP_MASK[irqGroup] &= ~(1UL << irqMask);
    }
    else
    {
        base->CM_IRQ_WAKEUP_MASK[irqGroup] |= (1UL << irqMask);
    }
}

/*!
 * brief Get the status of the IRQ wakeup request.
 *
 * param base GPC CPU module base address.
 * param irqId ID of the IRQ, accessible range is 0-255.
 * return Indicate the IRQ request is asserted or not.
 */
bool GPC_CM_GetIrqWakeupStatus(GPC_CPU_MODE_CTRL_Type *base, uint32_t irqId)
{
    assert(irqId < (GPC_CPU_MODE_CTRL_CM_IRQ_WAKEUP_MASK_COUNT * 32UL));

    uint32_t irqGroup = irqId / 32UL;
    uint32_t irqMask  = irqId % 32UL;
    bool irqStatus    = false;

    irqStatus = (((base->CM_IRQ_WAKEUP_STAT[irqGroup] >> irqMask) & 0x1UL) == 0x1UL) ? true : false;
    return irqStatus;
}

/*!
 * brief Config the cpu mode transition step.
 *
 * note This function can not config the setpoint sleep/wakeup operation for those
 * operation is controlled by setpoint control. This funcion can not config the standby
 * sleep/wakeup too, because those operation is controlled by standby controlled.
 *
 * param base GPC CPU module base address.
 * param step step type, refer to "gpc_cm_tran_step_t".
 * param config transition step configuration, refer to "gpc_tran_step_config_t".
 */
void GPC_CM_ConfigCpuModeTransitionStep(GPC_CPU_MODE_CTRL_Type *base,
                                        gpc_cm_tran_step_t step,
                                        const gpc_tran_step_config_t *config)
{
    if (!((step >= kGPC_CM_SleepSP) && (step <= kGPC_CM_WakeupSP)))
    {
        uint32_t tmp32 = *(uint32_t *)((uint32_t)base + s_cmRegOffset[step]);

        if (config->enableStep)
        {
            tmp32 &= ~(GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_STEP_CNT_MASK |
                       GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_CNT_MODE_MASK);
            tmp32 |= GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_CNT_MODE(config->cntMode);
            if (config->cntMode != kGPC_StepCounterDisableMode)
            {
                tmp32 |= GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_STEP_CNT(config->stepCount);
            }
            tmp32 &= ~GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_DISABLE_MASK;
        }
        else
        {
            tmp32 |= GPC_CPU_MODE_CTRL_CM_SLEEP_SSAR_CTRL_DISABLE_MASK;
        }
        *(uint32_t *)((uint32_t)base + s_cmRegOffset[step]) = tmp32;
    }
}

/*!
 * brief Request a set point transition before the CPU transfers into a sleep mode.
 *
 * This function triggers the set point transition during a CPU Sleep/wakeup event and selects which one the CMC want
 * to transfer to.
 *
 * param base GPC CPU module base address.
 * param setPointSleep The set point CPU want the system to transit to on next CPU platform sleep sequence.
 * param setPointWakeup The set point CPU want the system to transit to on next CPU platform wakeup sequence.
 * param wakeupSel Select the set point transition on the next CPU platform wakeup sequence.
 */
void GPC_CM_RequestSleepModeSetPointTransition(GPC_CPU_MODE_CTRL_Type *base,
                                               uint8_t setPointSleep,
                                               uint8_t setPointWakeup,
                                               gpc_cm_wakeup_sp_sel_t wakeupSel)
{
    uint32_t tmp32 = base->CM_SP_CTRL;

    tmp32 &= ~(GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_RUN_EN_MASK | GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_SLEEP_MASK |
               GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_WAKEUP_MASK | GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_WAKEUP_SEL_MASK);
    /* Config set point transition in the next sleep sequence. */
    tmp32 |=
        GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_SLEEP_EN_MASK | GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_SLEEP(setPointSleep);
    /* Config set point transition in the next wakeup sequence. */
    tmp32 |= GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_WAKEUP_EN_MASK |
             GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_WAKEUP(setPointWakeup) |
             GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_WAKEUP_SEL(wakeupSel);

    base->CM_SP_CTRL = tmp32;
}

/*!
 * brief Request a set point transition during run mode.
 *
 * This function triggers the set point transition and selects which one the CMC want to transfer to.
 *
 * param base GPC CPU module base address.
 * param setPointRun The set point CPU want the system to transit in the run mode.
 */
void GPC_CM_RequestRunModeSetPointTransition(GPC_CPU_MODE_CTRL_Type *base, uint8_t setPointRun)
{
    uint32_t tmp32 = base->CM_SP_CTRL;

    tmp32 &= ~(GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_RUN_MASK);
    tmp32 |= GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_RUN_EN_MASK | GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_RUN(setPointRun);

    base->CM_SP_CTRL = tmp32;
    while ((base->CM_SP_CTRL & GPC_CPU_MODE_CTRL_CM_SP_CTRL_CPU_SP_RUN_EN_MASK) != 0UL)
    {
    }
}

/*
 * brief Set the set point mapping value for each cpu mode.
 *
 * This function configures which set point is allowed when CPU enters RUN/WAIT/STOP/SUSPEND. If there are multiple
 * setpoints, use:
 * code
 *    map = kkGPC_SetPoint0 | kGPC_SetPoint1 | ... | kGPC_SetPoint15;
 * encode
 *
 * param base GPC CPU module base address.
 * param mode CPU mode. Refer to "gpc_cpu_mode_t".
 * param map Map value of the set point. Refer to "_gpc_setpoint_map".
 */
void GPC_CM_SetCpuModeSetPointMapping(GPC_CPU_MODE_CTRL_Type *base, gpc_cpu_mode_t mode, uint32_t map)
{
    /* Ensure the allowed set point is in the accessible range (0-15). */
    map = map & 0xFFFFUL;

    switch (mode)
    {
        case kGPC_RunMode:
            base->CM_RUN_MODE_MAPPING = map;
            break;
        case kGPC_WaitMode:
            base->CM_WAIT_MODE_MAPPING = map;
            break;
        case kGPC_StopMode:
            base->CM_STOP_MODE_MAPPING = map;
            break;
        case kGPC_SuspendMode:
            base->CM_SUSPEND_MODE_MAPPING = map;
            break;
        default:
            assert(false);
            break;
    }
}

/*
 * brief Request the chip into standby mode.
 *
 * param base GPC CPU module base address.
 * param mode CPU mode. Refer to "gpc_cpu_mode_t".
 */
void GPC_CM_RequestStandbyMode(GPC_CPU_MODE_CTRL_Type *base, const gpc_cpu_mode_t mode)
{
    assert(mode != kGPC_RunMode);

    switch (mode)
    {
        case kGPC_WaitMode:
            base->CM_STBY_CTRL |= GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_WAIT_MASK;
            break;
        case kGPC_StopMode:
            base->CM_STBY_CTRL |= GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_STOP_MASK;
            break;
        case kGPC_SuspendMode:
            base->CM_STBY_CTRL |= GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_SUSPEND_MASK;
            break;
        default:
            /* This branch should never be hit. */
            break;
    }
}

/*
 * brief Clear the standby mode request.
 *
 * param base GPC CPU module base address.
 * param mode CPU mode. Refer to "gpc_cpu_mode_t".
 */
void GPC_CM_ClearStandbyModeRequest(GPC_CPU_MODE_CTRL_Type *base, const gpc_cpu_mode_t mode)
{
    assert(mode != kGPC_RunMode);

    switch (mode)
    {
        case kGPC_WaitMode:
            base->CM_STBY_CTRL &= ~GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_WAIT_MASK;
            break;
        case kGPC_StopMode:
            base->CM_STBY_CTRL &= ~GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_STOP_MASK;
            break;
        case kGPC_SuspendMode:
            base->CM_STBY_CTRL &= ~GPC_CPU_MODE_CTRL_CM_STBY_CTRL_STBY_SUSPEND_MASK;
            break;
        default:
            /* This branch should never be hit. */
            break;
    }
}

/*!
 * brief Clears CPU module interrut status flags.
 *
 * param base GPC CPU module base address.
 * param mask The interrupt status flags to be cleared. Should be the OR'ed value of _gpc_cm_interrupt_status_flag.
 */
void GPC_CM_ClearInterruptStatusFlags(GPC_CPU_MODE_CTRL_Type *base, uint32_t mask)
{
    uint32_t temp32;

    temp32 = base->CM_INT_CTRL;
    temp32 &= ~(GPC_CM_ALL_INTERRUPT_STATUS);
    base->CM_INT_CTRL = (mask | temp32);
}

/*!
 * brief Config the set point transition step.
 *
 * param base GPC Setpoint controller base address.
 * param step step type, refer to "gpc_sp_tran_step_t".
 * param config transition step configuration, refer to "gpc_tran_step_config_t".
 */
void GPC_SP_ConfigSetPointTransitionStep(GPC_SET_POINT_CTRL_Type *base,
                                         gpc_sp_tran_step_t step,
                                         const gpc_tran_step_config_t *config)
{
    uint32_t tmp32 = *(uint32_t *)((uint32_t)base + s_spRegOffset[step]);

    if (config->enableStep)
    {
        tmp32 &=
            ~(GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_STEP_CNT_MASK | GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_CNT_MODE_MASK);
        tmp32 |= GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_CNT_MODE(config->cntMode);
        if (config->cntMode != kGPC_StepCounterDisableMode)
        {
            tmp32 |= GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_STEP_CNT(config->stepCount);
        }
        tmp32 &= ~GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_DISABLE_MASK;
    }
    else
    {
        tmp32 |= GPC_SET_POINT_CTRL_SP_SSAR_SAVE_CTRL_DISABLE_MASK;
    }
    *(uint32_t *)((uint32_t)base + s_spRegOffset[step]) = tmp32;
}

/*!
 * brief Config the standby transition step.
 *
 * param base GPC Setpoint controller base address.
 * param step step type, refer to "gpc_stby_tran_step_t".
 * param config transition step configuration, refer to "gpc_tran_step_config_t".
 */
void GPC_STBY_ConfigStandbyTransitionStep(GPC_STBY_CTRL_Type *base,
                                          gpc_stby_tran_step_t step,
                                          const gpc_tran_step_config_t *config)
{
    uint32_t tmp32 = *(uint32_t *)((uint32_t)base + s_stbyRegOffset[step]);

    if (config->enableStep)
    {
        tmp32 &= ~(GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_STEP_CNT_MASK | GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_CNT_MODE_MASK);
        tmp32 |= GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_CNT_MODE(config->cntMode);
        if (config->cntMode != kGPC_StepCounterDisableMode)
        {
            tmp32 |= GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_STEP_CNT(config->stepCount);
        }
        tmp32 &= ~GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_DISABLE_MASK;
    }
    else
    {
        tmp32 |= GPC_STBY_CTRL_STBY_LPCG_IN_CTRL_DISABLE_MASK;
    }
    *(uint32_t *)((uint32_t)base + s_stbyRegOffset[step]) = tmp32;
}
