/*
 * Copyright (c) 2013 - 2014, Freescale Semiconductor, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * o Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * o Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <assert.h>
#include "fsl_adc_driver.h"
#include "fsl_adc_hal.h"
#include "fsl_adc_common.h"
#include "fsl_clock_manager.h"
#include "fsl_interrupt_manager.h"

/* Define MACRO for formatting the ADC result value. */
#define ADC_CONV_SIGN32_MASK    (0x80000000U)
#if (FSL_FEATURE_ADC_MAX_RESOLUTION >= 16)
#define ADC_CONV_DATA16_MASK    (0xFFFFU)
#define ADC_CONV_DATA15_MASK    (0x7FFFU)
#define ADC_CONV_SIGN15_MASK    (0x8000U)
#endif /* FSL_FEATURE_ADC_MAX_RESOLUTION */
#define ADC_CONV_DATA12_MASK    (0x0FFFU)
#define ADC_CONV_SIGN12_MASK    (0xF000U)
#define ADC_CONV_DATA10_MASK    (0x03FFU)
#define ADC_CONV_SIGN10_MASK    (0xFC00U)
#define ADC_CONV_DATA8_MASK     (0x00FFU)
#define ADC_CONV_SIGN8_MASK     (0xFF00U)

static adc_state_t *volatile g_adcStatePtr[HW_ADC_INSTANCE_COUNT];

#if FSL_FEATURE_ADC_HAS_CALIBRATION

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_GetAutoCalibrationParam
 * Description   : Execute the the process of auto calibration and fetch
 * the calibration parameters that would be kept in "adc_calibration_param_t"
 * type variable. When executing the process of auto calibration, the ADC
 * has been configured internally to work in the situation with highest
 * precision. Since this API may be called before the initialization, it enables
 * the ADC clock internally.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_GetAutoCalibrationParam(uint32_t instance, adc_calibration_param_t *paramPtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);

    uint16_t dummy = 0U;
    uint32_t baseAddr = g_adcBaseAddr[instance];
    
    dummy = dummy; /* Avoid warning when in compiling. */
    if (!paramPtr)
    {
        return kStatus_ADC_InvalidArgument;
    }
    /* Special configuration for highest accuracy. */
    /* Enable clock for ADC. */
    if (!CLOCK_SYS_GetAdcGateCmd(instance))
    {
        CLOCK_SYS_EnableAdcClock(instance);
    }

    /* Maximum Average. */
#if FSL_FEATURE_ADC_HAS_HW_AVERAGE
    ADC_HAL_SetHwAverageCmd(baseAddr, true);
    ADC_HAL_SetHwAverageMode(baseAddr, kAdcHwAverageCountOf32);
#endif /* FSL_FEATURE_ADC_HAS_HW_AVERAGE */

    /* Lowest ADC Frequency. */
    ADC_HAL_SetClkSrcMode(baseAddr, kAdcClkSrcOfBusClk);
    ADC_HAL_SetClkDividerMode(baseAddr, kAdcClkDividerInputOf8);

    /* Reference voltage as Vadd. */
    ADC_HAL_SetRefVoltSrcMode(baseAddr, kAdcRefVoltSrcOfVref);

    /* Software trigger. */
    ADC_HAL_SetHwTriggerCmd(baseAddr, false);

    ADC_HAL_SetAutoCalibrationCmd(baseAddr, true); /* Launch auto calibration. */
    while ( !ADC_HAL_GetChnConvCompletedCmd(baseAddr, 0U) )
    {
        if ( ADC_HAL_GetAutoCalibrationFailedCmd(baseAddr) )
        {
            return kStatus_ADC_Failed;
        }
    }
    /* Read parameters that generated by auto calibration. */
#if FSL_FEATURE_ADC_HAS_OFFSET_CORRECTION
    paramPtr->offsetValue = ADC_HAL_GetOffsetValue(baseAddr);
#endif /* FSL_FEATURE_ADC_HAS_OFFSET_CORRECTION */
    paramPtr->plusSideGainValue = ADC_HAL_GetAutoPlusSideGainValue(baseAddr);
#if FSL_FEATURE_ADC_HAS_DIFF_MODE
    paramPtr->minusSideGainValue = ADC_HAL_GetAutoMinusSideGainValue(baseAddr);
#endif /* FSL_FEATURE_ADC_HAS_DIFF_MODE */
    dummy = ADC_HAL_GetChnConvValueRAW(baseAddr, 0U);
    
    ADC_HAL_SetAutoCalibrationCmd(baseAddr, false); /* Terminate auto calibration. */

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_SetCalibrationParam
 * Description   : Set the calibration parameters for ADC module. These
 * parameters can be fetched by launching the process of auto calibration
 * or to use some predefined values that filled in the structure of 
 * "adc_calibration_param_t". For higher precision,  it is recommended to
 * execute the process of calibration before initializing the ADC module.
 * Since this API may be called before the initialization, it enables the ADC
 * clock internally.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_SetCalibrationParam(uint32_t instance, adc_calibration_param_t *paramPtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);

    uint32_t baseAddr = g_adcBaseAddr[instance];
    
    if (!paramPtr)
    {
        return kStatus_ADC_InvalidArgument;
    }
    /* Special configuration for highest accuracy. */
    /* Enable clock for ADC. */
    if (!CLOCK_SYS_GetAdcGateCmd(instance))
    {
        CLOCK_SYS_EnableAdcClock(instance);
    }

#if FSL_FEATURE_ADC_HAS_OFFSET_CORRECTION
    ADC_HAL_SetOffsetValue(baseAddr, paramPtr->offsetValue);
#endif /* FSL_FEATURE_ADC_HAS_OFFSET_CORRECTION */
    ADC_HAL_SetPlusSideGainValue(baseAddr, paramPtr->plusSideGainValue);
#if FSL_FEATURE_ADC_HAS_DIFF_MODE
    ADC_HAL_SetMinusSideGainValue(baseAddr, paramPtr->minusSideGainValue);
#endif /* FSL_FEATURE_ADC_HAS_DIFF_MODE */

    return kStatus_ADC_Success;
}

#endif /* FSL_FEATURE_ADC_HAS_CALIBRATION */

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_StructInitUserConfigForIntMode
 * Description   : Fill the initial user configuration for interrupt mode.
 * Then call initialization function with the filled parameter would configure
 * the ADC module work as the interrupt mode.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_StructInitUserConfigForIntMode(adc_user_config_t *userConfigPtr)
{
    if ( !userConfigPtr )
    {
        return kStatus_ADC_InvalidArgument;
    }

    userConfigPtr->intEnable = true;
    userConfigPtr->lowPowerEnable = true;
    userConfigPtr->clkDividerMode = kAdcClkDividerInputOf8;
    userConfigPtr->resolutionMode = kAdcResolutionBitOf12or13;
    userConfigPtr->clkSrcMode = kAdcClkSrcOfAsynClk;
    userConfigPtr->asyncClkEnable = true;
    userConfigPtr->highSpeedEnable = false;
    userConfigPtr->hwTriggerEnable = false;
#if FSL_FEATURE_ADC_HAS_DMA
    userConfigPtr->dmaEnable = false;
#endif /* FSL_FEATURE_ADC_HAS_DMA */
    userConfigPtr->refVoltSrcMode = kAdcRefVoltSrcOfVref;
    userConfigPtr->continuousConvEnable = true;

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_StructInitUserConfigForBlockingMode
 * Description   : Fill the initial user configuration for blocking mode.
 * Then call initialization function with the filled parameter would configure
 * the ADC module work as the blocking mode.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_StructInitUserConfigForBlockingMode(adc_user_config_t *userConfigPtr)
{
    if ( !userConfigPtr )
    {
        return kStatus_ADC_InvalidArgument;
    }

    userConfigPtr->intEnable = false;
    userConfigPtr->lowPowerEnable = false;
    userConfigPtr->clkDividerMode = kAdcClkDividerInputOf8;
    userConfigPtr->resolutionMode = kAdcResolutionBitOf12or13;
    userConfigPtr->clkSrcMode = kAdcClkSrcOfBusClk;
    userConfigPtr->asyncClkEnable = false;
    userConfigPtr->highSpeedEnable = false;
    userConfigPtr->hwTriggerEnable = false;
#if FSL_FEATURE_ADC_HAS_DMA
    userConfigPtr->dmaEnable = false;
#endif /* FSL_FEATURE_ADC_HAS_DMA */
    userConfigPtr->refVoltSrcMode = kAdcRefVoltSrcOfVref;
    userConfigPtr->continuousConvEnable = true;

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_StructInitUserConfigForOneTimeTriggerMode
 * Description   : Fill the initial user configuration for one time trigger mode.
 * Then call initialization function with the filled parameter would configure
 * the ADC module work as the one time trigger mode.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_StructInitUserConfigForOneTimeTriggerMode(adc_user_config_t *userConfigPtr)
{
    if ( !userConfigPtr )
    {
        return kStatus_ADC_InvalidArgument;
    }

    userConfigPtr->intEnable = false;
    userConfigPtr->lowPowerEnable = false;
    userConfigPtr->clkDividerMode = kAdcClkDividerInputOf8;
    userConfigPtr->resolutionMode = kAdcResolutionBitOf12or13;
    userConfigPtr->clkSrcMode = kAdcClkSrcOfBusClk;
    userConfigPtr->asyncClkEnable = false;
    userConfigPtr->highSpeedEnable = false;
    userConfigPtr->hwTriggerEnable = false;
#if FSL_FEATURE_ADC_HAS_DMA
    userConfigPtr->dmaEnable = false;
#endif /* FSL_FEATURE_ADC_HAS_DMA */
    userConfigPtr->refVoltSrcMode = kAdcRefVoltSrcOfVref;
    userConfigPtr->continuousConvEnable = false;

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_Init
 * Description   : Initialize the comparator in ADC module. No mater if the
 * calibration has been done for the device, this API with initial configuration
 * should be called before any other operations to the ADC module. In fact,
 * these initial configure are mainly for the comparator itself. For advanced
 * feature, responding APIs would be called after this function.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_Init(uint32_t instance, adc_user_config_t *userConfigPtr, adc_state_t *userStatePtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);

    uint32_t baseAddr = g_adcBaseAddr[instance];

    if ( (!userConfigPtr) || (!userStatePtr) )
    {
        return kStatus_ADC_InvalidArgument;
    }
    /* Enable clock for ADC. */
    if (!CLOCK_SYS_GetAdcGateCmd(instance))
    {
        CLOCK_SYS_EnableAdcClock(instance);
    }

    /* Reset all the register to a known state. */
    ADC_HAL_Init(baseAddr);

    /* Low power. */
    ADC_HAL_SetLowPowerCmd(baseAddr, userConfigPtr->lowPowerEnable);

    /* Clock divider. */
    ADC_HAL_SetClkDividerMode(baseAddr, userConfigPtr->clkDividerMode);

    /* Conversion resolution mode. */
    ADC_HAL_SetResolutionMode(baseAddr, userConfigPtr->resolutionMode);

    /* Input clock select. */
    ADC_HAL_SetClkSrcMode(baseAddr, userConfigPtr->clkSrcMode);

    /* Asynchronous clock output. */
    ADC_HAL_SetAsyncClkCmd(baseAddr, userConfigPtr->asyncClkEnable);

    /* High speed configuration. */
    ADC_HAL_SetHighSpeedCmd(baseAddr, userConfigPtr->highSpeedEnable);

    /* Conversion trigger selection. */
    ADC_HAL_SetHwTriggerCmd(baseAddr, userConfigPtr->hwTriggerEnable);

#if FSL_FEATURE_ADC_HAS_DMA
    /* DMA. */
    ADC_HAL_SetDmaCmd(baseAddr, userConfigPtr->dmaEnable);
#endif /* FSL_FEATURE_ADC_HAS_DMA */

    /* Voltage reference selection. */
    ADC_HAL_SetRefVoltSrcMode(baseAddr, userConfigPtr->refVoltSrcMode);

    /* Continuous conversion. */
    ADC_HAL_SetContinuousConvCmd(baseAddr, userConfigPtr->continuousConvEnable);

    /* Internal context. */
    g_adcStatePtr[instance] = userStatePtr;

    /* Interrupt. */
    if (userConfigPtr->intEnable)
    {
        /* Enable ADC interrupt in NVIC level.*/
        INT_SYS_EnableIRQ(g_adcIrqId[instance] );
    }
    else
    {
        /* Disable ADC interrupt in NVIC level.*/
        INT_SYS_DisableIRQ(g_adcIrqId[instance] );
    }

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_Deinit
 * Description   : De-initialize the comparator in ADC module. It will gate
 * the clock to ADC module. When ADC is no long used in application, calling
 * this API will shut down the device to reduce power consumption.
 *
 *END*************************************************************************/
void ADC_DRV_Deinit(uint32_t instance)
{
    /* Disable ADC interrupt in NVIC level. */
    INT_SYS_DisableIRQ( g_adcIrqId[instance] );

    /* Disable clock for ADC. */
    CLOCK_SYS_DisableAdcClock(instance);
    
    /* Reset the pointer to context structure. */
    g_adcStatePtr[instance] = NULL;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_EnableLongSample
 * Description   : Initialize the feature of long sample mode in ADC module.
 * This API would enable the feature of long sample mode and set it with
 * indicated configuration. Launch the long sample mode would adjust the sample 
 * period to allow higher impedance inputs to be accurately sampled.
 *
 *END*************************************************************************/
void ADC_DRV_EnableLongSample(uint32_t instance, adc_long_sample_cycle_mode_t mode)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    ADC_HAL_SetLongSampleCmd(baseAddr, true);
    ADC_HAL_SetLongSampleCycleMode(baseAddr, mode);
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_DisableLongSample
 * Description   : De-initialize the feature of long sample mode in
 * ADC module. This API will disable the the feature of long sample mode, and
 * then, the ADC module would be working without long sample mode.
 *
 *END*************************************************************************/
void ADC_DRV_DisableLongSample(uint32_t instance)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    ADC_HAL_SetLongSampleCmd(baseAddr, false);
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_EnableHwCmp
 * Description   : Initialize the feature of long sample mode in ADC
 * module. This API would enable the feature of hardware compare with
 * indicated configuration. Launch the hardware compare would make the
 * conversion result in predefined range can be only accepted. Values out of
 * range would be ignored during conversion.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_EnableHwCmp(uint32_t instance, adc_hw_cmp_config_t *hwCmpConfigPtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    if (!hwCmpConfigPtr)
    {
        return kStatus_ADC_InvalidArgument;
    }

    /* Enable CMP feature. */
    ADC_HAL_SetHwCmpCmd(baseAddr, true);

    /* Set CMP range mode. */
    ADC_HAL_SetHwCmpMode(baseAddr, hwCmpConfigPtr->cmpRangeMode);

    /* Set CMP values. */
    ADC_HAL_SetHwCmpValue1(baseAddr, hwCmpConfigPtr->cmpValue1);
    ADC_HAL_SetHwCmpValue2(baseAddr, hwCmpConfigPtr->cmpValue2);
    
    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_DisableHwCmp
 * Description   : De-initialize the feature of hardware compare in
 * ADC module. This API will disable the the feature of hardware compare
 * and then, the ADC module would be working without hardware compare.
 *
 *END*************************************************************************/
void ADC_DRV_DisableHwCmp(uint32_t instance)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    /* Disable CMP feature. */
    ADC_HAL_SetHwCmpCmd(baseAddr, false);
}

#if FSL_FEATURE_ADC_HAS_HW_AVERAGE

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_EnableHwAverage
 * Description   : Initialize the feature of hardware average in ADC
 * module. This API would enable the feature of hardware average with
 * indicated configuration. Launch the hardware average would perform a hardware
 * average of multiple conversions. It would help to improve the stability of
 * conversion result.
 *
 *END*************************************************************************/
void ADC_DRV_EnableHwAverage(uint32_t instance, adc_hw_average_count_mode_t mode)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    /* Enable Average feature. */
    ADC_HAL_SetHwAverageCmd(baseAddr, true);
    /* Set Average mode. */
    ADC_HAL_SetHwAverageMode(baseAddr, mode);
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_DisableHwAverage
 * Description   : De-initialize the feature of hardware average in
 * ADC module. This API will disable the the feature of hardware average,
 * and then, the ADC module would be working without hardware average.
 *
 *END*************************************************************************/
void ADC_DRV_DisableHwAverage(uint32_t instance)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    /* Disable Average feature. */
    ADC_HAL_SetHwAverageCmd(baseAddr, false);
}

#endif /* FSL_FEATURE_ADC_HAS_HW_AVERAGE */

#if FSL_FEATURE_ADC_HAS_PGA

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_EnablePGA
 * Description   : Enable the feature of Programmable Gain Amplifier 
 * (PGA) in ADC module. This API would enable the feature of PGA with indicated
 * configuration. The PGA is designed to increase the dynamic range by
 * amplifying low-amplitude signals before they are fed into the ADC module.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_EnablePGA(uint32_t instance, adc_pga_config_t *pgaConfigPtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    if ( !pgaConfigPtr)
    {
        return kStatus_ADC_InvalidArgument;
    }
    /* Enable PGA feature. */
    ADC_HAL_SetPgaCmd(baseAddr, true);

    /* Chopping control. */
    ADC_HAL_SetPgaChoppingCmd(baseAddr, pgaConfigPtr->choppingEnable);

    /* Low power mode. */
    ADC_HAL_SetPgaLowPowerCmd(baseAddr, pgaConfigPtr->runInLowPowerModeEnable);

    /* Gain setting. */
    ADC_HAL_SetPgaGainMode(baseAddr, pgaConfigPtr->gainValue);

    /* Offset measurement. */
    ADC_HAL_SetPgaOffsetMeasurementCmd(baseAddr, pgaConfigPtr->offsetMeasurementEnable);

    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_DisablePGA
 * Description   : Disable the feature of hardware average in
 * ADC module. This API will disable the the feature of PGA. And then, the ADC
 * module would be working without PGA.
 *
 *END*************************************************************************/
void ADC_DRV_DisablePGA(uint32_t instance)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    /* Disable PGA feature. */
    ADC_HAL_SetPgaCmd(baseAddr, false);
}

#endif /* FSL_FEATURE_ADC_HAS_PGA */

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_ConfigConvChn
 * Description   : Configure the conversion channel. When ADC module has
 * been initialized by enabling software trigger (disable hardware trigger),
 * calling this API will trigger the conversion.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_ConfigConvChn(uint32_t instance,
    uint32_t chnGroup, adc_chn_config_t *chnConfigPtr)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    if (!chnConfigPtr)
    {
        return kStatus_ADC_InvalidArgument;
    }
#if FSL_FEATURE_ADC_HAS_MUX_SELECT
    if (chnConfigPtr->chnMux!= ADC_HAL_GetChnMuxMode(baseAddr) )
    {
        ADC_HAL_SetChnMuxMode(baseAddr, chnConfigPtr->chnMux);
    }
#endif /* FSL_FEATURE_ADC_HAS_MUX_SELECT */
    ADC_HAL_ConfigChn(baseAddr, chnGroup,
        chnConfigPtr->intEnable, chnConfigPtr->diffEnable, chnConfigPtr->chnNum);
    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_WaitConvDone
 * Description   : Wait the latest conversion for its complete. When
 * trigger the conversion by configuring the available channel, the converter
 * would launch to work, this API should be called to wait for the conversion's
 * complete when no interrupt or DMA mode is used for ADC module. After the
 * waiting, the available data of conversion result could be fetched then. 
 * The complete flag would not be cleared until the result data would be read.
 *
 *END*************************************************************************/
void ADC_DRV_WaitConvDone(uint32_t instance, uint32_t chnGroup)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    while ( !ADC_HAL_GetChnConvCompletedCmd(baseAddr, chnGroup) )
    {}
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_PauseConv
 * Description   : Pause current conversion setting by software.
 *
 *END*************************************************************************/
void ADC_DRV_PauseConv(uint32_t instance, uint32_t chnGroup)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    ADC_HAL_ConfigChn(baseAddr, chnGroup, false, false, 0x1F);
}


/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_GetConvValueRAW
 * Description   : Get the conversion value from the ADC module.
 *
 *END*************************************************************************/
uint16_t ADC_DRV_GetConvValueRAW(uint32_t instance, uint32_t chnGroup)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];

    return ADC_HAL_GetChnConvValueRAW(baseAddr, chnGroup);
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_GetConvValueRAWInt
 * Description   : Get the latest conversion value in buffer when using
 * interrupt mode. Note that this function is only available in interrupt mode.
 *
 *END*************************************************************************/
uint16_t ADC_DRV_GetConvValueRAWInt(uint32_t instance, uint32_t chnGroup)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    assert(instance < FSL_FEATURE_ADC_CONVERSION_CONTROL_COUNT);
    return g_adcStatePtr[instance]->configGroup[chnGroup].convBuff;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_ConvRAWData
 * Description   : Format the initial value fetched from ADC module. 
 * Initial value fetched from ADC module can not be read as an normal number,
 * especially for signed value generated by differential conversion. This
 * function can format the initial value to be a readable one.
 *
 *END*************************************************************************/
int32_t ADC_DRV_ConvRAWData(uint16_t convValue, bool diffEnable, adc_resolution_mode_t mode)
{
    uint32_t ret32 = 0U;
    switch (mode)
    {
#if (FSL_FEATURE_ADC_MAX_RESOLUTION >= 16)
    case kAdcResolutionBitOf16:
        if (!diffEnable)
        {
            ret32 = convValue & ADC_CONV_DATA16_MASK; /* Get Data. */
        }
        else
        {
            ret32 = convValue & ADC_CONV_DATA15_MASK; /* Get Data. */
            ret32 |= ( (convValue & ADC_CONV_SIGN15_MASK) ? ADC_CONV_SIGN32_MASK:0); /* Get sign. */
        }
        break;
#endif /* FSL_FEATURE_ADC_MAX_RESOLUTION */
    case kAdcResolutionBitOf12or13:
        /* Get Data. */
        ret32 = convValue & ADC_CONV_DATA12_MASK;
        if (diffEnable)

        {
            ret32 |= ( (convValue & ADC_CONV_SIGN12_MASK) ? ADC_CONV_SIGN32_MASK:0); /* Get sign. */
        }
        break;
    case kAdcResolutionBitOf10or11:
        /* Get Data. */
        ret32 = convValue & ADC_CONV_DATA10_MASK;
        if (diffEnable)
        {
            ret32 |= ( (convValue & ADC_CONV_SIGN10_MASK) ? ADC_CONV_SIGN32_MASK:0); /* Get sign. */
        }
        break;
    case kAdcResolutionBitOf8or9:
        /* Get Data. */
        ret32 = convValue & ADC_CONV_DATA8_MASK;
        if (diffEnable)
        {
            ret32 |= ( (convValue & ADC_CONV_SIGN8_MASK) ? ADC_CONV_SIGN32_MASK:0); /* Get sign. */
        }
        break;
    default:
        break;    
    }
    return (int32_t)(ret32);
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_InstallCallback
 * Description   : Install the user-defined callback in ADC module.
 * When an ADC interrupt request is served, the callback will be executed 
 * inside the ISR.
 *
 *END*************************************************************************/
adc_status_t ADC_DRV_InstallCallback(uint32_t instance, uint32_t chnGroup, adc_callback_t userCallback)
{
    assert (instance < HW_ADC_INSTANCE_COUNT);
    if ( !g_adcStatePtr[instance] )
    {
        return kStatus_ADC_Failed;
    }
    /* Fill callback function into state structure. */
    g_adcStatePtr[instance]->configGroup[chnGroup].userCallbackFunc = userCallback;
    
    return kStatus_ADC_Success;
}

/*FUNCTION*********************************************************************
 *
 * Function Name : ADC_DRV_IRQHandler
 * Description   : The driver-defined ISR in ADC module. 
 * It includes the process for interrupt mode defined by driver. Currently, it
 * will be called inside the system-defined ISR.
 *
 *END*************************************************************************/
void ADC_DRV_IRQHandler(uint32_t instance)
{
    assert(instance < HW_ADC_INSTANCE_COUNT);
    uint32_t baseAddr = g_adcBaseAddr[instance];
    uint32_t chnGroupIndex = 0U;
    if ( g_adcStatePtr[instance] )
    {
        while (chnGroupIndex < FSL_FEATURE_ADC_CONVERSION_CONTROL_COUNT)
        {
            if ( ADC_HAL_GetChnConvCompletedCmd(baseAddr, chnGroupIndex))
            {
                /* Move conversion data from register to buffer. */
                g_adcStatePtr[instance]->configGroup[chnGroupIndex].convBuff = 
                    ADC_HAL_GetChnConvValueRAW(baseAddr, chnGroupIndex);
                /* Execute user-defined callback function. */
                if (g_adcStatePtr[instance]->configGroup[chnGroupIndex].userCallbackFunc)
                {
                    
                    (*(g_adcStatePtr[instance]->configGroup[chnGroupIndex].userCallbackFunc))();
                }
            }
            chnGroupIndex++;
        }
    }
}

/******************************************************************************
 * EOF
 *****************************************************************************/

