//*****************************************************************************
//
//  am_hal_adc.c
//! @file
//!
//! @brief Functions for interfacing with the Analog to Digital Converter.
//!
//! @addtogroup adc2 Analog-to-Digital Converter (ADC)
//! @ingroup apollo2hal
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2019, Ambiq Micro
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 
// 2. 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.
// 
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
// 
// 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.
//
// This is part of revision v2.2.0-7-g63f7c2ba1 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include "am_mcu_apollo.h"

//*****************************************************************************
//
// Private Types.
//
//*****************************************************************************

#define AM_HAL_MAGIC_ADC                0xAFAFAF
#define AM_HAL_ADC_CHK_HANDLE(h)        ((h) && ((am_hal_handle_prefix_t *)(h))->s.bInit && (((am_hal_handle_prefix_t *)(h))->s.magic == AM_HAL_MAGIC_ADC))

// ****************************************************************************
//
// Apollo3 Temperature Trim Value Locations and default coefficients.
//
// ****************************************************************************
#define AM_HAL_ADC_CALIB_TEMP_ADDR          (0x50023840)
#define AM_HAL_ADC_CALIB_AMBIENT_ADDR       (0x50023844)
#define AM_HAL_ADC_CALIB_ADC_OFFSET_ADDR    (0x50023848)

// ****************************************************************************
//
// Default coefficients (used when trims not provided):
//  TEMP_DEFAULT    = Temperature in deg K (e.g. 299.5 - 273.15 = 26.35)
//  AMBIENT_DEFAULT = Voltage measurement at default temperature.
//  OFFSET_DEFAULT  = Default ADC offset at 1v.
//
// ****************************************************************************
#define AM_HAL_ADC_CALIB_TEMP_DEFAULT       (299.5F)
#define AM_HAL_ADC_CALIB_AMBIENT_DEFAULT    (1.02809F)
#define AM_HAL_ADC_CALIB_ADC_OFFSET_DEFAULT (-0.004281F)


//
// ADC Power save register state.
//
typedef struct
{
    bool          bValid;
    uint32_t      regCFG;
    uint32_t      regSL0CFG;
    uint32_t      regSL1CFG;
    uint32_t      regSL2CFG;
    uint32_t      regSL3CFG;
    uint32_t      regSL4CFG;
    uint32_t      regSL5CFG;
    uint32_t      regSL6CFG;
    uint32_t      regSL7CFG;
    uint32_t      regWULIM;
    uint32_t      regWLLIM;
    uint32_t      regINTEN;
} am_hal_adc_register_state_t;

//
// ADC State structure.
//
typedef struct
{
    //
    // Handle validation prefix.
    //
    am_hal_handle_prefix_t      prefix;

    //
    // Physical module number.
    //
    uint32_t                    ui32Module;

    //
    // ADC Capabilities.
    //
    am_hal_adc_capabilities_t   capabilities;

    // Power Save-Restore register state
    am_hal_adc_register_state_t registerState;

} am_hal_adc_state_t;

//*****************************************************************************
//
//! @brief Private SRAM view of temperature trims.
//!
//! This static SRAM union is private to the ADC HAL functions.
//
//*****************************************************************************
static union
{
    //! These trim values are loaded as uint32_t values.
    struct
    {
              //! Temperature of the package test head (in degrees Kelvin)
              uint32_t ui32CalibrationTemperature;

              //! Voltage corresponding to temperature measured on test head.
              uint32_t ui32CalibrationVoltage;

              //! ADC offset voltage measured on the package test head.
              uint32_t ui32CalibrationOffset;

              //! Flag if default (guess) or measured.
              bool bMeasured;
    } ui32;
    //! These trim values are accessed as floats when used in temp calculations.
    struct
    {
              //! Temperature of the package test head in degrees Kelvin
              float    fCalibrationTemperature;

              //! Voltage corresponding to temperature measured on test head.
              float    fCalibrationVoltage;

              //! ADC offset voltage measured on the package test head.
              float    fCalibrationOffset;

              //! Flag if default (guess) or measured.
              float fMeasuredFlag;
    } flt;
} priv_temp_trims;

//*****************************************************************************
//
// Global Variables.
//
//*****************************************************************************
am_hal_adc_state_t             g_ADCState[AM_REG_ADC_NUM_MODULES];

uint32_t                       g_ADCSlotsConfigured;

//*****************************************************************************
//
//! @brief ADC initialization function
//!
//! @param ui32Module   - module instance.
//! @param handle       - returns the handle for the module instance.
//!
//! This function accepts a module instance, allocates the interface and then
//! returns a handle to be used by the remaining interface functions.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_initialize(uint32_t ui32Module, void **ppHandle)
{

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Validate the module number
    //
    if ( ui32Module >= AM_REG_ADC_NUM_MODULES )
    {
        return AM_HAL_STATUS_OUT_OF_RANGE;
    }

    //
    // Check for valid arguements.
    //
    if ( !ppHandle )
    {
        return AM_HAL_STATUS_INVALID_ARG;
    }

    //
    // Check if the handle is unallocated.
    //
    if ( g_ADCState[ui32Module].prefix.s.bInit )
    {
        return AM_HAL_STATUS_INVALID_OPERATION;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Initialize the handle.
    //
    g_ADCState[ui32Module].prefix.s.bInit = true;
    g_ADCState[ui32Module].prefix.s.magic = AM_HAL_MAGIC_ADC;
    g_ADCState[ui32Module].ui32Module = ui32Module;

    //
    // Initialize the number of slots configured.
    //
    g_ADCSlotsConfigured = 0;

    //
    // Return the handle.
    //
    *ppHandle = (void *)&g_ADCState[ui32Module];

    //
    // Before returning, grab the temperature trims.
    //
    priv_temp_trims.ui32.ui32CalibrationTemperature =
                  am_hal_flash_load_ui32((uint32_t*)AM_HAL_ADC_CALIB_TEMP_ADDR);
    priv_temp_trims.ui32.ui32CalibrationVoltage     =
                  am_hal_flash_load_ui32((uint32_t*)AM_HAL_ADC_CALIB_AMBIENT_ADDR);
    priv_temp_trims.ui32.ui32CalibrationOffset      =
                  am_hal_flash_load_ui32((uint32_t*)AM_HAL_ADC_CALIB_ADC_OFFSET_ADDR);

    if ( (priv_temp_trims.ui32.ui32CalibrationTemperature == 0xffffffff)    ||
         (priv_temp_trims.ui32.ui32CalibrationVoltage     == 0xffffffff)    ||
         (priv_temp_trims.ui32.ui32CalibrationOffset      == 0xffffffff) )
    {
        //
        // Since the device has not been calibrated on the tester, we'll load
        // default calibration values.  These default values should result
        // in worst-case temperature measurements of +-6 degress C.
        //
        priv_temp_trims.flt.fCalibrationTemperature = AM_HAL_ADC_CALIB_TEMP_DEFAULT;
        priv_temp_trims.flt.fCalibrationVoltage     = AM_HAL_ADC_CALIB_AMBIENT_DEFAULT;
        priv_temp_trims.flt.fCalibrationOffset      = AM_HAL_ADC_CALIB_ADC_OFFSET_DEFAULT;
        priv_temp_trims.ui32.bMeasured = false;
    }
    else
    {
        priv_temp_trims.ui32.bMeasured = true;
    }

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief MSPI deinitialization function
//!
//! @param handle       - returns the handle for the module instance.
//!
//! This function accepts a handle to an instance and de-initializes the
//! interface.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_deinitialize(void *pHandle)
{
    uint32_t            status = AM_HAL_STATUS_SUCCESS;
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    if ( pADCState->prefix.s.bEnable )
    {
        status = am_hal_adc_disable(pHandle);
    }

    pADCState->prefix.s.bInit = false;

    //
    // Return the status.
    //
    return status;
}

//*****************************************************************************
//
//! @brief ADC configuration function
//!
//! @param handle   - handle for the module instance.
//! @param pConfig  - pointer to the configuration structure.
//!
//! This function configures the ADC for operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_configure(void *pHandle,
                     am_hal_adc_config_t *psConfig)
{
    uint32_t            ui32Config;
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;
    uint32_t            ui32Module = pADCState->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    ui32Config = 0;

    //
    // Set the ADC clock source.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_CLKSEL, psConfig->eClock);

    //
    // Set the ADC trigger polarity.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_TRIGPOL, psConfig->ePolarity);

    //
    // Set the ADC trigger.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_TRIGSEL, psConfig->eTrigger);

    //
    // Set the ADC reference voltage.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_REFSEL, psConfig->eReference);

    //
    // Set the Destructive FIFO read.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_DFIFORDEN, 1);

    //
    // Set the ADC clock mode.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_CKMODE, psConfig->eClockMode);

    //
    // Set the ADC low power mode.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_LPMODE, psConfig->ePowerMode);

    //
    // Set the ADC repetition mode.
    //
    ui32Config |= _VAL2FLD(ADC_CFG_RPTEN, psConfig->eRepeat);

    //
    // Set the configuration in the ADC peripheral.
    //
    ADCn(ui32Module)->CFG = ui32Config;

    //
    // Return status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC slot configuration function
//!
//! @param handle   - handle for the module instance.
//! @param pConfig  - pointer to the configuration structure.
//!
//! This function configures the ADC slot for operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_configure_slot(void *pHandle,
                          uint32_t ui32SlotNumber,
                          am_hal_adc_slot_config_t *pSlotConfig)
{
    uint32_t            ui32Config;
    uint32_t            ui32RegOffset;
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;
    uint32_t            ui32Module = pADCState->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }

    //
    // Check the slot number.
    //
    if ( ui32SlotNumber >= AM_HAL_ADC_MAX_SLOTS )
    {
        return AM_HAL_STATUS_OUT_OF_RANGE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    ui32Config = 0;

    //
    // Set the measurements to average
    //
    ui32Config |= _VAL2FLD(ADC_SL0CFG_ADSEL0, pSlotConfig->eMeasToAvg);

    //
    // Set the precision mode.
    //
    ui32Config |= _VAL2FLD(ADC_SL0CFG_PRMODE0, pSlotConfig->ePrecisionMode);

    //
    // Set the channel.
    //
    ui32Config |= _VAL2FLD(ADC_SL0CFG_CHSEL0, pSlotConfig->eChannel);

    //
    // Enable window comparison if configured.
    //
    ui32Config |= _VAL2FLD(ADC_SL0CFG_WCEN0, pSlotConfig->bWindowCompare);

    //
    // Enable the slot if configured.
    //
    ui32Config |= _VAL2FLD(ADC_SL0CFG_SLEN0, pSlotConfig->bEnabled);

    //
    // Locate the correct register for this ADC slot.
    //
    ui32RegOffset = ((uint32_t)&ADCn(ui32Module)->SL0CFG) + (4 * ui32SlotNumber);

    //
    // Write the register with the caller's configuration value.
    //
    AM_REGVAL(ui32RegOffset) = ui32Config;

    //
    // Update the nubmer of slots configured.
    //
    g_ADCSlotsConfigured++;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC DMA configuration function
//!
//! @param handle   - handle for the module instance.
//! @param pConfig  - pointer to the configuration structure.
//!
//! This function configures the ADC DMA for operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_configure_dma(void *pHandle,
                         am_hal_adc_dma_config_t *pDMAConfig)
{
    uint32_t    ui32Config;
    uint32_t    ui32Module = ((am_hal_adc_state_t *)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    ui32Config = 0;

    //
    // Configure the DMA complete power-off.
    //
    ui32Config |= _VAL2FLD(ADC_DMACFG_DPWROFF, 0);      // DPWROFF not supported!

    //
    // Configure the data to be transferred.
    //
    if ( g_ADCSlotsConfigured > 1 )
    {
        // Need slot number to distinguish between slot results.
        ui32Config |= _VAL2FLD(ADC_DMACFG_DMAMSK, ADC_DMACFG_DMAMSK_DIS);
    }
    else
    {
        ui32Config |= _VAL2FLD(ADC_DMACFG_DMAMSK, ADC_DMACFG_DMAMSK_EN);
    }

    //
    // Enable DMA Halt on Status (DMAERR or DMACPL) by default.
    //
    ui32Config |= _VAL2FLD(ADC_DMACFG_DMAHONSTAT, ADC_DMACFG_DMAHONSTAT_EN);

    //
    // Configure the DMA dynamic priority handling.
    //
    ui32Config |= _VAL2FLD(ADC_DMACFG_DMADYNPRI, pDMAConfig->bDynamicPriority);

    //
    // Configure the DMA static priority.
    //
    ui32Config |= _VAL2FLD(ADC_DMACFG_DMAPRI, pDMAConfig->ePriority);

    //
    // Enable the DMA (does not start until ADC is enabled and triggered).
    //
    ui32Config |= _VAL2FLD(ADC_DMACFG_DMAEN, ADC_DMACFG_DMAEN_EN);

    //
    // Set the DMA configuration.
    //
    ADCn(ui32Module)->DMACFG = ui32Config;

    //
    // Set the DMA transfer count.
    //
    ADCn(ui32Module)->DMATOTCOUNT_b.TOTCOUNT = pDMAConfig->ui32SampleCount;

    //
    // Set the DMA target address.
    //
    ADCn(ui32Module)->DMATARGADDR = pDMAConfig->ui32TargetAddress;

    //
    // Set the DMA trigger on FIFO 75% full.
    //
    ADCn(ui32Module)->DMATRIGEN = ADC_DMATRIGEN_DFIFO75_Msk;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC device specific control function.
//!
//! @param handle   - handle for the module instance.
//!
//! This function provides for special control functions for the ADC operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t am_hal_adc_control(void *pHandle,
                            am_hal_adc_request_e eRequest,
                            void *pArgs)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t *)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    switch ( eRequest )
    {
        case AM_HAL_ADC_REQ_WINDOW_CONFIG:
        {
            am_hal_adc_window_config_t *pWindowConfig = (am_hal_adc_window_config_t *)pArgs;

#ifndef AM_HAL_DISABLE_API_VALIDATION
            //
            // Check the window limits.
            //
            if ( (pWindowConfig->ui32Upper > ADC_WULIM_ULIM_Msk)   ||
                 (pWindowConfig->ui32Lower > ADC_WLLIM_LLIM_Msk) )
            {
                return AM_HAL_STATUS_OUT_OF_RANGE;
            }
#endif // AM_HAL_DISABLE_API_VALIDATION
            //
            // Set the window comparison upper and lower limits.
            //
            ADCn(ui32Module)->WULIM = _VAL2FLD(ADC_WULIM_ULIM, pWindowConfig->ui32Upper);
            ADCn(ui32Module)->WLLIM = _VAL2FLD(ADC_WLLIM_LLIM, pWindowConfig->ui32Lower);

            //
            // Set the window scale per precision mode if indicated.
            //
            ADCn(ui32Module)->SCWLIM = _VAL2FLD(ADC_SCWLIM_SCWLIMEN,
                                                pWindowConfig->bScaleLimits);
        }
        break;

        case AM_HAL_ADC_REQ_TEMP_CELSIUS_GET:
            //
            // pArgs must point to an array of 3 floats.  To assure that the
            // array is valid, upon calling the 3rd float (pArgs[2]) must be
            // set to the to the value -123.456F.
            //
            if ( pArgs != NULL )
            {
                float *pfArray = (float*)pArgs;
                float fTemp, fCalibration_temp, fCalibration_voltage, fCalibration_offset, fVoltage;

                if ( pfArray[2] == -123.456F )
                {
                    //
                    // Get the scaled voltage obtained from the ADC sample.
                    // The ADC sample value is scaled up by the reference voltage
                    // (e.g. 1.5F), then divided by 65536.0F.
                    //
                    fVoltage = pfArray[0];

                    //
                    // Get calibration temperature from trimmed values & convert to degrees K.
                    //
                    fCalibration_temp = priv_temp_trims.flt.fCalibrationTemperature;
                    fCalibration_voltage = priv_temp_trims.flt.fCalibrationVoltage;
                    fCalibration_offset  = priv_temp_trims.flt.fCalibrationOffset;

                    //
                    // Compute the temperature.
                    //
                    fTemp  = fCalibration_temp;
                    fTemp /= (fCalibration_voltage - fCalibration_offset);
                    fTemp *= (fVoltage - fCalibration_offset);

                    //
                    // Give it back to the caller in Celsius.
                    //
                    pfArray[1] = fTemp - 273.15f;
                }
                else
                {
                    return AM_HAL_STATUS_INVALID_OPERATION;
                }
            }
            else
            {
                return AM_HAL_STATUS_INVALID_ARG;
            }
            break;

        case AM_HAL_ADC_REQ_TEMP_TRIMS_GET:
            //
            // pArgs must point to an array of 4 floats.  To assure that the
            // array is valid, upon calling the 4th float (pArgs[3]) must be
            // set to the value -123.456.
            // On return, pArgs[3] is set to 1 if the returned values are
            //  calibrated, or 0 if default calibration values.
            //
            if ( pArgs != NULL )
            {
                float *pfArray = (float*)pArgs;
                if ( pfArray[3] == -123.456F )
                {
                    //
                    // Return trim temperature as a float.
                    //
                    pfArray[0] = priv_temp_trims.flt.fCalibrationTemperature;

                    //
                    // Return trim voltage as a float.
                    //
                    pfArray[1] = priv_temp_trims.flt.fCalibrationVoltage;

                    //
                    // Return trim ADC offset voltage as a float.
                    //
                    pfArray[2] = priv_temp_trims.flt.fCalibrationOffset;

                    //
                    // Set the calibrated or uncalibrated flag
                    //
                    ((uint32_t*)pArgs)[3] = priv_temp_trims.ui32.bMeasured;
                }
                else
                {
                    return AM_HAL_STATUS_INVALID_OPERATION;
                }
            }
            else
            {
                return AM_HAL_STATUS_INVALID_ARG;
            }
            break;

        default:
            return AM_HAL_STATUS_INVALID_ARG;
    }

    //
    // Return status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC enable function
//!
//! @param handle   - handle for the module instance.
//!
//! This function enables the ADC operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_enable(void *pHandle)
{
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;
    uint32_t            ui32Module = pADCState->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }

    if ( pADCState->prefix.s.bEnable )
    {
        return AM_HAL_STATUS_SUCCESS;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Enable the ADC.
    //
    ADCn(ui32Module)->CFG_b.ADCEN = 0x1;

    //
    // Set flag to indicate module is enabled.
    //
    pADCState->prefix.s.bEnable = true;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC disable function
//!
//! @param handle   - handle for the module instance.
//!
//! This function disables the ADC operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_disable(void *pHandle)
{
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;
    uint32_t            ui32Module = pADCState->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Disable the ADC.
    //
    ADCn(ui32Module)->CFG_b.ADCEN = 0x0;

    //
    // Set flag to indicate module is disabled.
    //
    pADCState->prefix.s.bEnable = false;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC status function
//!
//! @param handle       - handle for the interface.
//!
//! This function returns the current status of the DMA operation.
//!
//! @return status      - DMA status flags.
//
//*****************************************************************************
uint32_t
am_hal_adc_status_get(void *pHandle, am_hal_adc_status_t *pStatus )
{
    uint32_t    ui32Module = ((am_hal_adc_state_t *)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Get the power status.
    //
    pStatus->bPoweredOn = (ADCn(ui32Module)->STAT & ADC_STAT_PWDSTAT_Msk) ==
                          _VAL2FLD(ADC_STAT_PWDSTAT, ADC_STAT_PWDSTAT_ON);

    //
    // Get the low power mode 1 status.
    //
    pStatus->bLPMode1 = (ADCn(ui32Module)->STAT & ADC_STAT_PWDSTAT_Msk) ==
                        _VAL2FLD(ADC_STAT_PWDSTAT, ADC_STAT_PWDSTAT_POWERED_DOWN);

    //
    //  Get the DMA status.
    //
    pStatus->bErr = ((ADCn(ui32Module)->DMASTAT & ADC_DMASTAT_DMAERR_Msk) > 0);
    pStatus->bCmp = ((ADCn(ui32Module)->DMASTAT & ADC_DMASTAT_DMACPL_Msk) > 0);
    pStatus->bTIP = ((ADCn(ui32Module)->DMASTAT & ADC_DMASTAT_DMATIP_Msk) > 0);

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC enable interrupts function
//!
//! @param handle       - handle for the interface.
//! @param ui32IntMask  - ADC interrupt mask.
//!
//! This function enables the specific indicated interrupts.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_interrupt_enable(void *pHandle, uint32_t ui32IntMask)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Enable the interrupts.
    //
    ADCn(ui32Module)->INTEN |= ui32IntMask;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC disable interrupts function
//!
//! @param handle       - handle for the interface.
//! @param ui32IntMask  - ADC interrupt mask.
//!
//! This function disable the specific indicated interrupts.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_interrupt_disable(void *pHandle, uint32_t ui32IntMask)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Disable the interrupts.
    //
    ADCn(ui32Module)->INTEN &= ~ui32IntMask;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC interrupt status function
//!
//! @param handle       - handle for the interface.
//!
//! This function returns the specific indicated interrupt status.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_interrupt_status(void *pHandle,
                            uint32_t  *pui32Status,
                            bool bEnabledOnly)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // if requested, only return the interrupts that are enabled.
    //
    if ( bEnabledOnly )
    {
        uint32_t ui32RetVal = ADCn(ui32Module)->INTSTAT;
        *pui32Status = ADCn(ui32Module)->INTEN & ui32RetVal;
    }
    else
    {
        *pui32Status = ADCn(ui32Module)->INTSTAT;
    }

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}


//*****************************************************************************
//
//! @brief ADC interrupt clear
//!
//! @param handle         - handle for the interface.
//! @param ui32IntMask    - uint32_t for interrupts to clear
//!
//! This function clears the interrupts for the given peripheral.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_interrupt_clear(void *pHandle, uint32_t ui32IntMask)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Clear the interrupts.
    //
    ADCn(ui32Module)->INTCLR = ui32IntMask;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
// ADC sample read function
//
// This function reads samples from the ADC FIFO or an SRAM sample buffer
// returned by a DMA operation.
//
//*****************************************************************************
uint32_t am_hal_adc_samples_read(void *pHandle, bool bFullSample,
                                 uint32_t *pui32InSampleBuffer,
                                 uint32_t *pui32InOutNumberSamples,
                                 am_hal_adc_sample_t *pui32OutBuffer)
{
    uint32_t      ui32Sample;
    uint32_t      ui32RequestedSamples = *pui32InOutNumberSamples;

    uint32_t ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }

    //
    // Check the output sample buffer pointer.
    //
    if ( NULL == pui32OutBuffer )
    {
        return AM_HAL_STATUS_INVALID_ARG;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION


    *pui32InOutNumberSamples = 0;

    //
    // Check if we are reading directly from FIFO or DMA SRAM buffer.
    //
    if ( NULL == pui32InSampleBuffer )
    {
        //
        // Grab a value from the ADC FIFO
        //
        do
        {
            ui32Sample = ADCn(ui32Module)->FIFOPR;
            pui32OutBuffer->ui32Slot   = AM_HAL_ADC_FIFO_SLOT(ui32Sample);
            pui32OutBuffer->ui32Sample = bFullSample                             ?
                                         AM_HAL_ADC_FIFO_FULL_SAMPLE(ui32Sample) :
                                         AM_HAL_ADC_FIFO_SAMPLE(ui32Sample);
            pui32OutBuffer++;
            (*pui32InOutNumberSamples)++;
        } while ((AM_HAL_ADC_FIFO_COUNT(ui32Sample) > 0) &&
                 (*pui32InOutNumberSamples < ui32RequestedSamples));
    }
    else
    {
        //
        // Process the samples from the provided sample buffer
        //
        do
        {
            ui32Sample = ADCn(ui32Module)->FIFOPR;
            pui32OutBuffer->ui32Slot   = AM_HAL_ADC_FIFO_SLOT(*pui32InSampleBuffer);
            pui32OutBuffer->ui32Sample = AM_HAL_ADC_FIFO_SAMPLE(*pui32InSampleBuffer);
            pui32InSampleBuffer++;
            pui32OutBuffer++;
            (*pui32InOutNumberSamples)++;
        } while (*pui32InOutNumberSamples < ui32RequestedSamples);
    }

    //
    // Return FIFO valid bits.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief Issue Software Trigger to the ADC.
//!
//! @param handle   - handle for the module instance.
//!
//! This function triggers the ADC operation.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_sw_trigger(void *pHandle)
{
    uint32_t    ui32Module = ((am_hal_adc_state_t*)pHandle)->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Write to the Software trigger register in the ADC.
    //
    ADCn(ui32Module)->SWT = 0x37;

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
//! @brief ADC power control function
//!
//! @param handle       - handle for the interface.
//! @param ePowerState  - the desired power state to move the peripheral to.
//! @param bRetainState - flag (if true) to save/restore peripheral state upon
//!                       power state change.
//!
//! This function updates the peripheral to a given power state.
//!
//! @return status      - generic or interface specific status.
//
//*****************************************************************************
uint32_t
am_hal_adc_power_control(void *pHandle,
                         am_hal_sysctrl_power_state_e ePowerState,
                         bool bRetainState)
{
    am_hal_adc_state_t  *pADCState = (am_hal_adc_state_t *)pHandle;
    uint32_t            ui32Module = pADCState->ui32Module;

#ifndef AM_HAL_DISABLE_API_VALIDATION
    //
    // Check the handle.
    //
    if ( !AM_HAL_ADC_CHK_HANDLE(pHandle) )
    {
        return AM_HAL_STATUS_INVALID_HANDLE;
    }
#endif // AM_HAL_DISABLE_API_VALIDATION

    //
    // Decode the requested power state and update MSPI operation accordingly.
    //
    switch (ePowerState)
    {
        case AM_HAL_SYSCTRL_WAKE:
            if ( bRetainState  &&  !pADCState->registerState.bValid )
            {
                return AM_HAL_STATUS_INVALID_OPERATION;
            }

            //
            // Enable the ADC power domain.
            //
            am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_PERIPH_ADC);

            if ( bRetainState )
            {
                ADCn(ui32Module)->SL0CFG = pADCState->registerState.regSL0CFG;
                ADCn(ui32Module)->SL1CFG = pADCState->registerState.regSL1CFG;
                ADCn(ui32Module)->SL2CFG = pADCState->registerState.regSL2CFG;
                ADCn(ui32Module)->SL3CFG = pADCState->registerState.regSL3CFG;
                ADCn(ui32Module)->SL4CFG = pADCState->registerState.regSL4CFG;
                ADCn(ui32Module)->SL5CFG = pADCState->registerState.regSL5CFG;
                ADCn(ui32Module)->SL6CFG = pADCState->registerState.regSL6CFG;
                ADCn(ui32Module)->SL7CFG = pADCState->registerState.regSL7CFG;
                ADCn(ui32Module)->WULIM  = pADCState->registerState.regWULIM;
                ADCn(ui32Module)->WLLIM  = pADCState->registerState.regWLLIM;
                ADCn(ui32Module)->INTEN  = pADCState->registerState.regINTEN;
                ADCn(ui32Module)->CFG    = pADCState->registerState.regCFG;

                pADCState->registerState.bValid     = false;
            }
            break;

        case AM_HAL_SYSCTRL_NORMALSLEEP:
        case AM_HAL_SYSCTRL_DEEPSLEEP:
            if ( bRetainState )
            {
                pADCState->registerState.regSL0CFG  = ADCn(ui32Module)->SL0CFG;
                pADCState->registerState.regSL1CFG  = ADCn(ui32Module)->SL1CFG;
                pADCState->registerState.regSL2CFG  = ADCn(ui32Module)->SL2CFG;
                pADCState->registerState.regSL3CFG  = ADCn(ui32Module)->SL3CFG;
                pADCState->registerState.regSL4CFG  = ADCn(ui32Module)->SL4CFG;
                pADCState->registerState.regSL5CFG  = ADCn(ui32Module)->SL5CFG;
                pADCState->registerState.regSL6CFG  = ADCn(ui32Module)->SL6CFG;
                pADCState->registerState.regSL7CFG  = ADCn(ui32Module)->SL7CFG;
                pADCState->registerState.regWULIM   = ADCn(ui32Module)->WULIM;
                pADCState->registerState.regWLLIM   = ADCn(ui32Module)->WLLIM;
                pADCState->registerState.regINTEN   = ADCn(ui32Module)->INTEN;
                pADCState->registerState.regCFG     = ADCn(ui32Module)->CFG;

                pADCState->registerState.bValid     = true;
            }

            //
            // Disable the ADC power domain.
            //
            am_hal_pwrctrl_periph_disable(AM_HAL_PWRCTRL_PERIPH_ADC);
            break;

        default:
            return AM_HAL_STATUS_INVALID_ARG;
    }

    //
    // Return the status.
    //
    return AM_HAL_STATUS_SUCCESS;
}

//*****************************************************************************
//
// End Doxygen group.
//! @}
//
//*****************************************************************************
