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

#include "fsl_opamp.h"

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

#if defined(OPAMP_RSTS)
#define OPAMP_RESETS_ARRAY OPAMP_RSTS
#endif

/*******************************************************************************
 * Prototypes
 ******************************************************************************/
static uint32_t OPAMP_GetInstance(OPAMP_Type *base);

/*******************************************************************************
 * Variables
 ******************************************************************************/
static OPAMP_Type *const s_opampBases[] = OPAMP_BASE_PTRS;

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

#if defined(OPAMP_RESETS_ARRAY)
/* Reset array */
static const reset_ip_name_t s_opampResets[] = OPAMP_RESETS_ARRAY;
#endif

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

static uint32_t OPAMP_GetInstance(OPAMP_Type *base)
{
    uint32_t instance;

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

    assert(instance < ARRAY_SIZE(s_opampBases));

    return instance;
}

/*!
 * brief Initialize OPAMP instance.
 *
 * param base OPAMP peripheral base address.
 * param config The pointer to opamp_config_t.
 */
void OPAMP_Init(OPAMP_Type *base, const opamp_config_t *config)
{
    assert(config != NULL);

    uint32_t tmp32 = 0U;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Enable the clock. */
    CLOCK_EnableClock(s_opampClocks[OPAMP_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */

#if defined(OPAMP_RESETS_ARRAY)
    RESET_ReleasePeripheralReset(s_opampResets[OPAMP_GetInstance(base)]);
#endif

    tmp32 = OPAMP_OPAMP_CTR_EN(config->enable) | OPAMP_OPAMP_CTR_MODE(config->mode) |
            OPAMP_OPAMP_CTR_BIASC(config->trimOption) | OPAMP_OPAMP_CTR_INTREF(config->intRefVoltage) |
            OPAMP_OPAMP_CTR_PREF(config->posRefVoltage) |
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW
            OPAMP_OPAMP_CTR_OUTSW(config->enableOutputSwitch) |
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1
            OPAMP_OPAMP_CTR_ADCSW1(config->enablePosADCSw1) |
#else
            OPAMP_OPAMP_CTR_ADCSW(config->enablePosADCSw) |
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1 */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2
            OPAMP_OPAMP_CTR_ADCSW2(config->enablePosADCSw2) |
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2 */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL
            OPAMP_OPAMP_CTR_INPSEL(config->PosInputChannelSelection) |
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD
            OPAMP_OPAMP_CTR_TRIGMD(config->enableTriggerMode) |
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD */
            OPAMP_OPAMP_CTR_NGAIN(config->negGain);
    base->OPAMP_CTR = tmp32;
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_BUFEN) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_BUFEN
    if (config->enableRefBuffer)
    {
        assert((uint32_t)(config->posGain) <= 7UL);
        base->OPAMP_CTR |= OPAMP_OPAMP_CTR_PGAIN(config->posGain) | OPAMP_OPAMP_CTR_BUFEN_MASK;
    }
    else
    {
        if ((uint32_t)(config->posGain) > 7UL)
        {
            base->OPAMP_CTR |= OPAMP_OPAMP_CTR_PGAIN((uint32_t)(config->posGain) - 7UL);
        }
        else
        {
            assert((uint32_t)(config->posGain) == 0UL);
            base->OPAMP_CTR |= OPAMP_OPAMP_CTR_PGAIN(config->posGain);
        }
    }
#else
    base->OPAMP_CTR |= OPAMP_OPAMP_CTR_PGAIN(config->posGain);
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD */
}

/*!
 * brief De-initialize OPAMP instance.
 *
 * param base OPAMP peripheral base address.
 */
void OPAMP_Deinit(OPAMP_Type *base)
{
    /* Disable OPAMP instance. */
    base->OPAMP_CTR &= ~OPAMP_OPAMP_CTR_EN_MASK;

#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL)
    /* Disable the clock. */
    CLOCK_DisableClock(s_opampClocks[OPAMP_GetInstance(base)]);
#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */
}

/*!
 * brief Get default configuration of OPAMP.
 *
 * code
 *     config->enable        = false;
 *     config->mode          = kOPAMP_LowNoiseMode;
 *     config->trimOption    = kOPAMP_TrimOptionDefault;
 *     config->intRefVoltage = kOPAMP_IntRefVoltVddaDiv2;
 *     config->enablePosADCSw = false;
 *     config->posRefVoltage = kOPAMP_PosRefVoltVrefh3;
 *     config->posGain       = kOPAMP_PosGainReserved;
 *     config->negGain       = kOPAMP_NegGainBufferMode;
 * endcode
 *
 * param config The pointer to opamp_config_t.
 */
void OPAMP_GetDefaultConfig(opamp_config_t *config)
{
    assert(config != NULL);

    config->enable        = false;
    config->mode          = kOPAMP_LowNoiseMode;
    config->trimOption    = kOPAMP_TrimOptionDefault;
    config->intRefVoltage = kOPAMP_IntRefVoltVddaDiv2;
    config->posRefVoltage = kOPAMP_PosRefVoltVrefh3;
    config->posGain       = kOPAMP_PosGainReserved;
    config->negGain       = kOPAMP_NegGainBufferMode;
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW
    config->enableOutputSwitch = true;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_OUTSW */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1
    config->enablePosADCSw1 = false;
#else
    config->enablePosADCSw = false;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW1 */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2
    config->enablePosADCSw2 = false;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_ADCSW2 */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_BUFEN) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_BUFEN
    config->enableRefBuffer = false;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_BUFEN */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL
    config->PosInputChannelSelection = kOPAMP_PosInputChannel0;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_INPSEL */
#if defined(FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD) && FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD
    config->enableTriggerMode = false;
#endif /* FSL_FEATURE_OPAMP_HAS_OPAMP_CTR_TRIGMD */
}
