/**
 * \file
 *
 * \brief ADC Controller driver.
 *
 * Copyright (c) 2013-2015 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * 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. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 *
 * \asf_license_stop
 *
 */
/*
 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
 */

#ifndef ADC2_H_INCLUDED
#define ADC2_H_INCLUDED

#include "compiler.h"
#include "status_codes.h"

#if (SAM4N)
#define TEMP_SENSOR
#define SLEEP_MODE_ADC    SLEEPMGR_SLEEP_WFI
#endif

#if (SAMG)
#define NO_TEMP_SENSOR
#define SLEEP_MODE_ADC    SLEEPMGR_ACTIVE
#endif

/** Write Protect Key */
#ifndef ADC_WPMR_WPKEY_PASSWD
#define ADC_WPMR_WPKEY_PASSWD    (0x414443u << 8)
#endif

/** Definitions for ADC resolution */
enum adc_resolution {
#if SAMG55
    ADC_12_BITS = ADC_EMR_OSR_NO_AVERAGE,     /* ADC 12-bit resolution */
    ADC_13_BITS = ADC_EMR_OSR_OSR4,           /* ADC 13-bit resolution */
    ADC_14_BITS = ADC_EMR_OSR_OSR16,          /* ADC 14-bit resolution */
    ADC_15_BITS = ADC_EMR_OSR_OSR64,          /* ADC 15-bit resolution */
    ADC_16_BITS = ADC_EMR_OSR_OSR256,         /* ADC 16-bit resolution */
#else
    ADC_8_BITS = ADC_MR_LOWRES_BITS_8,        /* ADC 8-bit resolution */
    ADC_10_BITS = ADC_MR_LOWRES_BITS_10,      /* ADC 10-bit resolution */
    ADC_11_BITS = ADC_EMR_OSR_OSR4,           /* ADC 11-bit resolution */
    ADC_12_BITS = ADC_EMR_OSR_OSR16           /* ADC 12-bit resolution */
#endif
};

/** Definitions for ADC power mode */
enum adc_power_mode {
    /* ADC core on and reference voltage circuitry on */
    ADC_POWER_MODE_0 = 0,
    /* ADC core off and reference voltage circuitry off */
    ADC_POWER_MODE_1
};

/** Definitions for ADC trigger */
enum adc_trigger {
    /* Starting a conversion is only possible by software. */
    ADC_TRIG_SW = ADC_MR_TRGEN_DIS,
    /* External trigger */
    ADC_TRIG_EXT = ADC_MR_TRGSEL_ADC_TRIG0 | ADC_MR_TRGEN,
    /* TIO Output of the Timer Counter Channel 0 */
    ADC_TRIG_TIO_CH_0 = ADC_MR_TRGSEL_ADC_TRIG1 | ADC_MR_TRGEN,
    /* TIO Output of the Timer Counter Channel 1 */
    ADC_TRIG_TIO_CH_1 = ADC_MR_TRGSEL_ADC_TRIG2 | ADC_MR_TRGEN,
    /* TIO Output of the Timer Counter Channel 2 */
    ADC_TRIG_TIO_CH_2 = ADC_MR_TRGSEL_ADC_TRIG3 | ADC_MR_TRGEN,
#if (SAMG)
    /* RTCOUT0 */
    ADC_TRIG_RTC_0 = ADC_MR_TRGSEL_ADC_TRIG4 | ADC_MR_TRGEN,
    /* RTTINC */
    ADC_TRIG_RTT = ADC_MR_TRGSEL_ADC_TRIG5 | ADC_MR_TRGEN,
#endif
    /* Freerun mode conversion. */
    ADC_TRIG_FREERUN = 0xFF
};

/** Definitions for ADC channel number */
enum adc_channel_num {
    ADC_CHANNEL_0 = 0,
    ADC_CHANNEL_1,
    ADC_CHANNEL_2,
    ADC_CHANNEL_3,
    ADC_CHANNEL_4,
    ADC_CHANNEL_5,
    ADC_CHANNEL_6,
    ADC_CHANNEL_7,
#if (SAM4N)
    ADC_CHANNEL_8,
    ADC_CHANNEL_9,
    ADC_CHANNEL_10,
    ADC_CHANNEL_11,
    ADC_CHANNEL_12,
    ADC_CHANNEL_13,
    ADC_CHANNEL_14,
    ADC_CHANNEL_15,
#endif
#ifdef TEMP_SENSOR
    ADC_TEMPERATURE_SENSOR,
#endif
    ADC_CHANNEL_ALL = 0xFFFF
};

/** Definitions for ADC Start Up Time */
enum adc_startup_time {
    ADC_STARTUP_TIME_0 = ADC_MR_STARTUP_SUT0,
    ADC_STARTUP_TIME_1 = ADC_MR_STARTUP_SUT8,
    ADC_STARTUP_TIME_2 = ADC_MR_STARTUP_SUT16,
    ADC_STARTUP_TIME_3 = ADC_MR_STARTUP_SUT24,
    ADC_STARTUP_TIME_4 = ADC_MR_STARTUP_SUT64,
    ADC_STARTUP_TIME_5 = ADC_MR_STARTUP_SUT80,
    ADC_STARTUP_TIME_6 = ADC_MR_STARTUP_SUT96,
    ADC_STARTUP_TIME_7 = ADC_MR_STARTUP_SUT112,
    ADC_STARTUP_TIME_8 = ADC_MR_STARTUP_SUT512,
    ADC_STARTUP_TIME_9 = ADC_MR_STARTUP_SUT576,
    ADC_STARTUP_TIME_10 = ADC_MR_STARTUP_SUT640,
    ADC_STARTUP_TIME_11 = ADC_MR_STARTUP_SUT704,
    ADC_STARTUP_TIME_12 = ADC_MR_STARTUP_SUT768,
    ADC_STARTUP_TIME_13 = ADC_MR_STARTUP_SUT832,
    ADC_STARTUP_TIME_14 = ADC_MR_STARTUP_SUT896,
    ADC_STARTUP_TIME_15 = ADC_MR_STARTUP_SUT960
};

/** Definitions for Comparison Mode */
enum adc_cmp_mode {
    ADC_CMP_MODE_0 = ADC_EMR_CMPMODE_LOW,
    ADC_CMP_MODE_1 = ADC_EMR_CMPMODE_HIGH,
    ADC_CMP_MODE_2 = ADC_EMR_CMPMODE_IN,
    ADC_CMP_MODE_3 = ADC_EMR_CMPMODE_OUT
};

#ifdef TEMP_SENSOR
/** Definitions for Temperature Comparison Mode */
enum adc_temp_cmp_mode {
    ADC_TEMP_CMP_MODE_0 = ADC_TEMPMR_TEMPCMPMOD_LOW,
    ADC_TEMP_CMP_MODE_1 = ADC_TEMPMR_TEMPCMPMOD_HIGH,
    ADC_TEMP_CMP_MODE_2 = ADC_TEMPMR_TEMPCMPMOD_IN,
    ADC_TEMP_CMP_MODE_3 = ADC_TEMPMR_TEMPCMPMOD_OUT
};
#endif
#if (SAMG)
/** Definitions for Last Channel Specific Measurement Comparison Mode */
enum adc_last_channel_cmp_mode {
    ADC_LAST_CHANNEL_CMP_MODE_0 = ADC_LCTMR_CMPMOD_LOW,
    ADC_LAST_CHANNEL_CMP_MODE_1 = ADC_LCTMR_CMPMOD_HIGH,
    ADC_LAST_CHANNEL_CMP_MODE_2 = ADC_LCTMR_CMPMOD_IN,
    ADC_LAST_CHANNEL_CMP_MODE_3 = ADC_LCTMR_CMPMOD_OUT
};
#endif

/** Definitions for Reference Voltage Selection */
enum adc_refer_voltage_source {
    ADC_REFER_VOL_EXTERNAL = 0,
    ADC_REFER_VOL_STUCK_AT_MIN,
    ADC_REFER_VOL_VDDANA,
    ADC_REFER_VOL_IRVS
};

/**
 * \brief ADC Enhanced configuration structure.
 *
 * Configuration structure for a ADC Enhanced instance.
 * This structure could be initialized by the \ref ADC_get_config_defaults()
 * function before being modified by the user application.
 */
struct adc_config {
    /** Resolution */
    enum adc_resolution resolution;
    /** Master Clock */
    uint32_t mck;
    /** ADC Clock */
    uint32_t adc_clock;
    /** Start Up Time */
    enum adc_startup_time startup_time;
    /** Tracking Time = (tracktim+1) / ADC clock */
    uint8_t tracktim;
    /** Transfer Period */
    uint8_t transfer;
    /** Use Sequence Enable */
    bool useq;
    /** TAG of ADC_LDCR register */
    bool tag;
    /** Averaging on Single Trigger Event */
    bool aste;
};

#ifdef TEMP_SENSOR
/** ADC Temperature Sensor configuration structure.*/
struct adc_temp_sensor_config {
    /** Temperature Sensor On */
    bool tempon;
    /** Temperature Comparison Mode */
    enum adc_temp_cmp_mode mode;
    /** Temperature Low Threshold */
    uint16_t low_threshold;
    /** Temperature High Threshold */
    uint16_t high_threshold;
};
#endif

#if (SAMG)
/** ADC Last Channel Specific Measurement configuration structure.*/
struct adc_last_channel_config {
    /** Specific Measurement On */
    bool dual_trig_on;
    /** Specific Measurement Comparison Mode */
    enum adc_last_channel_cmp_mode mode;
    /** Specific Measurement Low Threshold */
    uint16_t low_threshold;
    /** Specific Measurement High Threshold */
    uint16_t high_threshold;
};
#endif

/** ADC interrupt source type */
enum adc_interrupt_source {
    ADC_INTERRUPT_EOC_0 = 0,
    ADC_INTERRUPT_EOC_1,
    ADC_INTERRUPT_EOC_2,
    ADC_INTERRUPT_EOC_3,
    ADC_INTERRUPT_EOC_4,
    ADC_INTERRUPT_EOC_5,
    ADC_INTERRUPT_EOC_6,
    ADC_INTERRUPT_EOC_7,
#if (SAM4N)
    ADC_INTERRUPT_EOC_8,
    ADC_INTERRUPT_EOC_9,
    ADC_INTERRUPT_EOC_10,
    ADC_INTERRUPT_EOC_11,
    ADC_INTERRUPT_EOC_12,
    ADC_INTERRUPT_EOC_13,
    ADC_INTERRUPT_EOC_14,
    ADC_INTERRUPT_EOC_15,
    ADC_INTERRUPT_EOC_16,
#endif
#ifdef TEMP_SENSOR
    ADC_INTERRUPT_TEMP_CHANGE,
#endif
    ADC_INTERRUPT_END_CAL,
    ADC_INTERRUPT_DATA_READY,
    ADC_INTERRUPT_OVERRUN_ERROR,
    ADC_INTERRUPT_COMP_ERROR,
    ADC_INTERRUPT_END_RXBUF,
    ADC_INTERRUPT_RXBUF_FULL,
    ADC_INTERRUPT_ALL = 0xFFFFFFFF
};

typedef void (*adc_callback_t)(void);

void adc_get_config_defaults(struct adc_config *const cfg);
enum status_code adc_init(Adc *const adc, struct adc_config *const config);

#ifdef TEMP_SENSOR
void adc_temp_sensor_get_config_defaults(
    struct adc_temp_sensor_config *const cfg);
void adc_temp_sensor_set_config(Adc *const adc,
                                struct adc_temp_sensor_config *config);
#endif
#if (SAMG)
void adc_last_channel_get_config_defaults(
    struct adc_last_channel_config *const cfg);
void adc_last_channel_set_config(Adc *const adc,
                                 struct adc_last_channel_config *config);
#endif

void adc_configure_sequence(Adc *const adc,
                            const enum adc_channel_num ch_list[], const uint8_t uc_num);
void adc_enable(void);
void adc_disable(void);
void adc_set_callback(Adc *const adc, enum adc_interrupt_source source,
                      adc_callback_t callback, uint8_t irq_level);

/**
 * \internal
 * \brief ADC channel sanity check
 *
 * \param adc  Base address of the ADC.
 * \param channel  Adc channel number.
 *
 */
static inline void adc_ch_sanity_check(Adc *const adc,
                                       const enum adc_channel_num channel)
{
    if (adc == ADC) {
        Assert((channel < NB_CH_ADC)
#ifdef TEMP_SENSOR
               ||(channel == ADC_TEMPERATURE_SENSOR)
#endif
              );
    }

    UNUSED(channel);
}

#if (SAMG)
#if SAMG55
/**
 * \brief Configure ADC clock to mck.
 *
 * \param adc  Base address of the ADC.
 *
 */
static inline void adc_select_clock_source_mck(Adc *const adc)
{
    uint32_t reg;

    reg = adc->ADC_EMR;

    reg &= ~ADC_EMR_SRCCLK_PMC_PCK;

    adc->ADC_EMR = reg;
}

/**
 * \brief Configure ADC clock to pck.
 *
 * \param adc  Base address of the ADC.
 *
 */
static inline void adc_select_clock_source_pck(Adc *const adc)
{
    uint32_t reg;

    reg = adc->ADC_EMR;

    reg |= ADC_EMR_SRCCLK_PMC_PCK;

    adc->ADC_EMR = reg;
}

#else
/**
 * \brief Configure ADC clock to MCK.
 *
 * \param adc  Base address of the ADC.
 *
 */
static inline void adc_set_clock_mck(Adc *const adc)
{
    uint32_t reg;

    reg = adc->ADC_EMR;

    reg |= ADC_MR_DIV1;

    adc->ADC_MR = reg;
}

/**
 * \brief Configure ADC clock to MCK/3.
 *
 * \param adc  Base address of the ADC.
 *
 */
static inline void adc_set_clock_mck_div3(Adc *const adc)
{
    uint32_t reg;

    reg = adc->ADC_MR;

    reg &= ~ADC_MR_DIV1;
    reg |= ADC_MR_DIV3;

    adc->ADC_MR = reg;
}
#endif
#endif

/**
 * \brief Configure conversion trigger and free run mode.
 *
 * \param adc  Base address of the ADC.
 * \param trigger Conversion trigger.
 *
 */
static inline void adc_set_trigger(Adc *const adc,
                                   const enum adc_trigger trigger)
{
    uint32_t reg;

    reg = adc->ADC_MR;

    if (trigger == ADC_TRIG_FREERUN) {
        reg |= ADC_MR_FREERUN_ON;
    } else {
        reg &= ~(ADC_MR_TRGSEL_Msk | ADC_MR_TRGEN | ADC_MR_FREERUN_ON);
        reg |= trigger;
    }

    adc->ADC_MR = reg;
}

void adc_set_resolution(Adc *const adc,
                        const enum adc_resolution res);

void adc_set_comparison_mode(Adc *const adc,
                             const enum adc_cmp_mode mode,
                             const enum adc_channel_num channel,
                             uint8_t cmp_filter);

/**
 * \brief Get comparison mode.
 *
 * \param adc  Base address of the ADC.
 *
 * \retval Compare mode value.
 */
static inline enum adc_cmp_mode adc_get_comparison_mode(Adc *const adc)
{
    return (enum adc_cmp_mode)(adc->ADC_EMR & ADC_EMR_CMPMODE_Msk);
}

/**
 * \brief Configure ADC compare window.
 *
 * \param adc  Base address of the ADC.
 * \param us_low_threshold Low threshold of compare window.
 * \param us_high_threshold High threshold of compare window.
 */
static inline void adc_set_comparison_window(Adc *const adc,
        const uint16_t us_low_threshold,
        const uint16_t us_high_threshold)
{
    adc->ADC_CWR = ADC_CWR_LOWTHRES(us_low_threshold) |
                   ADC_CWR_HIGHTHRES(us_high_threshold);
}

/**
 * \brief Enable or disable write protection of ADC registers.
 *
 * \param adc  Base address of the ADC.
 * \param is_enable 1 to enable, 0 to disable.
 */
static inline void adc_set_writeprotect(Adc *const adc,
                                        const bool is_enable)
{
    if (is_enable) {
        adc->ADC_WPMR = ADC_WPMR_WPEN | ADC_WPMR_WPKEY_PASSWD;
    } else {
        adc->ADC_WPMR = ADC_WPMR_WPKEY_PASSWD;
    }
}

/**
 * \brief Indicate write protect status.
 *
 * \param adc  Base address of the ADC.
 *
 * \return 0 if no write protect violation occurred, or 16-bit write protect
 * violation source.
 */
static inline uint32_t adc_get_writeprotect_status(Adc *const adc)
{
    uint32_t reg_value;

    reg_value = adc->ADC_WPSR;
    if (reg_value & ADC_WPSR_WPVS) {
        return (reg_value & ADC_WPSR_WPVSRC_Msk) >> ADC_WPSR_WPVSRC_Pos;
    } else {
        return 0;
    }
}

/**
 * \brief Get ADC overrun error status.
 *
 * \param adc  Base address of the ADC.
 *
 * \return ADC overrun error status.
 */
static inline uint32_t adc_get_overrun_status(Adc *const adc)
{
    return adc->ADC_OVER;
}

/**
 * \brief Set ADC averaging on single trigger event
 *
 * \param adc Base address of the ADC.
 */
static inline void adc_average_on_single_trigger(Adc *const adc)
{
    adc->ADC_EMR |= ADC_EMR_ASTE_SINGLE_TRIG_AVERAGE;
}

/**
 * \brief Set ADC averaging on serval trigger events
 *
 * \param adc Base address of the ADC.
 */
static inline void adc_average_on_multi_trigger(Adc *const adc)
{
    adc->ADC_EMR &= ~ADC_EMR_ASTE_SINGLE_TRIG_AVERAGE;
}

/**
 * \brief Start analog-to-digital conversion.
 *
 * \note If one of the hardware event is selected as ADC trigger,
 * this function can NOT start analog to digital conversion.
 *
 * \param adc  Base address of the ADC.
 */
static inline void adc_start_software_conversion(Adc *const adc)
{
    adc->ADC_CR = ADC_CR_START;
}

void adc_set_power_mode(Adc *const adc,
                        const enum adc_power_mode mode);

/**
 * \brief Enable the specified ADC channel.
 *
 * \param adc  Base address of the ADC.
 * \param adc_ch Adc channel number.
 */
static inline void adc_channel_enable(Adc *const adc,
                                      const enum adc_channel_num adc_ch)
{
    if (adc_ch != ADC_CHANNEL_ALL) {
        adc_ch_sanity_check(adc, adc_ch);
    }

    adc->ADC_CHER = (adc_ch == ADC_CHANNEL_ALL) ?
                    ADC_CHANNEL_ALL : 1 << adc_ch;
}

/**
 * \brief Disable the specified ADC channel.
 *
 * \param adc  Base address of the ADC.
 * \param adc_ch Adc channel number.
 */
static inline void adc_channel_disable(Adc *const adc,
                                       const enum adc_channel_num adc_ch)
{
    if (adc_ch != ADC_CHANNEL_ALL) {
        adc_ch_sanity_check(adc, adc_ch);
    }

    adc->ADC_CHDR = (adc_ch == ADC_CHANNEL_ALL) ?
                    ADC_CHANNEL_ALL : 1 << adc_ch;
}

/**
 * \brief Get the ADC channel status.
 *
 * \param adc  Base address of the ADC.
 * \param adc_ch Adc channel number.
 *
 * \retval 1 if channel is enabled.
 * \retval 0 if channel is disabled.
 */
static inline uint32_t adc_channel_get_status(Adc *const adc,
        const enum adc_channel_num adc_ch)
{
    adc_ch_sanity_check(adc, adc_ch);

    return adc->ADC_CHSR & (1 << adc_ch);
}

/**
 * \brief Read the Converted Data of the selected channel.
 *
 * \param adc  Base address of the ADC.
 * \param adc_ch Adc channel number.
 *
 * \return ADC converted value of the selected channel.
 */
static inline uint32_t adc_channel_get_value(Adc *const adc,
        enum adc_channel_num adc_ch)
{
    adc_ch_sanity_check(adc, adc_ch);

    return adc->ADC_CDR[adc_ch];
}

/**
 * \brief Get the Last Data Converted.
 *
 * \param adc  Base address of the ADC.
 *
 * \return ADC latest converted value.
 */
static inline uint32_t adc_get_latest_value(Adc *const adc)
{
    return adc->ADC_LCDR & ADC_LCDR_LDATA_Msk;
}

/**
 * \brief Get the Last Converted Channel Number.
 *
 * \param adc Base address of the ADC.
 *
 * \return ADC Last Converted Channel Number.
 */
static inline uint32_t adc_get_latest_chan_num(Adc *const adc)
{
#if SAMG55
    return (adc->ADC_LCDR & ADC_LCDR_CHNBOSR_Msk) >> ADC_LCDR_CHNBOSR_Pos;
#else
    return (adc->ADC_LCDR & ADC_LCDR_CHNB_Msk) >> ADC_LCDR_CHNB_Pos;
#endif
}

void adc_enable_interrupt(Adc *const adc,
                          enum adc_interrupt_source interrupt_source);

void adc_disable_interrupt(Adc *const adc,
                           enum adc_interrupt_source interrupt_source);

/**
 * \brief Get ADC interrupt status.
 *
 * \param adc  Base address of the ADC.
 *
 * \return The interrupt status value.
 */
static inline uint32_t adc_get_interrupt_status(Adc *const adc)
{
    return adc->ADC_ISR;
}

/**
 * \brief Get ADC interrupt mask.
 *
 * \param adc  Base address of the ADC.
 *
 * \return The interrupt mask value.
 */
static inline uint32_t adc_get_interrupt_mask(Adc *const adc)
{
    return adc->ADC_IMR;
}

/**
 * \brief Get PDC registers base address.
 *
 * \param adc  Base address of the ADC.
 *
 * \return Adc Pdc register base address.
 */
static inline Pdc *adc_get_pdc_base(Adc *const adc)
{
    Pdc *p_pdc_base = NULL;

    if (adc == ADC) {
        p_pdc_base = PDC_ADC;
    }

    return p_pdc_base;
}

/**
 * \brief Launch an automatic calibration of the ADC on next sequence.
 *
 * \param adc  Base address of the ADC.
 *
 * \retval STATUS_OK  An automatic calibration is launched.
 * \retval STATUS_ERR_BUSY  Automatic calibration can not be launched because
 *         the ADC is in freerun mode.
 */
static inline enum status_code adc_start_calibration(Adc *const adc)
{
    if ((adc->ADC_MR & ADC_MR_FREERUN) == ADC_MR_FREERUN_ON) {
        return STATUS_ERR_BUSY;
    }

    adc->ADC_CR = ADC_CR_AUTOCAL;
    return STATUS_OK;
}

#if (SAM4N)
/**
 * \brief ADC Reference Voltage Selection
 *
 * \param  adc  Base address of the ADC.
 * \param  adc_ref_src The source selection for ADC reference voltage,
 * ADC_REFER_VOL_EXTERNAL - the external pin ADVREF defines the voltage reference.
 * ADC_REFER_VOL_STUCK_AT_MIN - the internal reference voltage is stuck at the minimum value
 * ADC_REFER_VOL_VDDANA - the internal voltage reference is forced to VDDANA. Effective only if ONREF is 1.
 * ADC_REFER_VOL_IRVS - the internal reference voltage is defined by field IRVS
 * See the product electrical characteristics for further details.
 * \param  irvs Internal reference volatage selection, only be effective when
 *         adc_ref_src equals to ADC_REFER_VOL_IRVS
 */
static inline void adc_ref_vol_sel(Adc *const adc,
                                   enum adc_refer_voltage_source adc_ref_src,
                                   uint8_t irvs)
{
    if (ADC_REFER_VOL_EXTERNAL == adc_ref_src) {
        adc->ADC_ACR &= ~ADC_ACR_ONREF_EN;
    } else if (ADC_REFER_VOL_STUCK_AT_MIN == adc_ref_src) {
        adc->ADC_ACR |= ADC_ACR_ONREF_EN;
        adc->ADC_ACR &= ~(ADC_ACR_IRVCE_EN | ADC_ACR_FORCEREF_EN);
    } else if (ADC_REFER_VOL_VDDANA == adc_ref_src) {
        adc->ADC_ACR |= ADC_ACR_ONREF_EN | ADC_ACR_FORCEREF_EN;
    } else if (ADC_REFER_VOL_IRVS == adc_ref_src) {
        adc->ADC_ACR &= ~ADC_ACR_FORCEREF_EN;
        adc->ADC_ACR |= ADC_ACR_ONREF_EN | ADC_ACR_IRVCE_EN |
                        (irvs << ADC_ACR_IRVS_Pos);
    }
}
#endif

/**
 * \page sam_adc2_quickstart Quickstart guide for ADC driver
 *
 * This is the quickstart guide for the \ref sam_drivers_adc2_group
 * "ADC2 driver" with step-by-step instructions on how to configure and use
 * the driver in a selection of use cases.
 *
 * The use cases contain several code fragments. The code fragments in the
 * steps for setup can be copied into a custom initialization function, while
 * the steps for usage can be copied into, e.g., the main application function.
 *
 * \section adc_basic_use_case Basic use case
 * In this basic use case, the ADC module and single channel are configured for:
 * - 10 -bit resolution
 * - ADC clock frequency is 6MHz
 * - Start Up Time is 64 periods ADC clock
 * - Tracking Time is 3 periods of ADC clock
 * - Transfer Period field shall be programmed with 2 as datasheet said
 * - The controller converts channels in a simple numeric order
 * - Appends the channel number to the conversion result in AFE_LDCR register
 * - Single Trigger is optional to get an averaged value
 * - Software triggering of conversions
 * - Single channel measurement
 * - ADC_CHANNEL_1 of ADC as input
 *
 * \subsection sam_adc2_quickstart_prereq Prerequisites
 * -# \ref sysclk_group "System Clock Management (Sysclock)"
 *
 * \section adc_basic_use_case_setup Setup steps
 * \subsection adc_basic_use_case_setup_code Example code
 * Add to application C-file:
 * \code
	adc_enable();
	adc_get_config_defaults(&adc_cfg);
	adc_init(ADC, &adc_cfg);
	adc_set_trigger(ADC, ADC_TRIG_SW);
	adc_channel_enable(ADC, ADC_CHANNEL_1);
\endcode
 *
 * \subsection adc_basic_use_case_setup_flow Workflow
 * -# Enable ADC Module:
 *   - \code adc_enable(); \endcode
 * -# Get the ADC default configurations:
 *   - \code adc_get_config_defaults(&adc_cfg); \endcode
 * -# Initialize the ADC Module:
 *   - \code adc_init(ADC, &adc_cfg); \endcode
 * -# Configure conversion trigger and free run mode:
 *   - \code adc_set_trigger(ADC, ADC_TRIG_SW); \endcode
 * -# Enable Channel:
 *   - \code adc_channel_enable(ADC, ADC_CHANNEL_1); \endcode
 *
 * \section adc_basic_use_case_usage Usage steps
 * \subsection adc_basic_use_case_usage_code Example code
 * Add to, e.g., main loop in application C-file:
 * \code
	adc_start_software_conversion(ADC);
	while (adc_get_interrupt_status(ADC) & (1 << ADC_CHANNEL_1));
	uint32_t result = adc_channel_get_value(ADC, ADC_CHANNEL_1);
\endcode
 *
 * \subsection adc_basic_use_case_usage_flow Workflow
 * -# Start ADC conversion on channel:
 *   - \code adc_start_software_conversion(ADC); \endcode
 * -# Wait for the conversion over:
 *   - \code while (adc_get_interrupt_status(ADC) & (1 << ADC_CHANNEL_1));
\endcode
 * -# Get the conversion result:
 *   - \code uint32_t result = adc_channel_get_value(ADC, ADC_CHANNEL_1);
\endcode
 */
#endif /* ADC2_H_INCLUDED */
