/**
 * \file
 *
 * \brief Power Management Controller (PMC) driver for SAM.
 *
 * Copyright (c) 2011-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>
 */

#include "pmc.h"

#if (SAM3N)
# define MAX_PERIPH_ID    31
#elif (SAM3XA)
# define MAX_PERIPH_ID    44
#elif (SAM3U)
# define MAX_PERIPH_ID    29
#elif (SAM3S || SAM4S)
# define MAX_PERIPH_ID    34
#elif (SAM4E)
# define MAX_PERIPH_ID    47
#elif (SAMV71)
# define MAX_PERIPH_ID    63
#elif (SAMV70)
# define MAX_PERIPH_ID    63
#elif (SAME70)
# define MAX_PERIPH_ID    63
#elif (SAMS70)
# define MAX_PERIPH_ID    63
#elif (SAM4N)
# define MAX_PERIPH_ID    31
#elif (SAM4C || SAM4CM || SAM4CP)
# define MAX_PERIPH_ID    43
#elif (SAMG51)
# define MAX_PERIPH_ID    47
#elif (SAMG53)
# define MAX_PERIPH_ID    47
#elif (SAMG54)
# define MAX_PERIPH_ID    47
#elif (SAMG55)
# define MAX_PERIPH_ID    50
#endif

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond

    /**
     * \defgroup sam_drivers_pmc_group Power Management Controller (PMC)
     *
     * \par Purpose
     *
     * The Power Management Controller (PMC) optimizes power consumption by
     * controlling all system and user peripheral clocks. The PMC enables/disables
     * the clock inputs to many of the peripherals and the Cortex-M Processor.
     *
     * @{
     */

    /**
     * \brief Set the prescaler of the MCK.
     *
     * \param ul_pres Prescaler value.
     */
    void pmc_mck_set_prescaler(uint32_t ul_pres)
{
    PMC->PMC_MCKR =
        (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}

#if SAMV71 || SAMV70 || SAME70 || SAMS70
/**
 * \brief Set the division of the MCK.
 *
 * \param ul_div Division value.
 */
void pmc_mck_set_division(uint32_t ul_div)
{
    switch (ul_div) {
        case 1:
            ul_div = PMC_MCKR_MDIV_EQ_PCK;
            break;
        case 2:
            ul_div = PMC_MCKR_MDIV_PCK_DIV2;
            break;
        case 3:
            ul_div = PMC_MCKR_MDIV_PCK_DIV3;
            break;
        case 4:
            ul_div = PMC_MCKR_MDIV_PCK_DIV4;
            break;
        default:
            ul_div = PMC_MCKR_MDIV_EQ_PCK;
            break;
    }
    PMC->PMC_MCKR =
        (PMC->PMC_MCKR & (~PMC_MCKR_MDIV_Msk)) | ul_div;
    while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}
#endif

/**
 * \brief Set the source of the MCK.
 *
 * \param ul_source Source selection value.
 */
void pmc_mck_set_source(uint32_t ul_source)
{
    PMC->PMC_MCKR =
        (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) | ul_source;
    while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
}

/**
 * \brief Switch master clock source selection to slow clock.
 *
 * \param ul_pres Processor clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_mck_to_sclk(uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) |
                    PMC_MCKR_CSS_SLOW_CLK;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * \brief Switch master clock source selection to main clock.
 *
 * \param ul_pres Processor clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_mck_to_mainck(uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) |
                    PMC_MCKR_CSS_MAIN_CLK;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * \brief Switch master clock source selection to PLLA clock.
 *
 * \param ul_pres Processor clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_mck_to_pllack(uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) |
                    PMC_MCKR_CSS_PLLA_CLK;

    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP || SAMG55)
/**
 * \brief Switch master clock source selection to PLLB clock.
 *
 * \param ul_pres Processor clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_mck_to_pllbck(uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) |
                    PMC_MCKR_CSS_PLLB_CLK;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}
#endif

#if (SAM3XA || SAM3U || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Switch master clock source selection to UPLL clock.
 *
 * \param ul_pres Processor clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_mck_to_upllck(uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | ul_pres;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) |
                    PMC_MCKR_CSS_UPLL_CLK;
    for (ul_timeout = PMC_TIMEOUT; !(PMC->PMC_SR & PMC_SR_MCKRDY);
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}
#endif

/**
 * \brief Switch slow clock source selection to external 32k (Xtal or Bypass).
 *
 * \note Switching SCLK back to 32krc is only possible by shutting down the
 *       VDDIO power supply.
 *
 * \param ul_bypass 0 for Xtal, 1 for bypass.
 */
void pmc_switch_sclk_to_32kxtal(uint32_t ul_bypass)
{
    /* Set Bypass mode if required */
    if (ul_bypass == 1) {
        SUPC->SUPC_MR |= SUPC_MR_KEY_PASSWD |
                         SUPC_MR_OSCBYPASS;
    }

    SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_XTALSEL;
}

/**
 * \brief Check if the external 32k Xtal is ready.
 *
 * \retval 1 External 32k Xtal is ready.
 * \retval 0 External 32k Xtal is not ready.
 */
uint32_t pmc_osc_is_ready_32kxtal(void)
{
    return ((SUPC->SUPC_SR & SUPC_SR_OSCSEL)
            && (PMC->PMC_SR & PMC_SR_OSCSELS));
}

/**
 * \brief Switch main clock source selection to internal fast RC.
 *
 * \param ul_moscrcf Fast RC oscillator(4/8/12Mhz).
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 * \retval 2 Invalid frequency.
 */
void pmc_switch_mainck_to_fastrc(uint32_t ul_moscrcf)
{
    /* Enable Fast RC oscillator but DO NOT switch to RC now */
    PMC->CKGR_MOR |= (CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN);

    /* Wait the Fast RC to stabilize */
    while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));

    /* Change Fast RC oscillator frequency */
    PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk) |
                    CKGR_MOR_KEY_PASSWD | ul_moscrcf;

    /* Wait the Fast RC to stabilize */
    while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));

    /* Switch to Fast RC */
    PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) |
                    CKGR_MOR_KEY_PASSWD;
}

/**
 * \brief Enable fast RC oscillator.
 *
 * \param ul_rc Fast RC oscillator(4/8/12Mhz).
 */
void pmc_osc_enable_fastrc(uint32_t ul_rc)
{
    /* Enable Fast RC oscillator but DO NOT switch to RC */
    PMC->CKGR_MOR |= (CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN);
    /* Wait the Fast RC to stabilize */
    while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));

    /* Change Fast RC oscillator frequency */
    PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk) |
                    CKGR_MOR_KEY_PASSWD | ul_rc;
    /* Wait the Fast RC to stabilize */
    while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));
}

/**
 * \brief Disable the internal fast RC.
 */
void pmc_osc_disable_fastrc(void)
{
    /* Disable Fast RC oscillator */
    PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &
                     ~CKGR_MOR_MOSCRCF_Msk)
                    | CKGR_MOR_KEY_PASSWD;
}

/**
 * \brief Check if the main fastrc is ready.
 *
 * \retval 0 Xtal is not ready, otherwise ready.
 */
uint32_t pmc_osc_is_ready_fastrc(void)
{
    return (PMC->PMC_SR & PMC_SR_MOSCRCS);
}

/**
 * \brief Enable main XTAL oscillator.
 *
 * \param ul_xtal_startup_time Xtal start-up time, in number of slow clocks.
 */
void pmc_osc_enable_main_xtal(uint32_t ul_xtal_startup_time)
{
    uint32_t mor = PMC->CKGR_MOR;
    mor &= ~(CKGR_MOR_MOSCXTBY|CKGR_MOR_MOSCXTEN);
    mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN |
           CKGR_MOR_MOSCXTST(ul_xtal_startup_time);
    PMC->CKGR_MOR = mor;
    /* Wait the main Xtal to stabilize */
    while (!(PMC->PMC_SR & PMC_SR_MOSCXTS));
}

/**
 * \brief Bypass main XTAL.
 */
void pmc_osc_bypass_main_xtal(void)
{
    uint32_t mor = PMC->CKGR_MOR;
    mor &= ~(CKGR_MOR_MOSCXTBY|CKGR_MOR_MOSCXTEN);
    mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY;
    /* Enable Crystal oscillator but DO NOT switch now. Keep MOSCSEL to 0 */
    PMC->CKGR_MOR = mor;
    /* The MOSCXTS in PMC_SR is automatically set */
}

/**
 * \brief Disable the main Xtal.
 */
void pmc_osc_disable_main_xtal(void)
{
    uint32_t mor = PMC->CKGR_MOR;
    mor &= ~(CKGR_MOR_MOSCXTBY|CKGR_MOR_MOSCXTEN);
    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
}

/**
 * \brief Check if the main crystal is bypassed.
 *
 * \retval 0 Xtal is bypassed, otherwise not.
 */
uint32_t pmc_osc_is_bypassed_main_xtal(void)
{
    return (PMC->CKGR_MOR & CKGR_MOR_MOSCXTBY);
}

/**
 * \brief Check if the main crystal is ready.
 *
 * \note If main crystal is bypassed, it's always ready.
 *
 * \retval 0 main crystal is not ready, otherwise ready.
 */
uint32_t pmc_osc_is_ready_main_xtal(void)
{
    return (PMC->PMC_SR & PMC_SR_MOSCXTS);
}

/**
 * \brief Switch main clock source selection to external Xtal/Bypass.
 *
 * \note The function may switch MCK to SCLK if MCK source is MAINCK to avoid
 *       any system crash.
 *
 * \note If used in Xtal mode, the Xtal is automatically enabled.
 *
 * \param ul_bypass 0 for Xtal, 1 for bypass.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
void pmc_switch_mainck_to_xtal(uint32_t ul_bypass,
                               uint32_t ul_xtal_startup_time)
{
    /* Enable Main Xtal oscillator */
    if (ul_bypass) {
        PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
                        CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY |
                        CKGR_MOR_MOSCSEL;
    } else {
        PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) |
                        CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN |
                        CKGR_MOR_MOSCXTST(ul_xtal_startup_time);
        /* Wait the Xtal to stabilize */
        while (!(PMC->PMC_SR & PMC_SR_MOSCXTS));

        PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
    }
}

/**
 * \brief Disable the external Xtal.
 *
 * \param ul_bypass 0 for Xtal, 1 for bypass.
 */
void pmc_osc_disable_xtal(uint32_t ul_bypass)
{
    /* Disable xtal oscillator */
    if (ul_bypass) {
        PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) |
                        CKGR_MOR_KEY_PASSWD;
    } else {
        PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
                        CKGR_MOR_KEY_PASSWD;
    }
}

/**
 * \brief Check if the MAINCK is ready. Depending on MOSCEL, MAINCK can be one
 * of Xtal, bypass or internal RC.
 *
 * \retval 1 Xtal is ready.
 * \retval 0 Xtal is not ready.
 */
uint32_t pmc_osc_is_ready_mainck(void)
{
    return PMC->PMC_SR & PMC_SR_MOSCSELS;
}

/**
 * \brief Select Main Crystal or internal RC as main clock source.
 *
 * \note This function will not enable/disable RC or Main Crystal.
 *
 * \param ul_xtal_rc 0 internal RC is selected, otherwise Main Crystal.
 */
void pmc_mainck_osc_select(uint32_t ul_xtal_rc)
{
    uint32_t mor = PMC->CKGR_MOR;
    if (ul_xtal_rc) {
        mor |=  CKGR_MOR_MOSCSEL;
    } else {
        mor &= ~CKGR_MOR_MOSCSEL;
    }
    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
}

/**
 * \brief Enable PLLA clock.
 *
 * \param mula PLLA multiplier.
 * \param pllacount PLLA counter.
 * \param diva Divider.
 */
void pmc_enable_pllack(uint32_t mula, uint32_t pllacount, uint32_t diva)
{
    /* first disable the PLL to unlock the lock */
    pmc_disable_pllack();

#if (SAM4C || SAM4CM || SAM4CP || SAMG)
    PMC->CKGR_PLLAR = CKGR_PLLAR_PLLAEN(diva) |
                      CKGR_PLLAR_PLLACOUNT(pllacount) | CKGR_PLLAR_MULA(mula);
#else
    PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_DIVA(diva) |
                      CKGR_PLLAR_PLLACOUNT(pllacount) | CKGR_PLLAR_MULA(mula);
#endif
    while ((PMC->PMC_SR & PMC_SR_LOCKA) == 0);
}

/**
 * \brief Disable PLLA clock.
 */
void pmc_disable_pllack(void)
{
#if (SAM4C || SAM4CM || SAM4CP || SAMG)
    PMC->CKGR_PLLAR = CKGR_PLLAR_MULA(0);
#else
    PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(0);
#endif
}

/**
 * \brief Is PLLA locked?
 *
 * \retval 0 Not locked.
 * \retval 1 Locked.
 */
uint32_t pmc_is_locked_pllack(void)
{
    return (PMC->PMC_SR & PMC_SR_LOCKA);
}

#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP || SAMG55)
/**
 * \brief Enable PLLB clock.
 *
 * \param mulb PLLB multiplier.
 * \param pllbcount PLLB counter.
 * \param divb Divider.
 */
void pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb)
{
    /* first disable the PLL to unlock the lock */
    pmc_disable_pllbck();

#if SAMG55
    PMC->CKGR_PLLAR = CKGR_PLLAR_PLLAEN(divb) |
                      CKGR_PLLAR_PLLACOUNT(pllbcount) | CKGR_PLLAR_MULA(mulb);
#else
    PMC->CKGR_PLLBR =
        CKGR_PLLBR_DIVB(divb) | CKGR_PLLBR_PLLBCOUNT(pllbcount)
        | CKGR_PLLBR_MULB(mulb);
#endif
    while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0);
}

/**
 * \brief Disable PLLB clock.
 */
void pmc_disable_pllbck(void)
{
    PMC->CKGR_PLLBR = CKGR_PLLBR_MULB(0);
}

/**
 * \brief Is PLLB locked?
 *
 * \retval 0 Not locked.
 * \retval 1 Locked.
 */
uint32_t pmc_is_locked_pllbck(void)
{
    return (PMC->PMC_SR & PMC_SR_LOCKB);
}
#endif

#if (SAM3XA || SAM3U || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Enable UPLL clock.
 */
void pmc_enable_upll_clock(void)
{
    PMC->CKGR_UCKR = CKGR_UCKR_UPLLCOUNT(3) | CKGR_UCKR_UPLLEN;

    /* Wait UTMI PLL Lock Status */
    while (!(PMC->PMC_SR & PMC_SR_LOCKU));
}

/**
 * \brief Disable UPLL clock.
 */
void pmc_disable_upll_clock(void)
{
    PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
}

/**
 * \brief Is UPLL locked?
 *
 * \retval 0 Not locked.
 * \retval 1 Locked.
 */
uint32_t pmc_is_locked_upll(void)
{
    return (PMC->PMC_SR & PMC_SR_LOCKU);
}
#endif

/**
 * \brief Enable the specified peripheral clock.
 *
 * \note The ID must NOT be shifted (i.e., 1 << ID_xxx).
 *
 * \param ul_id Peripheral ID (ID_xxx).
 *
 * \retval 0 Success.
 * \retval 1 Invalid parameter.
 */
uint32_t pmc_enable_periph_clk(uint32_t ul_id)
{
    if (ul_id > MAX_PERIPH_ID) {
        return 1;
    }

    if (ul_id < 32) {
        if ((PMC->PMC_PCSR0 & (1u << ul_id)) != (1u << ul_id)) {
            PMC->PMC_PCER0 = 1 << ul_id;
        }
#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
    } else {
        ul_id -= 32;
        if ((PMC->PMC_PCSR1 & (1u << ul_id)) != (1u << ul_id)) {
            PMC->PMC_PCER1 = 1 << ul_id;
        }
#endif
    }

    return 0;
}

/**
 * \brief Disable the specified peripheral clock.
 *
 * \note The ID must NOT be shifted (i.e., 1 << ID_xxx).
 *
 * \param ul_id Peripheral ID (ID_xxx).
 *
 * \retval 0 Success.
 * \retval 1 Invalid parameter.
 */
uint32_t pmc_disable_periph_clk(uint32_t ul_id)
{
    if (ul_id > MAX_PERIPH_ID) {
        return 1;
    }

    if (ul_id < 32) {
        if ((PMC->PMC_PCSR0 & (1u << ul_id)) == (1u << ul_id)) {
            PMC->PMC_PCDR0 = 1 << ul_id;
        }
#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 \
		|| SAMV70 || SAME70 || SAMS70)
    } else {
        ul_id -= 32;
        if ((PMC->PMC_PCSR1 & (1u << ul_id)) == (1u << ul_id)) {
            PMC->PMC_PCDR1 = 1 << ul_id;
        }
#endif
    }
    return 0;
}

/**
 * \brief Enable all peripheral clocks.
 */
void pmc_enable_all_periph_clk(void)
{
    PMC->PMC_PCER0 = PMC_MASK_STATUS0;
    while ((PMC->PMC_PCSR0 & PMC_MASK_STATUS0) != PMC_MASK_STATUS0);

#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMV71  \
		|| SAMV70 || SAME70 || SAMS70)
    PMC->PMC_PCER1 = PMC_MASK_STATUS1;
    while ((PMC->PMC_PCSR1 & PMC_MASK_STATUS1) != PMC_MASK_STATUS1);
#endif
}

/**
 * \brief Disable all peripheral clocks.
 */
void pmc_disable_all_periph_clk(void)
{
    PMC->PMC_PCDR0 = PMC_MASK_STATUS0;
    while ((PMC->PMC_PCSR0 & PMC_MASK_STATUS0) != 0);

#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMV71 \
		|| SAMV70 || SAME70 || SAMS70)
    PMC->PMC_PCDR1 = PMC_MASK_STATUS1;
    while ((PMC->PMC_PCSR1 & PMC_MASK_STATUS1) != 0);
#endif
}

/**
 * \brief Check if the specified peripheral clock is enabled.
 *
 * \note The ID must NOT be shifted (i.e., 1 << ID_xxx).
 *
 * \param ul_id Peripheral ID (ID_xxx).
 *
 * \retval 0 Peripheral clock is disabled or unknown.
 * \retval 1 Peripheral clock is enabled.
 */
uint32_t pmc_is_periph_clk_enabled(uint32_t ul_id)
{
    if (ul_id > MAX_PERIPH_ID) {
        return 0;
    }

#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMV71 \
		|| SAMV70 || SAME70 || SAMS70)
    if (ul_id < 32) {
#endif
        if ((PMC->PMC_PCSR0 & (1u << ul_id))) {
            return 1;
        } else {
            return 0;
        }
#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAM4C || SAM4CM || SAM4CP || SAMV71 \
		|| SAMV70 || SAME70 || SAMS70)
    } else {
        ul_id -= 32;
        if ((PMC->PMC_PCSR1 & (1u << ul_id))) {
            return 1;
        } else {
            return 0;
        }
    }
#endif
}

/**
 * \brief Set the prescaler for the specified programmable clock.
 *
 * \param ul_id Peripheral ID.
 * \param ul_pres Prescaler value.
 */
void pmc_pck_set_prescaler(uint32_t ul_id, uint32_t ul_pres)
{
    PMC->PMC_PCK[ul_id] =
        (PMC->PMC_PCK[ul_id] & ~PMC_PCK_PRES_Msk) | ul_pres;
    while ((PMC->PMC_SCER & (PMC_SCER_PCK0 << ul_id))
            && !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)));
}

/**
 * \brief Set the source oscillator for the specified programmable clock.
 *
 * \param ul_id Peripheral ID.
 * \param ul_source Source selection value.
 */
void pmc_pck_set_source(uint32_t ul_id, uint32_t ul_source)
{
    PMC->PMC_PCK[ul_id] =
        (PMC->PMC_PCK[ul_id] & ~PMC_PCK_CSS_Msk) | ul_source;
    while ((PMC->PMC_SCER & (PMC_SCER_PCK0 << ul_id))
            && !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)));
}

/**
 * \brief Switch programmable clock source selection to slow clock.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_sclk(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_SLOW_CLK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)); --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * \brief Switch programmable clock source selection to main clock.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_mainck(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_MAIN_CLK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)); --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * \brief Switch programmable clock source selection to PLLA clock.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_pllack(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_PLLA_CLK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)); --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP || SAMG55)
/**
 * \brief Switch programmable clock source selection to PLLB clock.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_pllbck(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_PLLB_CLK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id));
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}
#endif

#if (SAM3XA || SAM3U || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Switch programmable clock source selection to UPLL clock.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_upllck(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_UPLL_CLK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id));
            --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}
#endif

/**
 * \brief Switch programmable clock source selection to mck.
 *
 * \param ul_id Id of the programmable clock.
 * \param ul_pres Programmable clock prescaler.
 *
 * \retval 0 Success.
 * \retval 1 Timeout error.
 */
uint32_t pmc_switch_pck_to_mck(uint32_t ul_id, uint32_t ul_pres)
{
    uint32_t ul_timeout;

    PMC->PMC_PCK[ul_id] = PMC_PCK_CSS_MCK | ul_pres;
    for (ul_timeout = PMC_TIMEOUT;
            !(PMC->PMC_SR & (PMC_SR_PCKRDY0 << ul_id)); --ul_timeout) {
        if (ul_timeout == 0) {
            return 1;
        }
    }

    return 0;
}

/**
 * \brief Enable the specified programmable clock.
 *
 * \param ul_id Id of the programmable clock.
 */
void pmc_enable_pck(uint32_t ul_id)
{
    PMC->PMC_SCER = PMC_SCER_PCK0 << ul_id;
}

/**
 * \brief Disable the specified programmable clock.
 *
 * \param ul_id Id of the programmable clock.
 */
void pmc_disable_pck(uint32_t ul_id)
{
    PMC->PMC_SCDR = PMC_SCER_PCK0 << ul_id;
}

/**
 * \brief Enable all programmable clocks.
 */
void pmc_enable_all_pck(void)
{
    PMC->PMC_SCER = PMC_SCER_PCK0 | PMC_SCER_PCK1 | PMC_SCER_PCK2;
}

/**
 * \brief Disable all programmable clocks.
 */
void pmc_disable_all_pck(void)
{
    PMC->PMC_SCDR = PMC_SCDR_PCK0 | PMC_SCDR_PCK1 | PMC_SCDR_PCK2;
}

/**
 * \brief Check if the specified programmable clock is enabled.
 *
 * \param ul_id Id of the programmable clock.
 *
 * \retval 0 Programmable clock is disabled or unknown.
 * \retval 1 Programmable clock is enabled.
 */
uint32_t pmc_is_pck_enabled(uint32_t ul_id)
{
    if (ul_id > 2) {
        return 0;
    }

    return (PMC->PMC_SCSR & (PMC_SCSR_PCK0 << ul_id));
}

#if (SAM4C || SAM4CM || SAM4CP)
/**
 * \brief Enable Coprocessor Clocks.
 */
void pmc_enable_cpck(void)
{
    PMC->PMC_SCER = PMC_SCER_CPCK | PMC_SCER_CPKEY_PASSWD;
}

/**
 * \brief Disable Coprocessor Clocks.
 */
void pmc_disable_cpck(void)
{
    PMC->PMC_SCDR = PMC_SCDR_CPCK | PMC_SCDR_CPKEY_PASSWD;
}

/**
 * \brief Check if the Coprocessor Clocks is enabled.
 *
 * \retval 0 Coprocessor Clocks is disabled.
 * \retval 1 Coprocessor Clocks is enabled.
 */
bool pmc_is_cpck_enabled(void)
{
    if(PMC->PMC_SCSR & PMC_SCSR_CPCK) {
        return 1;
    } else {
        return 0;
    }
}

/**
 * \brief Enable Coprocessor Bus Master Clocks.
 */
void pmc_enable_cpbmck(void)
{
    PMC->PMC_SCER = PMC_SCER_CPCK | PMC_SCER_CPKEY_PASSWD;
}

/**
 * \brief Disable Coprocessor Bus Master Clocks.
 */
void pmc_disable_cpbmck(void)
{
    PMC->PMC_SCDR = PMC_SCDR_CPCK | PMC_SCDR_CPKEY_PASSWD;
}

/**
 * \brief Check if the Coprocessor Bus Master Clocks is enabled.
 *
 * \retval 0 Coprocessor Bus Master Clocks is disabled.
 * \retval 1 Coprocessor Bus Master Clocks is enabled.
 */
bool pmc_is_cpbmck_enabled(void)
{
    if(PMC->PMC_SCSR & PMC_SCSR_CPBMCK) {
        return 1;
    } else {
        return 0;
    }
}

/**
 * \brief Set the prescaler for the Coprocessor Master Clock.
 *
 * \param ul_pres Prescaler value.
 */
void pmc_cpck_set_prescaler(uint32_t ul_pres)
{
    PMC->PMC_MCKR =
        (PMC->PMC_MCKR & (~PMC_MCKR_CPPRES_Msk)) | PMC_MCKR_CPPRES(ul_pres);
}

/**
 * \brief Set the source for the Coprocessor Master Clock.
 *
 * \param ul_source Source selection value.
 */
void pmc_cpck_set_source(uint32_t ul_source)
{
    PMC->PMC_MCKR =
        (PMC->PMC_MCKR & (~PMC_MCKR_CPCSS_Msk)) | ul_source;
}
#endif

#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Switch UDP (USB) clock source selection to PLLA clock.
 *
 * \param ul_usbdiv Clock divisor.
 */
void pmc_switch_udpck_to_pllack(uint32_t ul_usbdiv)
{
    PMC->PMC_USB = PMC_USB_USBDIV(ul_usbdiv);
}
#endif

#if (SAM3S || SAM4S || SAMG55)
/**
 * \brief Switch UDP (USB) clock source selection to PLLB clock.
 *
 * \param ul_usbdiv Clock divisor.
 */
void pmc_switch_udpck_to_pllbck(uint32_t ul_usbdiv)
{
    PMC->PMC_USB = PMC_USB_USBDIV(ul_usbdiv) | PMC_USB_USBS;
}
#endif

#if (SAM3XA || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Switch UDP (USB) clock source selection to UPLL clock.
 *
 * \param ul_usbdiv Clock divisor.
 */
void pmc_switch_udpck_to_upllck(uint32_t ul_usbdiv)
{
    PMC->PMC_USB = PMC_USB_USBS | PMC_USB_USBDIV(ul_usbdiv);
}
#endif

#if (SAM3S || SAM3XA || SAM4S || SAM4E || SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Enable UDP (USB) clock.
 */
void pmc_enable_udpck(void)
{
#if (SAM3S || SAM4S || SAM4E || SAMG55)
    PMC->PMC_SCER = PMC_SCER_UDP;
#elif (SAMV71 || SAMV70 || SAME70 || SAMS70)
    PMC->PMC_SCER = PMC_SCER_USBCLK;
#else
    PMC->PMC_SCER = PMC_SCER_UOTGCLK;
# endif
}

/**
 * \brief Disable UDP (USB) clock.
 */
void pmc_disable_udpck(void)
{
#if (SAM3S || SAM4S || SAM4E || SAMG55)
    PMC->PMC_SCDR = PMC_SCDR_UDP;
#elif (SAMV71 || SAMV70 || SAME70 || SAMS70)
    PMC->PMC_SCDR = PMC_SCDR_USBCLK;
#else
    PMC->PMC_SCDR = PMC_SCDR_UOTGCLK;
# endif
}
#endif

#if SAMG55
/**
 * \brief Switch UHP (USB) clock source selection to PLLA clock.
 *
 * \param ul_usbdiv Clock divisor.
 */
void pmc_switch_uhpck_to_pllack(uint32_t ul_usbdiv)
{
    PMC->PMC_USB = PMC_USB_USBDIV(ul_usbdiv);
}

/**
 * \brief Switch UHP (USB) clock source selection to PLLB clock.
 *
 * \param ul_usbdiv Clock divisor.
 */
void pmc_switch_uhpck_to_pllbck(uint32_t ul_usbdiv)
{
    PMC->PMC_USB = PMC_USB_USBDIV(ul_usbdiv) | PMC_USB_USBS;
}

/**
 * \brief Enable UHP (USB) clock.
 */
void pmc_enable_uhpck(void)
{
    PMC->PMC_SCER = PMC_SCER_UHP;
}
#endif

/**
 * \brief Enable PMC interrupts.
 *
 * \param ul_sources Interrupt sources bit map.
 */
void pmc_enable_interrupt(uint32_t ul_sources)
{
    PMC->PMC_IER = ul_sources;
}

/**
 * \brief Disable PMC interrupts.
 *
 * \param ul_sources Interrupt sources bit map.
 */
void pmc_disable_interrupt(uint32_t ul_sources)
{
    PMC->PMC_IDR = ul_sources;
}

/**
 * \brief Get PMC interrupt mask.
 *
 * \return The interrupt mask value.
 */
uint32_t pmc_get_interrupt_mask(void)
{
    return PMC->PMC_IMR;
}

/**
 * \brief Get current status.
 *
 * \return The current PMC status.
 */
uint32_t pmc_get_status(void)
{
    return PMC->PMC_SR;
}

/**
 * \brief Set the wake-up inputs for fast startup mode registers
 *        (event generation).
 *
 * \param ul_inputs Wake up inputs to enable.
 */
void pmc_set_fast_startup_input(uint32_t ul_inputs)
{
    ul_inputs &= PMC_FAST_STARTUP_Msk;
    PMC->PMC_FSMR |= ul_inputs;
}

/**
 * \brief Clear the wake-up inputs for fast startup mode registers
 *        (remove event generation).
 *
 * \param ul_inputs Wake up inputs to disable.
 */
void pmc_clr_fast_startup_input(uint32_t ul_inputs)
{
    ul_inputs &= PMC_FAST_STARTUP_Msk;
    PMC->PMC_FSMR &= ~ul_inputs;
}

#if (SAM4C || SAM4CM || SAM4CP)
/**
 * \brief Set the wake-up inputs of coprocessor for fast startup mode registers
 *        (event generation).
 *
 * \param ul_inputs Wake up inputs to enable.
 */
void pmc_cp_set_fast_startup_input(uint32_t ul_inputs)
{
    ul_inputs &= PMC_FAST_STARTUP_Msk;
    PMC->PMC_CPFSMR |= ul_inputs;
}

/**
 * \brief Clear the wake-up inputs of coprocessor for fast startup mode registers
 *        (remove event generation).
 *
 * \param ul_inputs Wake up inputs to disable.
 */
void pmc_cp_clr_fast_startup_input(uint32_t ul_inputs)
{
    ul_inputs &= PMC_FAST_STARTUP_Msk;
    PMC->PMC_CPFSMR &= ~ul_inputs;
}
#endif

#if (!(SAMG51 || SAMG53 || SAMG54))
/**
 * \brief Enable Sleep Mode.
 * Enter condition: (WFE or WFI) + (SLEEPDEEP bit = 0) + (LPM bit = 0)
 *
 * \param uc_type 0 for wait for interrupt, 1 for wait for event.
 * \note For SAM4S, SAM4C, SAM4CM, SAM4CP, SAMV71 and SAM4E series,
 * since only WFI is effective, uc_type = 1 will be treated as uc_type = 0.
 */
void pmc_enable_sleepmode(uint8_t uc_type)
{
#if !(SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMV71 || SAMV70 || SAME70 || SAMS70)
    PMC->PMC_FSMR &= (uint32_t) ~ PMC_FSMR_LPM; // Enter Sleep mode
#endif
    SCB->SCR &= (uint32_t) ~ SCB_SCR_SLEEPDEEP_Msk; // Deep sleep

#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMV71 || SAMV70 || SAME70 || SAMS70)
    UNUSED(uc_type);
    __WFI();
#else
    if (uc_type == 0) {
        __WFI();
    } else {
        __WFE();
    }
#endif
}
#endif

#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAMG || SAM4CP || SAMV71 || SAMV70 || SAME70 || SAMS70)
static uint32_t ul_flash_in_wait_mode = PMC_WAIT_MODE_FLASH_DEEP_POWERDOWN;
                                        /**
                                         * \brief Set the embedded flash state in wait mode
                                         *
                                         * \param ul_flash_state PMC_WAIT_MODE_FLASH_STANDBY flash in standby mode,
                                         * PMC_WAIT_MODE_FLASH_DEEP_POWERDOWN flash in deep power down mode.
                                         */
                                        void pmc_set_flash_in_wait_mode(uint32_t ul_flash_state)
{
    ul_flash_in_wait_mode = ul_flash_state;
}

/**
 * \brief Enable Wait Mode. Enter condition: (WAITMODE bit = 1) + FLPM
 *
 * \note In this function, FLPM will retain, WAITMODE bit will be set,
 * Generally, this function will be called by pmc_sleep() in order to
 * complete all sequence entering wait mode.
 * See \ref pmc_sleep() for entering different sleep modes.
 */
void pmc_enable_waitmode(void)
{
    uint32_t i;

    /* Flash in wait mode */
    i = PMC->PMC_FSMR;
    i &= ~PMC_FSMR_FLPM_Msk;
    i |= ul_flash_in_wait_mode;
    PMC->PMC_FSMR = i;

    /* Set the WAITMODE bit = 1 */
    PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_WAITMODE;

    /* Waiting for Master Clock Ready MCKRDY = 1 */
    while (!(PMC->PMC_SR & PMC_SR_MCKRDY));

    /* Waiting for MOSCRCEN bit cleared is strongly recommended
     * to ensure that the core will not execute undesired instructions
     */
    for (i = 0; i < 500; i++) {
        __NOP();
    }
    while (!(PMC->CKGR_MOR & CKGR_MOR_MOSCRCEN));

#if (!SAMG)
    /* Restore Flash in idle mode */
    i = PMC->PMC_FSMR;
    i &= ~PMC_FSMR_FLPM_Msk;
    i |= PMC_WAIT_MODE_FLASH_IDLE;
    PMC->PMC_FSMR = i;
#endif
}
#else
/**
 * \brief Enable Wait Mode. Enter condition: WFE + (SLEEPDEEP bit = 0) +
 * (LPM bit = 1)
 */
void pmc_enable_waitmode(void)
{
    uint32_t i;

    PMC->PMC_FSMR |= PMC_FSMR_LPM; /* Enter Wait mode */
    SCB->SCR &= (uint32_t) ~ SCB_SCR_SLEEPDEEP_Msk; /* Deep sleep */

    __WFE();

    /* Waiting for MOSCRCEN bit cleared is strongly recommended
     * to ensure that the core will not execute undesired instructions
     */
    for (i = 0; i < 500; i++) {
        __NOP();
    }
    while (!(PMC->CKGR_MOR & CKGR_MOR_MOSCRCEN));

}
#endif

#if (!(SAMG51 || SAMG53 || SAMG54))
/**
 * \brief Enable Backup Mode. Enter condition: WFE/(VROFF bit = 1) +
 * (SLEEPDEEP bit = 1)
 */
void pmc_enable_backupmode(void)
{
#if (SAM4C || SAM4CM || SAM4CP)
    uint32_t tmp = SUPC->SUPC_MR & ~(SUPC_MR_BUPPOREN | SUPC_MR_KEY_Msk);
    SUPC->SUPC_MR = tmp | SUPC_MR_KEY_PASSWD;
    while (SUPC->SUPC_SR & SUPC_SR_BUPPORS);
#endif
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
    SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_VROFF_STOP_VREG;
    __WFE();
    __WFI();
#else
    __WFE();
#endif
}
#endif

/**
 * \brief Enable Clock Failure Detector.
 */
void pmc_enable_clock_failure_detector(void)
{
    uint32_t ul_reg = PMC->CKGR_MOR;

    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | CKGR_MOR_CFDEN | ul_reg;
}

/**
 * \brief Disable Clock Failure Detector.
 */
void pmc_disable_clock_failure_detector(void)
{
    uint32_t ul_reg = PMC->CKGR_MOR & (~CKGR_MOR_CFDEN);

    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | ul_reg;
}

#if (SAM4N || SAM4C || SAM4CM || SAM4CP || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Enable Slow Crystal Oscillator Frequency Monitoring.
 */
void pmc_enable_sclk_osc_freq_monitor(void)
{
    uint32_t ul_reg = PMC->CKGR_MOR;

    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | CKGR_MOR_XT32KFME | ul_reg;
}

/**
 * \brief Disable Slow Crystal Oscillator Frequency Monitoring.
 */
void pmc_disable_sclk_osc_freq_monitor(void)
{
    uint32_t ul_reg = PMC->CKGR_MOR & (~CKGR_MOR_XT32KFME);

    PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | ul_reg;
}
#endif

/**
 * \brief Enable or disable write protect of PMC registers.
 *
 * \param ul_enable 1 to enable, 0 to disable.
 */
void pmc_set_writeprotect(uint32_t ul_enable)
{
    if (ul_enable) {
        PMC->PMC_WPMR = PMC_WPMR_WPKEY_PASSWD | PMC_WPMR_WPEN;
    } else {
        PMC->PMC_WPMR = PMC_WPMR_WPKEY_PASSWD;
    }
}

/**
 * \brief Return write protect status.
 *
 * \return Return write protect status.
 */
uint32_t pmc_get_writeprotect_status(void)
{
    return PMC->PMC_WPSR;
}

#if (SAMG53 || SAMG54 || SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Enable the specified peripheral clock.
 *
 * \note The ID must NOT be shifted (i.e., 1 << ID_xxx).
 *
 * \param ul_id Peripheral ID (ID_xxx).
 *
 * \retval 0 Success.
 * \retval 1 Fail.
 */
uint32_t pmc_enable_sleepwalking(uint32_t ul_id)
{
    uint32_t temp;
#if (SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
    if ((7 <= ul_id) && (ul_id<= 29)) {
#else
    if ((8 <= ul_id) && (ul_id<= 29)) {
#endif
        temp = pmc_get_active_status0();
        if (temp & (1 << ul_id)) {
            return 1;
        }
        PMC->PMC_SLPWK_ER0 = 1 << ul_id;
        temp = pmc_get_active_status0();
        if (temp & (1 << ul_id)) {
            pmc_disable_sleepwalking(ul_id);
            return 1;
        }
        return 0;
    }
#if (SAMV71 || SAMV70 || SAME70 || SAMS70)
    else if ((32 <= ul_id) && (ul_id<= 60)) {
        ul_id -= 32;
        temp = pmc_get_active_status1();
        if (temp & (1 << ul_id)) {
            return 1;
        }
        PMC->PMC_SLPWK_ER1 = 1 << ul_id;
        temp = pmc_get_active_status1();
        if (temp & (1 << ul_id)) {
            pmc_disable_sleepwalking(ul_id);
            return 1;
        }
        return 0;
    }
#endif
    else {
        return 1;
    }
}

/**
 * \brief Disable the sleepwalking of specified peripheral.
 *
 * \note The ID must NOT be shifted (i.e., 1 << ID_xxx).
 *
 * \param ul_id Peripheral ID (ID_xxx).
 *
 * \retval 0 Success.
 * \retval 1 Invalid parameter.
 */
uint32_t pmc_disable_sleepwalking(uint32_t ul_id)
{
#if (SAMG55 || SAMV71 || SAMV70 || SAME70 || SAMS70)
    if ((7 <= ul_id) && (ul_id<= 29)) {
#else
    if ((8 <= ul_id) && (ul_id<= 29)) {
#endif
        PMC->PMC_SLPWK_DR0 = 1 << ul_id;
        return 0;
    }
#if (SAMV71 || SAMV70 || SAME70 || SAMS70)
    else if ((32 <= ul_id) && (ul_id<= 60)) {
        ul_id -= 32;
        PMC->PMC_SLPWK_DR1 = 1 << ul_id;
        return 0;
    }
#endif
    else {
        return 1;
    }
}

/**
 * \brief Return peripheral sleepwalking enable status.
 *
 * \return the status register value.
 */
uint32_t pmc_get_sleepwalking_status0(void)
{
    return PMC->PMC_SLPWK_SR0;
}

/**
 * \brief Return peripheral active status.
 *
 * \return the status register value.
 */
uint32_t pmc_get_active_status0(void)
{
    return PMC->PMC_SLPWK_ASR0;
}

#endif

#if (SAMV71 || SAMV70 || SAME70 || SAMS70)
/**
 * \brief Return peripheral sleepwalking enable status.
 *
 * \return the status register value.
 */
uint32_t pmc_get_sleepwalking_status1(void)
{
    return PMC->PMC_SLPWK_SR1;
}

/**
 * \brief Return peripheral active status.
 *
 * \return the status register value.
 */
uint32_t pmc_get_active_status1(void)
{
    return PMC->PMC_SLPWK_ASR1;
}
#endif

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond
