--- /dev/null
+/**\r
+ * \file\r
+ *\r
+ * \brief Sleep mode access\r
+ *\r
+ * Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.\r
+ *\r
+ * \asf_license_start\r
+ *\r
+ * \page License\r
+ *\r
+ * Redistribution and use in source and binary forms, with or without\r
+ * modification, are permitted provided that the following conditions are met:\r
+ *\r
+ * 1. Redistributions of source code must retain the above copyright notice,\r
+ * this list of conditions and the following disclaimer.\r
+ *\r
+ * 2. Redistributions in binary form must reproduce the above copyright notice,\r
+ * this list of conditions and the following disclaimer in the documentation\r
+ * and/or other materials provided with the distribution.\r
+ *\r
+ * 3. The name of Atmel may not be used to endorse or promote products derived\r
+ * from this software without specific prior written permission.\r
+ *\r
+ * 4. This software may only be redistributed and used in connection with an\r
+ * Atmel microcontroller product.\r
+ *\r
+ * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED\r
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
+ * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR\r
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\r
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN\r
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
+ * POSSIBILITY OF SUCH DAMAGE.\r
+ *\r
+ * \asf_license_stop\r
+ *\r
+ */\r
+/*\r
+ * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>\r
+ */\r
+\r
+#include <compiler.h>\r
+#include "sleep.h"\r
+\r
+/* SAM3,SAM4,SAMG,SAMV,SAMS and SAME series */\r
+#if (SAM3S || SAM3N || SAM3XA || SAM3U || SAM4S || SAM4E || SAM4N || SAM4C || \\r
+ SAM4CM || SAMG || SAM4CP || SAMV71 || SAMV70 || SAMS70 || SAME70)\r
+# include "pmc.h"\r
+# include "board.h"\r
+\r
+/* Checking board configuration of main clock xtal statup time */\r
+#if !defined(BOARD_OSC_STARTUP_US)\r
+# warning The board main clock xtal statup time has not been defined. Using default settings.\r
+# define BOARD_OSC_STARTUP_US (15625UL)\r
+#endif\r
+\r
+#if !defined(EFC0)\r
+# define EFC0 EFC\r
+#endif\r
+\r
+/**\r
+ * Save clock settings and shutdown PLLs\r
+ */\r
+__always_inline static void pmc_save_clock_settings(\r
+ uint32_t *p_osc_setting,\r
+ uint32_t *p_pll0_setting,\r
+ uint32_t *p_pll1_setting,\r
+ uint32_t *p_mck_setting,\r
+ uint32_t *p_fmr_setting,\r
+#if defined(EFC1)\r
+ uint32_t *p_fmr_setting1,\r
+#endif\r
+ const bool disable_xtal)\r
+{\r
+ uint32_t mor = PMC->CKGR_MOR;\r
+ uint32_t mckr = PMC->PMC_MCKR;\r
+ uint32_t fmr = EFC0->EEFC_FMR;\r
+# if defined(EFC1)\r
+ uint32_t fmr1 = EFC1->EEFC_FMR;\r
+# endif\r
+\r
+ if (p_osc_setting) {\r
+ *p_osc_setting = mor;\r
+ }\r
+ if (p_pll0_setting) {\r
+ *p_pll0_setting = PMC->CKGR_PLLAR;\r
+ }\r
+ if (p_pll1_setting) {\r
+#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)\r
+ *p_pll1_setting = PMC->CKGR_PLLBR;\r
+#elif (SAM3U || SAM3XA)\r
+ *p_pll1_setting = PMC->CKGR_UCKR;\r
+#else\r
+ *p_pll1_setting = 0;\r
+#endif\r
+ }\r
+ if (p_mck_setting) {\r
+ *p_mck_setting = mckr;\r
+ }\r
+ if (p_fmr_setting) {\r
+ *p_fmr_setting = fmr;\r
+ }\r
+#if defined(EFC1)\r
+ if (p_fmr_setting1) {\r
+ *p_fmr_setting1 = fmr1;\r
+ }\r
+#endif\r
+\r
+ /* Enable FAST RC */\r
+ PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor | CKGR_MOR_MOSCRCEN;\r
+ /* if MCK source is PLL, switch to mainck */\r
+ if ((mckr & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) {\r
+ /* MCK -> MAINCK */\r
+ mckr = (mckr & (~PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS_MAIN_CLK;\r
+ PMC->PMC_MCKR = mckr;\r
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY));\r
+ }\r
+ /* MCK prescale -> 1 */\r
+ if (mckr & PMC_MCKR_PRES_Msk) {\r
+ mckr = (mckr & (~PMC_MCKR_PRES_Msk));\r
+ PMC->PMC_MCKR = mckr;\r
+ while(!(PMC->PMC_SR & PMC_SR_MCKRDY));\r
+ }\r
+ /* Disable PLLs */\r
+ pmc_disable_pllack();\r
+#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)\r
+ pmc_disable_pllbck();\r
+#elif (SAM3U || SAM3XA)\r
+ pmc_disable_upll_clock();\r
+#endif\r
+\r
+ /* Prepare for entering WAIT mode */\r
+ /* Wait fast RC ready */\r
+ while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));\r
+\r
+ /* Switch mainck to FAST RC */\r
+#if SAMG\r
+ /**\r
+ * For the sleepwalking feature, we need an accurate RC clock. Only 24M and\r
+ * 16M are trimmed in production. Here we select the 24M.\r
+ * And so wait state need to be 1.\r
+ */\r
+ EFC0->EEFC_FMR = (fmr & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(1);\r
+\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_MOSCRCF_24_MHz |\r
+ CKGR_MOR_KEY_PASSWD;\r
+#else\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) |\r
+ CKGR_MOR_KEY_PASSWD;\r
+#endif\r
+ while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));\r
+\r
+#if (!SAMG)\r
+ /* FWS update */\r
+ EFC0->EEFC_FMR = fmr & (~EEFC_FMR_FWS_Msk);\r
+#if defined(EFC1)\r
+ EFC1->EEFC_FMR = fmr1 & (~EEFC_FMR_FWS_Msk);\r
+#endif\r
+#endif\r
+\r
+ /* Disable XTALs */\r
+ if (disable_xtal) {\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |\r
+ CKGR_MOR_KEY_PASSWD;\r
+ }\r
+}\r
+\r
+/**\r
+ * Restore clock settings\r
+ */\r
+__always_inline static void pmc_restore_clock_setting(\r
+ const uint32_t osc_setting,\r
+ const uint32_t pll0_setting,\r
+ const uint32_t pll1_setting,\r
+ const uint32_t mck_setting,\r
+ const uint32_t fmr_setting\r
+#if defined(EFC1)\r
+ , const uint32_t fmr_setting1\r
+#endif\r
+ )\r
+{\r
+ uint32_t mckr;\r
+ uint32_t pll_sr = 0;\r
+\r
+ /* Switch mainck to external xtal */\r
+ if (CKGR_MOR_MOSCXTBY == (osc_setting & CKGR_MOR_MOSCXTBY)) {\r
+ /* Bypass mode */\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |\r
+ CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY |\r
+ CKGR_MOR_MOSCSEL;\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &\r
+ ~CKGR_MOR_MOSCRCF_Msk)\r
+ | CKGR_MOR_KEY_PASSWD;\r
+ } else if (CKGR_MOR_MOSCXTEN == (osc_setting & CKGR_MOR_MOSCXTEN)) {\r
+ /* Enable External XTAL */\r
+ if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCXTEN)) {\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) |\r
+ CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN;\r
+ /* Wait the Xtal to stabilize */\r
+ while (!(PMC->PMC_SR & PMC_SR_MOSCXTS));\r
+ }\r
+ /* Select External XTAL */\r
+ if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)) {\r
+ PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;\r
+ while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));\r
+ }\r
+ /* Disable Fast RC */\r
+ PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &\r
+ ~CKGR_MOR_MOSCRCF_Msk)\r
+ | CKGR_MOR_KEY_PASSWD;\r
+ }\r
+\r
+ if (pll0_setting & CKGR_PLLAR_MULA_Msk) {\r
+#if (SAM4C || SAM4CM || SAMG || SAM4CP)\r
+ PMC->CKGR_PLLAR = pll0_setting;\r
+#else\r
+ PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | pll0_setting;\r
+#endif\r
+ pll_sr |= PMC_SR_LOCKA;\r
+ }\r
+#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)\r
+ if (pll1_setting & CKGR_PLLBR_MULB_Msk) {\r
+ PMC->CKGR_PLLBR = pll1_setting;\r
+ pll_sr |= PMC_SR_LOCKB;\r
+ }\r
+#elif (SAM3U || SAM3XA)\r
+ if (pll1_setting & CKGR_UCKR_UPLLEN) {\r
+ PMC->CKGR_UCKR = pll1_setting;\r
+ pll_sr |= PMC_SR_LOCKU;\r
+ }\r
+#else\r
+ UNUSED(pll1_setting);\r
+#endif\r
+ /* Wait MCK source ready */\r
+ switch(mck_setting & PMC_MCKR_CSS_Msk) {\r
+ case PMC_MCKR_CSS_PLLA_CLK:\r
+ while (!(PMC->PMC_SR & PMC_SR_LOCKA));\r
+ break;\r
+#if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)\r
+ case PMC_MCKR_CSS_PLLB_CLK:\r
+ while (!(PMC->PMC_SR & PMC_SR_LOCKB));\r
+ break;\r
+#elif (SAM3U || SAM3XA)\r
+ case PMC_MCKR_CSS_UPLL_CLK:\r
+ while (!(PMC->PMC_SR & PMC_SR_LOCKU));\r
+ break;\r
+#endif\r
+ }\r
+\r
+ /* Switch to faster clock */\r
+ mckr = PMC->PMC_MCKR;\r
+\r
+ /* Set PRES */\r
+ PMC->PMC_MCKR = (mckr & ~PMC_MCKR_PRES_Msk)\r
+ | (mck_setting & PMC_MCKR_PRES_Msk);\r
+ while (!(PMC->PMC_SR & PMC_SR_MCKRDY));\r
+\r
+ /* Restore flash wait states */\r
+ EFC0->EEFC_FMR = fmr_setting;\r
+#if defined(EFC1)\r
+ EFC1->EEFC_FMR = fmr_setting1;\r
+#endif\r
+\r
+ /* Set CSS and others */\r
+ PMC->PMC_MCKR = mck_setting;\r
+ while (!(PMC->PMC_SR & PMC_SR_MCKRDY));\r
+\r
+ /* Waiting all restored PLLs ready */\r
+ while (!(PMC->PMC_SR & pll_sr));\r
+}\r
+\r
+/** If clocks are switched for some sleep mode */\r
+static volatile bool b_is_sleep_clock_used = false;\r
+/** Callback invoked once when clocks are restored */\r
+static pmc_callback_wakeup_clocks_restored_t callback_clocks_restored = NULL;\r
+\r
+void pmc_sleep(int sleep_mode)\r
+{\r
+ switch (sleep_mode) {\r
+#if (!(SAMG51 || SAMG53 || SAMG54))\r
+ case SAM_PM_SMODE_SLEEP_WFI:\r
+ case SAM_PM_SMODE_SLEEP_WFE:\r
+#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)\r
+ SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;\r
+ cpu_irq_enable();\r
+ __WFI();\r
+ break;\r
+#else\r
+ PMC->PMC_FSMR &= (uint32_t)~PMC_FSMR_LPM;\r
+ SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;\r
+ cpu_irq_enable();\r
+ if (sleep_mode == SAM_PM_SMODE_SLEEP_WFI)\r
+ __WFI();\r
+ else\r
+ __WFE();\r
+ break;\r
+#endif\r
+#endif\r
+\r
+ case SAM_PM_SMODE_WAIT_FAST:\r
+ case SAM_PM_SMODE_WAIT: {\r
+ uint32_t mor, pllr0, pllr1, mckr;\r
+ uint32_t fmr;\r
+#if defined(EFC1)\r
+ uint32_t fmr1;\r
+#endif\r
+#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)\r
+ (sleep_mode == SAM_PM_SMODE_WAIT_FAST) ?\r
+ pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_STANDBY) :\r
+ pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_DEEP_POWERDOWN);\r
+#endif\r
+ cpu_irq_disable();\r
+ b_is_sleep_clock_used = true;\r
+\r
+#if (SAM4C || SAM4CM || SAM4CP)\r
+ /* Backup the sub-system 1 status and stop sub-system 1 */\r
+ uint32_t cpclk_backup = PMC->PMC_SCSR &\r
+ (PMC_SCSR_CPCK | PMC_SCSR_CPBMCK);\r
+ PMC->PMC_SCDR = cpclk_backup | PMC_SCDR_CPKEY_PASSWD;\r
+#endif\r
+ pmc_save_clock_settings(&mor, &pllr0, &pllr1, &mckr, &fmr,\r
+#if defined(EFC1)\r
+ &fmr1,\r
+#endif\r
+ (sleep_mode == SAM_PM_SMODE_WAIT));\r
+\r
+ /* Enter wait mode */\r
+ cpu_irq_enable();\r
+\r
+ pmc_enable_waitmode();\r
+\r
+ cpu_irq_disable();\r
+ pmc_restore_clock_setting(mor, pllr0, pllr1, mckr, fmr\r
+#if defined(EFC1)\r
+ , fmr1\r
+#endif\r
+ );\r
+\r
+#if (SAM4C || SAM4CM || SAM4CP)\r
+ /* Restore the sub-system 1 */\r
+ PMC->PMC_SCER = cpclk_backup | PMC_SCER_CPKEY_PASSWD;\r
+#endif\r
+ b_is_sleep_clock_used = false;\r
+ if (callback_clocks_restored) {\r
+ callback_clocks_restored();\r
+ callback_clocks_restored = NULL;\r
+ }\r
+ cpu_irq_enable();\r
+\r
+ break;\r
+ }\r
+#if (!(SAMG51 || SAMG53 || SAMG54))\r
+ case SAM_PM_SMODE_BACKUP:\r
+ SCB->SCR |= SCR_SLEEPDEEP;\r
+#if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)\r
+ SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_VROFF_STOP_VREG;\r
+ cpu_irq_enable();\r
+ __WFI() ;\r
+#else\r
+ cpu_irq_enable();\r
+ __WFE() ;\r
+#endif\r
+ break;\r
+#endif\r
+ }\r
+}\r
+\r
+bool pmc_is_wakeup_clocks_restored(void)\r
+{\r
+ return !b_is_sleep_clock_used;\r
+}\r
+\r
+void pmc_wait_wakeup_clocks_restore(\r
+ pmc_callback_wakeup_clocks_restored_t callback)\r
+{\r
+ if (b_is_sleep_clock_used) {\r
+ cpu_irq_disable();\r
+ callback_clocks_restored = callback;\r
+ } else if (callback) {\r
+ callback();\r
+ }\r
+}\r
+\r
+#endif\r