/*
 * Copyright 2020-2022 Yuntu Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

/**
 * @file eadc_hw_access.h
 */

#ifndef eADC_HW_ACCESS_H
#define eADC_HW_ACCESS_H

#include <stdint.h>
#include <stdbool.h>
#include "device_registers.h"
#include "eadc_driver.h"


/*! @file eadc_hw_access.h*/

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define EADC_WAIT_TIMEOUT (1000U)

/*! @brief eADC Interrupt Enable */
typedef enum
{
    eADC_READY_INTERRUPT = 0U,
    eADC_END_OF_SAMPLING_INTERRUPT = 1U,
    eADC_END_OF_CONVERSION_INTERRUPT = 2U,
    eADC_NORMAL_ERROR_INTERRUPT = 3U,
    eADC_HIGH_PRIORITY_END_OF_SEQUENCE_INTERRUPT = 4U,
    eADC_LOW_PRIORITY_END_OF_SEQUENCE_INTERRUPT = 5U,
    eADC_OVERRUN_INTERRUPT = 6U,
    eADC_LOW_PRIORITY_TRIGGER_ERROR_INTERRUPT = 7U,
    eADC_WATCHDOG_INTERRUPT = 16U,
} eadc_interrupt_enable_t;

/*! @brief eADC status */
typedef enum
{
    eADC_READY_FLAG = 0U,
    eADC_END_OF_SAMPLING_FLAG = 1U,
    eADC_END_OF_CONVERSION_FLAG = 2U,
    eADC_NORMAL_ERROR_FLAG = 3U,
    eADC_HIGH_PRIORITY_END_OF_SEQUENCE_FLAG = 4U,
    eADC_LOW_PRIORITY_END_OF_SEQUENCE_FLAG = 5U,
    eADC_LOW_PRIORITY_TRIGGER_ERROR_FLAG = 6U,
    eADC_WATCHDOG0_FLAG = 16U,
    eADC_WATCHDOG1_FLAG = 17U,
    eADC_WATCHDOG2_FLAG = 18U,
    eADC_WATCHDOG3_FLAG = 19U,
} eadc_status_t;

/*******************************************************************************
 * API
 ******************************************************************************/

#if defined (__cplusplus)
extern "C" {
#endif

/*!
 * @name Converter
 * General eADC functions.
 */
/*! @{*/

/*!
 * @brief Sets the eADC clock divider configuration.
 *
 * This functions configures the eADC instance clock divider.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] clockDivide clk divider
 */
static inline void eADC_SetClockDivide(eADC_Type *const baseAddr,
                                      const eadc_clk_divide_t clockDivide)
{
    uint32_t tmp = baseAddr->CFG1;
    tmp &= ~(eADC_CFG0_PRS_MASK);
    tmp |= eADC_CFG0_PRS(clockDivide);
    baseAddr->CFG1 = tmp;
}

/*!
 * @brief Sets the Start time in AD clock cycles
 *
 * This function configures the start time for the eADC (in
 * eADC clocks). The actual start time will be the value
 * provided plus 1.  
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] sampletime Start time in AD Clocks
 */
static inline void eADC_SetStartTime(eADC_Type *const baseAddr,
                                    uint8_t starttime)
{
    /* Clip start time to minimum value */
    uint32_t tmp = baseAddr->CFG1;
    tmp &= ~(eADC_CFG1_STCNT_MASK);
    tmp |= eADC_CFG1_STCNT(starttime);
    baseAddr->CFG1 = tmp;
}

static inline void eADC_SetSampleAverage(eADC_Type *const baseAddr,
                                        eadc_sample_average_t sampleAverage)
{
    uint32_t tmp = baseAddr->CFG1;
    tmp &= ~(eADC_CFG1_SMPAVGE_MASK);
    tmp |= eADC_CFG1_SMPAVGE(sampleAverage);
    baseAddr->CFG1 = tmp;
}

static inline void eADC_SetSwitchChannelCnt(eADC_Type *const baseAddr,
                                           uint8_t switchChannelCnt)
{
    uint32_t tmp = baseAddr->CFG1;
    tmp &= ~(eADC_CFG1_SWITCHCNT_MASK);
    tmp |= eADC_CFG1_SWITCHCNT(switchChannelCnt);
    baseAddr->CFG1 = tmp;
}

static inline void eADC_SetClkDivider(eADC_Type *const baseAddr,
                                     eadc_clk_divide_t divider)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_PRS_MASK);
    tmp |= eADC_CFG0_PRS(divider);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetSampleTime(eADC_Type *const baseAddr,
                                     uint8_t sampleTime0, uint8_t sampleTime1)
{
    uint32_t tmp = baseAddr->SMP;
    tmp &= ~(eADC_SMP_SMP0_MASK | eADC_SMP_SMP1_MASK);
    tmp |= eADC_SMP_SMP0(sampleTime0) | eADC_SMP_SMP1(sampleTime1);
    baseAddr->SMP = tmp;
}

static inline void eADC_SetSeqCfg(eADC_Type *const baseAddr,
                                 eadc_sequence_length_t config)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_SEQCFG_MASK);
    tmp |= eADC_CFG0_SEQCFG(config);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetHighSeqSelect(eADC_Type *const baseAddr,
                                      eadc_high_priority_sequence_select_t select)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_HSEQSEL_MASK);
    tmp |= eADC_CFG0_HSEQSEL(select);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetLowPowerEnable(eADC_Type *const baseAddr,
                                         bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_LPEN_MASK);
    tmp |= eADC_CFG0_LPEN(enable);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetAutoOffEnable(eADC_Type *const baseAddr,
                                        bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_AUTOOFF_MASK);
    tmp |= eADC_CFG0_AUTOOFF(enable);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetDMAGenOverride(eADC_Type *const baseAddr,
                                         bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_DMAREQCFG_MASK);
    if(enable == false)
    {
        tmp |= eADC_CFG0_DMAREQCFG_MASK;
    }
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetDataAlign(eADC_Type *const baseAddr,
                                    eadc_align_t align)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_ALIGN_MASK);
    tmp |= eADC_CFG0_ALIGN(align);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetSeqMode(eADC_Type *const baseAddr,
                                  eadc_sequence_mode_t mode)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_MODE_MASK);
    tmp |= eADC_CFG0_MODE(mode);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetResolution(eADC_Type *const baseAddr,
                                     eadc_resolution_t resolution)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_RES_MASK);
    tmp |= eADC_CFG0_RES(resolution);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetHighSeqLen(eADC_Type *const baseAddr,
                                   uint8_t length)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_HSEQLEN_MASK);
    tmp |= eADC_CFG0_HSEQLEN(length - 1);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetSwEnable(eADC_Type *const baseAddr,
                                   bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_SFTTEN_MASK);
    tmp |= eADC_CFG0_SFTTEN(enable);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetLowSeqLen(eADC_Type *const baseAddr,
                                   uint8_t length)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_LSEQLEN_MASK);
    tmp |= eADC_CFG0_LSEQLEN(length - 1);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetLowDmaEnable(eADC_Type *const baseAddr,
                                      bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_LDMAEN_MASK);
    tmp |= eADC_CFG0_LDMAEN(enable);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetHighDmaEnable(eADC_Type *const baseAddr,
                                      bool enable)
{
    uint32_t tmp = baseAddr->CFG0;
    tmp &= ~(eADC_CFG0_HDMAEN_MASK);
    tmp |= eADC_CFG0_HDMAEN(enable);
    baseAddr->CFG0 = tmp;
}

static inline void eADC_SetINTE(eADC_Type *const baseAddr,
                               eadc_interrupt_enable_t interrupt, bool enable)
{
    uint32_t tmp = baseAddr->INTE;
    tmp &= ~(1 << (uint32_t)interrupt);
    tmp |= (enable << (uint32_t)interrupt);
    baseAddr->INTE = tmp;
}

static inline bool eADC_GetStatus(eADC_Type *const baseAddr,
                                 eadc_status_t flag)
{
    if(baseAddr->STS & (1 << (uint32_t)flag))
    {
        return true;
    }
    return false;
}

static inline void eADC_ClearStatus(eADC_Type *const baseAddr,
                                   eadc_status_t flag)
{
    uint16_t timeout_cnt = 0U;
    baseAddr->STS = (1 << (uint32_t)flag);
    while(((baseAddr->STS & (1 << (uint32_t)flag)) == 0u) && (timeout_cnt++ < EADC_WAIT_TIMEOUT));
}

static inline bool eADC_GetOverrun(eADC_Type *const baseAddr,
                                   uint8_t channel)
{
    if(baseAddr->OVRSTS & (1 << (uint32_t)channel))
    {
        return true;
    }
    return false;
}

static inline void eADC_ClearOverrun(eADC_Type *const baseAddr,
                                     uint8_t channel)
{
    uint16_t timeout_cnt = 0U;
    baseAddr->OVRSTS = (1 << (uint32_t)channel);
    while(((baseAddr->OVRSTS & (1 << (uint32_t)channel)) == 0u) && (timeout_cnt++ < EADC_WAIT_TIMEOUT));
}

/*!
 * @brief Sets the sequence channel ID by sequence index
 *
 * This function set the channel ID of given sequence index.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] idx Sequence index
 * @param[in] channelCfg channel configuration
 */
static inline void eADC_SetSequenceChannel(eADC_Type *const baseAddr,
                                         uint8_t idx,
                                         eadc_inputchannel_config_t channelCfg)
{
    baseAddr->CHSEL[idx] = eADC_CHSEL_SMPSEL(channelCfg.sampleSelect) |
                           eADC_CHSEL_OVRMOD(channelCfg.overflowMode) |
                           eADC_CHSEL_CHSEL(channelCfg.channel);
}

/*!
 * @brief Stop eADC
 *
 * This function stop the eADC
 *
 * @param[in] baseAddr eadc base pointer
 */
static inline void eADC_Stop(eADC_Type *const baseAddr)
{
    if (baseAddr->CTRL & eADC_CTRL_ADSTART_MASK)
    {
        baseAddr->CTRL |= eADC_CTRL_ADSTOP_MASK;
        while (eADC_CTRL_ADSTOP_MASK & baseAddr->CTRL)
        {
            /* wait eADC sequence stop */
        }
    }
}

/*!
 * @brief Start eADC
 *
 * This function start the eADC
 *
 * @param[in] baseAddr eadc base pointer
 */
static inline void eADC_Start(eADC_Type *const baseAddr)
{
    uint32_t tmp = baseAddr->CTRL;
    tmp &= (uint32_t) (~(eADC_CTRL_ADSTART_MASK));
    tmp |= eADC_CTRL_ADSTART(1);
    baseAddr->CTRL = (uint32_t) tmp;
}

/*!
 * @brief Disable eADC
 *
 * This function disable eADC and let eADC enter low power mode
 *
 * @param[in] baseAddr eadc base pointer
 */
static inline void eADC_Disable(eADC_Type *const baseAddr)
{
    baseAddr->CTRL |= eADC_CTRL_ADDIS_MASK;
    while (eADC_CTRL_ADDIS_MASK & baseAddr->CTRL)
    {
        /* wait eADC diabled */
    }
}

/*!
 * @brief Enable eADC
 *
 * This function enable the eADC
 *
 * @param[in] baseAddr eadc base pointer
 */
static inline void eADC_Enable(eADC_Type *const baseAddr)
{
    if (0 == (eADC_CTRL_ADEN_MASK & baseAddr->CTRL))
    {
        baseAddr->CTRL |= eADC_CTRL_ADEN_MASK;
    }
}


/*!
 * @brief Sets the Analog Watchdog Interrupt Enable Flag state
 *
 * This function configures the Analog Watchdog Interrupt Enable
 * Flag.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] state the Analog Watchdog Interrupt Enable Flag state
 */
static inline void eADC_SetAwdIntEnableFlag(eADC_Type *const baseAddr,
                                           const bool state)
{
    uint32_t tmp = (uint32_t) baseAddr->INTE;
    tmp &= (uint32_t) (~(eADC_INTE_WDIE_MASK));
    tmp |= eADC_INTE_WDIE(state ? (uint32_t) 1u : (uint32_t) 0u);
    baseAddr->INTE = (uint32_t) tmp;
}

/*!
 * @brief Sets the Analog Watchdog Effective Mode
 *
 * This function configures the Analog Watchdog Effective Mode
 * Flag.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] state the Analog Watchdog Effective Mode state
 * @param[in] wdgChannel eadc watch dog channel
 */
static inline void eADC_SetAwdEffectiveMode(eADC_Type *const baseAddr,
                                           const bool effective_mode,
                                           const uint8_t wdgChannel)
{
    uint32_t tmp = (uint32_t) baseAddr->WDTH[wdgChannel];
    tmp &= (uint32_t) (~(eADC_WDTH_THMD_MASK));
    tmp |= eADC_WDTH_THMD(effective_mode ? (uint32_t) 1u : (uint32_t) 0u);
    baseAddr->WDTH[wdgChannel] = (uint32_t) tmp;
}

/*!
 * @brief Sets the Compare Register Low value
 *
 * This function writes a 12-bit value in the Hardware
 * Compare Register Low. This value defines the lower
 * limit for the Hardware Compare Range. This value is always
 * 12-bit resolution value (for lower resolution modes, internal
 * bit shifting will take place).
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] value the new Compare Register Low value
 * @param[in] wdgChannel eadc watch dog channel
 */
static inline void eADC_SetHwCompareCompLowValue(eADC_Type *const baseAddr,
                                                const uint16_t value,
                                                const uint8_t wdgChannel)
{
    uint32_t tmp = (uint32_t) baseAddr->WDTH[wdgChannel];
    tmp &= ~eADC_WDTH_LOW_MASK;
    tmp |= eADC_WDTH_LOW(value);
    baseAddr->WDTH[wdgChannel] = tmp;
}

/*!
 * @brief Sets the Compare Register High value
 *
 * This function writes a 12-bit value in the Hardware
 * Compare Register High. This value defines the upper 
 * limit for the Hardware Compare Range. This value is always
 * 12-bit resolution (for lower resolution modes, internal
 * bit shifting will take place).
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] value the new Compare Register High value
 * @param[in] wdgChannel eadc watch dog channel
 */
static inline void eADC_SetHwCompareCompHighValue(eADC_Type *const baseAddr,
                                                 const uint16_t value,
                                                 const uint8_t wdgChannel)
{
    uint32_t tmp = (uint32_t) baseAddr->WDTH[wdgChannel];
    tmp &= ~eADC_WDTH_HIGH_MASK;
    tmp |= eADC_WDTH_HIGH(value);
    baseAddr->WDTH[wdgChannel] = tmp;
}

/*!
 * @brief Sets the Hardware Compare All Channel Enable Flag state
 *
 * This function configures the Hardware Compare All Channel
 * Enable Flag. Using this feature, the eADC can be configured
 * to enable hardware compare on all channels.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] state the new Hardware Compare Greater Than Enable Flag state
 */
static inline void eADC_SetHwCompareAllEnableFlag(eADC_Type *const baseAddr,
                                                 const bool state)
{
    uint32_t tmp = (uint32_t) baseAddr->WDCTRL;
    tmp &= (uint32_t) (~(eADC_WDCTRL_WD0SGL_MASK));
    tmp |= eADC_WDCTRL_WD0SGL(state ? (uint32_t) 0u : (uint32_t) 1u);
    baseAddr->WDCTRL = (uint32_t) tmp;
}

/*!
 * @brief Sets the Hardware Compare Enable Flag state
 *
 * This functions configures the Hardware Compare Enable Flag.
 * Hardware Compare can be used to check if the eADC result
 * is within or outside of a predefined range.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] wdgChannel eadc wdg channel
 * @param[in] state the new Hardware Compare Enable Flag state
 */
static inline void eADC_SetHwCompareEnableFlag(eADC_Type *const baseAddr,
                                              const bool state, const uint8_t wdgChannel)
{
    uint32_t tmp = (uint32_t) baseAddr->WDCTRL;
    /* Channel Enable Bit Shift */
    /* Channel 0: 7u, Channel 1: 15u, Channel 2: 23u, Channel 3: 31u */
    uint8_t channelEnableBitShift = (uint8_t) ((wdgChannel << 3U) + 7);
    tmp &= (uint32_t) (~((1U << channelEnableBitShift)));
    tmp |= (state ? (uint32_t) 1u : (uint32_t) 0u) << channelEnableBitShift;
    baseAddr->WDCTRL = (uint32_t) tmp;
}

/*!
 * @brief Sets the Hardware Compare Channel
 *
 * This function configures the channel index of the Hardware Compare.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] channel eadc input channel
 * @param[in] wdgChannel eadc watch dog channel
 * @param[in] state the new Hardware Compare channel
 */
static inline void eADC_SetHwCompareRangeEnableFlag(eADC_Type *const baseAddr,
                                                   const eadc_inputchannel_t channel,
                                                   const uint8_t wdgChannel)
{
    uint32_t tmp = (uint32_t) baseAddr->WDCTRL;
    /* Channel Compare Bit Shift */
    /* Channel 0: 0u, Channel 1: 8u, Channel 2: 16u, Channel 3: 24u */
    uint8_t channelCmpValBitShift = (uint8_t) (wdgChannel << 3);
    tmp &= (uint32_t) (~(0x3FU << channelCmpValBitShift));
    tmp |= (channel & 0x3FU) << channelCmpValBitShift;
    baseAddr->WDCTRL = (uint32_t) tmp;
}

/*!
 * @brief Gets the result from result FIFO
 *
 * This function returns the conversion result from FIFO.
 * Flag.
 *
 * @param[in] baseAddr eadc base pointer
 * @param[in] index index of eADC sequence
 */
static inline uint16_t eADC_ReadFIFO(eADC_Type *const baseAddr, uint8_t index)
{
    return baseAddr->DATA[index];
}

#if defined (__cplusplus)
}
#endif


#endif /* eADC_HW_ACCESS_H */
/*******************************************************************************
 * EOF
 ******************************************************************************/
