t
Fork of mbed-dev by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/pmc/pmc.c
- Revision:
- 107:414e9c822e99
diff -r c405d2eb506d -r 414e9c822e99 targets/hal/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/pmc/pmc.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/hal/TARGET_Atmel/TARGET_SAM_CortexM4/drivers/pmc/pmc.c Tue Apr 05 18:15:12 2016 +0100 @@ -0,0 +1,1647 @@ +/** + * \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