/* Copyright Statement:
 *
 * This software/firmware and related documentation ("AutoChips Software") are
 * protected under relevant copyright laws. The information contained herein is
 * confidential and proprietary to AutoChips Inc. and/or its licensors. Without
 * the prior written permission of AutoChips inc. and/or its licensors, any
 * reproduction, modification, use or disclosure of AutoChips Software, and
 * information contained herein, in whole or in part, shall be strictly
 * prohibited.
 *
 * AutoChips Inc. (C) 2022. All rights reserved.
 *
 * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES
 * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("AUTOCHIPS SOFTWARE")
 * RECEIVED FROM AUTOCHIPS AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER
 * ON AN "AS-IS" BASIS ONLY. AUTOCHIPS EXPRESSLY DISCLAIMS ANY AND ALL
 * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NONINFRINGEMENT. NEITHER DOES AUTOCHIPS PROVIDE ANY WARRANTY WHATSOEVER WITH
 * RESPECT TO THE SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY,
 * INCORPORATED IN, OR SUPPLIED WITH THE AUTOCHIPS SOFTWARE, AND RECEIVER AGREES
 * TO LOOK ONLY TO SUCH THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO.
 * RECEIVER EXPRESSLY ACKNOWLEDGES THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO
 * OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES CONTAINED IN AUTOCHIPS
 * SOFTWARE. AUTOCHIPS SHALL ALSO NOT BE RESPONSIBLE FOR ANY AUTOCHIPS SOFTWARE
 * RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR
 * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND AUTOCHIPS'S
 * ENTIRE AND CUMULATIVE LIABILITY WITH RESPECT TO THE AUTOCHIPS SOFTWARE
 * RELEASED HEREUNDER WILL BE, AT AUTOCHIPS'S OPTION, TO REVISE OR REPLACE THE
 * AUTOCHIPS SOFTWARE AT ISSUE, OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE
 * CHARGE PAID BY RECEIVER TO AUTOCHIPS FOR SUCH AUTOCHIPS SOFTWARE AT ISSUE.
 */

/*!
 * @file ac780x_adc.c
 *
 * @brief This file provides analog to digital converter module integration functions.
 *
 */

/* ===========================================  Includes  =========================================== */
#include "ac780x_adc_reg.h"

/* ============================================  Define  ============================================ */

/* ===========================================  Typedef  ============================================ */

/* ==========================================  Variables  =========================================== */
/* ADC related info */
static const IRQn_Type s_adcIRQ[ADC_INSTANCE_MAX] = {ADC0_IRQn};

/* ADC callback pointer */
static DeviceCallback_Type s_adcCallback[ADC_INSTANCE_MAX] = {NULL};

static uint32_t s_extendGEOE = 0;

/* ====================================  Functions declaration  ===================================== */

/* ======================================  Functions define  ======================================== */
__STATIC_INLINE void ADC_GetFlashFormatGainOffset(const ADC_Type *ADCx, int16_t *gain, int16_t *offset)
{
    *gain = (int16_t)((uint16_t)(READ_BIT32(ADCx->CGV, ADC_CGV_GE0_Msk) >> ADC_CGV_GE0_Pos) << 7U) >> 7U;
    *offset = (int16_t)((uint16_t)(READ_BIT32(ADCx->COV, ADC_COV_OE0_Msk) >> ADC_COV_OE0_Pos) << 7U) >> 7U;
}

/*!
 * @brief ADC initialize.
 *
 * @param[in] ADCx: adc module
                 - ADC0
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void ADC_Init(ADC_Type *ADCx, const ADC_ConfigType *config)
{
    uint8_t instance = 0;
    int16_t ge = 0, oe = 0;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH >= config->regularSequenceLength);
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH >= config->injectSequenceLength);

    /* Enable adc clock */
    CKGEN_Enable(CLK_ADC0, ENABLE);
    CKGEN_SoftReset(SRST_ADC0, ENABLE);

    /* Set clock prescaler */
    ADC_SetClockPrescaler(ADCx, config->clkPsc);

    /* Set adc mode */
    ADC_SetScanMode(ADCx, config->scanModeEn);
    ADC_SetContinuousMode(ADCx, config->continuousModeEn);
    ADC_SetRegularDiscontinuousMode(ADCx, config->regularDiscontinuousModeEn);
    ADC_SetInjectDiscontinuousMode(ADCx, config->injectDiscontinuousModeEn);
    ADC_SetInjectAutoMode(ADCx, config->injectAutoModeEn);
    ADC_SetIntervalMode(ADCx, config->intervalModeEn);
    ADC_SetRegularDiscontinuousNum(ADCx, config->regularDiscontinuousNum);
    ADC_SetRegularTriggerSource(ADCx, config->regularTriggerMode);
    ADC_SetInjectTriggerSource(ADCx, config->injectTriggerMode);
    ADC_SetDataAlign(ADCx, config->dataAlign);

    /* Init analog */
    MODIFY_REG32(ANA_RG->ADC_CFG0, (0x3U << 30U), (30U), (1U));

    if (0 == s_extendGEOE)
    {
        /* Extend ge oe value */
        ADC_GetFlashFormatGainOffset(ADCx, &ge, &oe);
        ADC_SetExternalGainOffset(ADCx, ge, oe);
        ADC_SetInternalGainOffset(ADCx, ge, oe);

        s_extendGEOE = 1;  /* only need call once after bootup, not affected by ADC_DeInit */
    }

    /* Set adc sequence length */
    if (0 == config->regularSequenceLength)
    {
        ADC_SetRegularLength(ADCx, 0);
    }
    else
    {
        ADC_SetRegularLength(ADCx, config->regularSequenceLength - 1);
    }

    if (0 == config->injectSequenceLength)
    {
        ADC_SetInjectLength(ADCx, 0);
    }
    else
    {
        ADC_SetInjectLength(ADCx, config->injectSequenceLength - 1);
    }

    /* Register callback function */
    s_adcCallback[instance] = config->callBack;

    /* Set adc interrupt */
    if (ENABLE == config->interruptEn)
    {
        NVIC_EnableIRQ(s_adcIRQ[instance]);
    }
    else
    {
        NVIC_DisableIRQ(s_adcIRQ[instance]);
    }

    /* Set ADC reference */
    ADC_SetVoltageReference(ADCx, config->voltageRef);

    /* Set power */
    ADC_SetPowerMode(ADCx, config->powerMode);
}

/*!
 * @brief ADC De-initialize.
 *
 * @param[in] ADCx: adc module
                 - ADC0
 * @return none
 */
void ADC_DeInit(ADC_Type *ADCx)
{
    uint8_t instance = 0;

    instance = ADC_INDEX(ADCx);
    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);

    NVIC_DisableIRQ(s_adcIRQ[instance]);
    NVIC_ClearPendingIRQ(s_adcIRQ[instance]);
    CKGEN_SoftReset(SRST_ADC0, DISABLE);
    CKGEN_Enable(CLK_ADC0, DISABLE);
}

/*!
 * @brief Get ADC converter configuration.
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[out] config: pointer that ADC converter configuration need to fill in
 */
void ADC_GetConfig(const ADC_Type *ADCx, ADC_ConfigType * const config)
{
    uint8_t instance = 0;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    DEVICE_ASSERT(NULL != config);

    config->clkPsc = ADC_GetClockPrescaler(ADCx);

    config->scanModeEn = ADC_GetScanMode(ADCx);
    config->continuousModeEn = ADC_GetContinuousMode(ADCx);
    config->regularDiscontinuousModeEn = ADC_GetRegularDiscontinuousMode(ADCx);
    config->injectDiscontinuousModeEn = ADC_GetInjectDiscontinuousMode(ADCx);
    config->injectAutoModeEn = ADC_GetInjectAutoMode(ADCx);
    config->intervalModeEn = ADC_GetIntervalMode(ADCx);
    config->regularDiscontinuousNum = ADC_GetRegularDiscontinuousNum(ADCx);
    config->voltageRef = ADC_GetVoltageReference(ADCx);
    config->interruptEn = (ACTION_Type)NVIC_GetEnableIRQ(s_adcIRQ[instance]);

    config->regularTriggerMode = ADC_GetRegularTriggerSource(ADCx);
    config->injectTriggerMode = ADC_GetInjectTriggerSource(ADCx);

    config->regularSequenceLength = ADC_GetRegularLength(ADCx);
    config->injectSequenceLength = ADC_GetInjectLength(ADCx);

    config->dataAlign = ADC_GetDataAlign(ADCx);

    config->callBack = s_adcCallback[instance];

    config->powerMode = ADC_GetPowerMode(ADCx);
}

/*!
 * @brief Get adc regular group channel & sample time setting for specific sequence.
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[in] channel: adc convert channel
                - ADC_CH_0
                - ADC_CH_1
                - ADC_CH_2
                - ADC_CH_3
                - ADC_CH_4
                - ADC_CH_5
                - ADC_CH_6
                - ADC_CH_7
                - ADC_CH_8
                - ADC_CH_9
                - ADC_CH_10
                - ADC_CH_11
                - ADC_CH_12
                - ADC_CH_13
                - ADC_CH_14
                - ADC_CH_15
                - ADC_CH_16
                - ADC_CH_17
                - ADC_CH_18
                - ADC_CH_VIN
                - ADC_CH_GEOE
 * @param[in] spt: sampel time setting
                - ADC_SPT_CLK_9
                - ADC_SPT_CLK_7
                - ADC_SPT_CLK_17
                - ADC_SPT_CLK_33
                - ADC_SPT_CLK_64
                - ADC_SPT_CLK_140
                - ADC_SPT_CLK_215
                - ADC_SPT_CLK_5
 * @param[in] seq: regular group sequence
 */
void ADC_GetRegularGroupChannel(const ADC_Type *ADCx, ADC_ChannelType *channel, ADC_SampleTimeType *spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(NULL != channel);
    DEVICE_ASSERT(NULL != spt);

    *channel = ADC_GetRegularSequence(ADCx, seq);
    *spt = ADC_GetChannelSampleTime(ADCx, *channel);
}

/*!
 * @brief Set adc regular group channel
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[in] channel: adc channel
                - ADC_CH_0
                - ADC_CH_1
                - ADC_CH_2
                - ADC_CH_3
                - ADC_CH_4
                - ADC_CH_5
                - ADC_CH_6
                - ADC_CH_7
                - ADC_CH_8
                - ADC_CH_9
                - ADC_CH_10
                - ADC_CH_11
                - ADC_CH_12
                - ADC_CH_13
                - ADC_CH_14
                - ADC_CH_15
                - ADC_CH_16
                - ADC_CH_17
                - ADC_CH_18
                - ADC_CH_VIN
                - ADC_CH_GEOE
 * @param[in] spt: sample time
                - ADC_SPT_CLK_9
                - ADC_SPT_CLK_7
                - ADC_SPT_CLK_17
                - ADC_SPT_CLK_33
                - ADC_SPT_CLK_64
                - ADC_SPT_CLK_140
                - ADC_SPT_CLK_215
                - ADC_SPT_CLK_5
 * @param[in] seq: regular group convert sequence
                - 0 ~ 18
 * @return none
 */
void ADC_SetRegularGroupChannel(ADC_Type *ADCx, ADC_ChannelType channel, ADC_SampleTimeType spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_CH_MAX > channel);
    DEVICE_ASSERT(ADC_SPT_CLK_MAX > spt);
    DEVICE_ASSERT(ADC_REGULAR_SEQUENCE_LEGNTH > seq);

    /* Set adc channel sample time */
    ADC_SetChannelSampleTime(ADCx, channel, spt);

    /* Set regular convert sequence */
    ADC_SetRegularSequence(ADCx, channel, seq);
}

/*!
 * @brief Get adc injection group channel & sample time setting for specific sequence.
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[out] channel: adc channel
                - ADC_CH_0
                - ADC_CH_1
                - ADC_CH_2
                - ADC_CH_3
                - ADC_CH_4
                - ADC_CH_5
                - ADC_CH_6
                - ADC_CH_7
                - ADC_CH_8
                - ADC_CH_9
                - ADC_CH_10
                - ADC_CH_11
                - ADC_CH_12
                - ADC_CH_13
                - ADC_CH_14
                - ADC_CH_15
                - ADC_CH_16
                - ADC_CH_17
                - ADC_CH_18
                - ADC_CH_VIN
                - ADC_CH_GEOE
 * @param[out] spt: sample time
                - ADC_SPT_CLK_9
                - ADC_SPT_CLK_7
                - ADC_SPT_CLK_17
                - ADC_SPT_CLK_33
                - ADC_SPT_CLK_64
                - ADC_SPT_CLK_140
                - ADC_SPT_CLK_215
                - ADC_SPT_CLK_5
 * @param[in] seq: injection group convert sequence
 */
void ADC_GetInjectGroupChannel(const ADC_Type *ADCx, ADC_ChannelType *channel, ADC_SampleTimeType *spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(NULL != channel);
    DEVICE_ASSERT(NULL != spt);

    *channel = ADC_GetInjectSequence(ADCx, seq);
    *spt = ADC_GetChannelSampleTime(ADCx, *channel);
}

/*!
* @brief Set adc injected group channel.
*
* @param[in] ADCx: adc module
                - ADC0
* @param[in] channel: adc channel
                - ADC_CH_0
                - ADC_CH_1
                - ADC_CH_2
                - ADC_CH_3
                - ADC_CH_4
                - ADC_CH_5
                - ADC_CH_6
                - ADC_CH_7
                - ADC_CH_8
                - ADC_CH_9
                - ADC_CH_10
                - ADC_CH_11
                - ADC_CH_12
                - ADC_CH_13
                - ADC_CH_14
                - ADC_CH_15
                - ADC_CH_16
                - ADC_CH_17
                - ADC_CH_18
                - ADC_CH_VIN
                - ADC_CH_GEOE
* @param[in] spt: sample time
                - ADC_SPT_CLK_9
                - ADC_SPT_CLK_7
                - ADC_SPT_CLK_17
                - ADC_SPT_CLK_33
                - ADC_SPT_CLK_64
                - ADC_SPT_CLK_140
                - ADC_SPT_CLK_215
                - ADC_SPT_CLK_5
* @param[in] seq: injected group convert sequence
                - 0 ~ 3
* @return none
*/
void ADC_SetInjectGroupChannel(ADC_Type *ADCx, ADC_ChannelType channel, ADC_SampleTimeType spt, uint8_t seq)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(ADC_CH_MAX > channel);
    DEVICE_ASSERT(ADC_INJECT_SEQUENCE_LEGNTH > seq);
    DEVICE_ASSERT(ADC_SPT_CLK_MAX > spt);

    /* Set adc channel sample time */
    ADC_SetChannelSampleTime(ADCx, channel, spt);

    /* Set injected convert sequence */
    ADC_SetInjectSequence(ADCx, channel, seq);
}

/*!
 * @brief Set adc analog monitor.
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[in] config: pointer to configuration structure
 * @return none
 */
void ADC_SetAnalogMonitor(ADC_Type *ADCx, const ADC_AMOConfigType *config)
{
    DEVICE_ASSERT(ADC_INSTANCE_MAX > ADC_INDEX(ADCx));
    DEVICE_ASSERT(NULL != config);

    /* Set analog monitor mode */
    ADC_SetAMOTriggerMode(ADCx, config->AMOTriggerMode);
    ADC_SetAMOInterrupt(ADCx, config->AMOInterruptEn);
    ADC_SetAMORegularMode(ADCx, config->AMORegularEn);
    ADC_SetAMOInjectMode(ADCx, config->AMOInjectEn);
    ADC_SetAMOSingleChannelMode(ADCx, config->AMOSingleModeEn);

    ADC_SetAMOSingleChannel(ADCx, config->AMOSingleChannel);
    ADC_SetAMOThreshold(ADCx, config->AMOHighThreshold, config->AMOLowThreshold);
    ADC_SetAMOOffset(ADCx, config->AMOHighOffset, config->AMOLowOffset);
}

/*!
 * @brief Set adc callback function.
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @param[in] func: callback function
 * @return none
 */
void ADC_SetCallback(ADC_Type *ADCx, const DeviceCallback_Type func)
{
    uint8_t instance = 0;

    instance = ADC_INDEX(ADCx);

    DEVICE_ASSERT(ADC_INSTANCE_MAX > instance);
    s_adcCallback[instance] = func;
}

/*!
 * @brief ADC common interrupt service routine
 *
 * @param[in] ADCx: adc module
                - ADC0
 * @return none
 */
static void ADC_CommonISR(ADC_Type *ADCx)
{
    uint8_t instance = 0;
    uint32_t wpara = 0U, lpara = 0U;
    uint32_t reg = 0U;

    instance = ADC_INDEX(ADCx);

    /* store device status */
    wpara = ADCx->STR;

    /* clear device status */
    ADCx->STR = ~wpara;

    reg = ADCx->REOC;
    if (reg > 0U)
    {
        ADCx->REOC = reg;
        wpara |= ADC_IRQ_REOC_Msk;
        lpara = reg;
    }
    else
    {
        reg = ADCx->IEOC;
        if (reg > 0U)
        {
            ADCx->IEOC = reg;
            wpara |= ADC_IRQ_IEOC_Msk;
            lpara = reg;
        }
    }

    if (NULL != s_adcCallback[instance])
    {
        /* callback */
        s_adcCallback[instance](ADCx, wpara, lpara);
    }
}

/*!
 * @brief ADC0 interrupt request handler
 *
 * @param[in] none
 * @return none
 */
void ADC0_IRQHandler(void)
{
    ADC_CommonISR(ADC0);
}

/* =============================================  EOF  ============================================== */
