4 * \brief Sleep mode access
\r
6 * Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.
\r
12 * Redistribution and use in source and binary forms, with or without
\r
13 * modification, are permitted provided that the following conditions are met:
\r
15 * 1. Redistributions of source code must retain the above copyright notice,
\r
16 * this list of conditions and the following disclaimer.
\r
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
\r
19 * this list of conditions and the following disclaimer in the documentation
\r
20 * and/or other materials provided with the distribution.
\r
22 * 3. The name of Atmel may not be used to endorse or promote products derived
\r
23 * from this software without specific prior written permission.
\r
25 * 4. This software may only be redistributed and used in connection with an
\r
26 * Atmel microcontroller product.
\r
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
\r
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
\r
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
\r
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
\r
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
\r
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
\r
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r
38 * POSSIBILITY OF SUCH DAMAGE.
\r
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
\r
47 #include <compiler.h>
\r
50 /* SAM3,SAM4,SAMG,SAMV,SAMS and SAME series */
\r
51 #if (SAM3S || SAM3N || SAM3XA || SAM3U || SAM4S || SAM4E || SAM4N || SAM4C || \
\r
52 SAM4CM || SAMG || SAM4CP || SAMV71 || SAMV70 || SAMS70 || SAME70)
\r
56 /* Checking board configuration of main clock xtal statup time */
\r
57 #if !defined(BOARD_OSC_STARTUP_US)
\r
58 # warning The board main clock xtal statup time has not been defined. Using default settings.
\r
59 # define BOARD_OSC_STARTUP_US (15625UL)
\r
67 * Save clock settings and shutdown PLLs
\r
69 __always_inline static void pmc_save_clock_settings(
\r
70 uint32_t *p_osc_setting,
\r
71 uint32_t *p_pll0_setting,
\r
72 uint32_t *p_pll1_setting,
\r
73 uint32_t *p_mck_setting,
\r
74 uint32_t *p_fmr_setting,
\r
76 uint32_t *p_fmr_setting1,
\r
78 const bool disable_xtal)
\r
80 uint32_t mor = PMC->CKGR_MOR;
\r
81 uint32_t mckr = PMC->PMC_MCKR;
\r
82 uint32_t fmr = EFC0->EEFC_FMR;
\r
84 uint32_t fmr1 = EFC1->EEFC_FMR;
\r
87 if (p_osc_setting) {
\r
88 *p_osc_setting = mor;
\r
90 if (p_pll0_setting) {
\r
91 *p_pll0_setting = PMC->CKGR_PLLAR;
\r
93 if (p_pll1_setting) {
\r
94 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
\r
95 *p_pll1_setting = PMC->CKGR_PLLBR;
\r
96 #elif (SAM3U || SAM3XA)
\r
97 *p_pll1_setting = PMC->CKGR_UCKR;
\r
99 *p_pll1_setting = 0;
\r
102 if (p_mck_setting) {
\r
103 *p_mck_setting = mckr;
\r
105 if (p_fmr_setting) {
\r
106 *p_fmr_setting = fmr;
\r
109 if (p_fmr_setting1) {
\r
110 *p_fmr_setting1 = fmr1;
\r
114 /* Enable FAST RC */
\r
115 PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor | CKGR_MOR_MOSCRCEN;
\r
116 /* if MCK source is PLL, switch to mainck */
\r
117 if ((mckr & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) {
\r
118 /* MCK -> MAINCK */
\r
119 mckr = (mckr & (~PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS_MAIN_CLK;
\r
120 PMC->PMC_MCKR = mckr;
\r
121 while(!(PMC->PMC_SR & PMC_SR_MCKRDY));
\r
123 /* MCK prescale -> 1 */
\r
124 if (mckr & PMC_MCKR_PRES_Msk) {
\r
125 mckr = (mckr & (~PMC_MCKR_PRES_Msk));
\r
126 PMC->PMC_MCKR = mckr;
\r
127 while(!(PMC->PMC_SR & PMC_SR_MCKRDY));
\r
130 pmc_disable_pllack();
\r
131 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
\r
132 pmc_disable_pllbck();
\r
133 #elif (SAM3U || SAM3XA)
\r
134 pmc_disable_upll_clock();
\r
137 /* Prepare for entering WAIT mode */
\r
138 /* Wait fast RC ready */
\r
139 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));
\r
141 /* Switch mainck to FAST RC */
\r
144 * For the sleepwalking feature, we need an accurate RC clock. Only 24M and
\r
145 * 16M are trimmed in production. Here we select the 24M.
\r
146 * And so wait state need to be 1.
\r
148 EFC0->EEFC_FMR = (fmr & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(1);
\r
150 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_MOSCRCF_24_MHz |
\r
151 CKGR_MOR_KEY_PASSWD;
\r
153 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) |
\r
154 CKGR_MOR_KEY_PASSWD;
\r
156 while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));
\r
160 EFC0->EEFC_FMR = fmr & (~EEFC_FMR_FWS_Msk);
\r
162 EFC1->EEFC_FMR = fmr1 & (~EEFC_FMR_FWS_Msk);
\r
166 /* Disable XTALs */
\r
167 if (disable_xtal) {
\r
168 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
\r
169 CKGR_MOR_KEY_PASSWD;
\r
174 * Restore clock settings
\r
176 __always_inline static void pmc_restore_clock_setting(
\r
177 const uint32_t osc_setting,
\r
178 const uint32_t pll0_setting,
\r
179 const uint32_t pll1_setting,
\r
180 const uint32_t mck_setting,
\r
181 const uint32_t fmr_setting
\r
183 , const uint32_t fmr_setting1
\r
188 uint32_t pll_sr = 0;
\r
190 /* Switch mainck to external xtal */
\r
191 if (CKGR_MOR_MOSCXTBY == (osc_setting & CKGR_MOR_MOSCXTBY)) {
\r
193 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
\r
194 CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY |
\r
196 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &
\r
197 ~CKGR_MOR_MOSCRCF_Msk)
\r
198 | CKGR_MOR_KEY_PASSWD;
\r
199 } else if (CKGR_MOR_MOSCXTEN == (osc_setting & CKGR_MOR_MOSCXTEN)) {
\r
200 /* Enable External XTAL */
\r
201 if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCXTEN)) {
\r
202 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) |
\r
203 CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN;
\r
204 /* Wait the Xtal to stabilize */
\r
205 while (!(PMC->PMC_SR & PMC_SR_MOSCXTS));
\r
207 /* Select External XTAL */
\r
208 if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)) {
\r
209 PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
\r
210 while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));
\r
212 /* Disable Fast RC */
\r
213 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &
\r
214 ~CKGR_MOR_MOSCRCF_Msk)
\r
215 | CKGR_MOR_KEY_PASSWD;
\r
218 if (pll0_setting & CKGR_PLLAR_MULA_Msk) {
\r
219 #if (SAM4C || SAM4CM || SAMG || SAM4CP)
\r
220 PMC->CKGR_PLLAR = pll0_setting;
\r
222 PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | pll0_setting;
\r
224 pll_sr |= PMC_SR_LOCKA;
\r
226 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
\r
227 if (pll1_setting & CKGR_PLLBR_MULB_Msk) {
\r
228 PMC->CKGR_PLLBR = pll1_setting;
\r
229 pll_sr |= PMC_SR_LOCKB;
\r
231 #elif (SAM3U || SAM3XA)
\r
232 if (pll1_setting & CKGR_UCKR_UPLLEN) {
\r
233 PMC->CKGR_UCKR = pll1_setting;
\r
234 pll_sr |= PMC_SR_LOCKU;
\r
237 UNUSED(pll1_setting);
\r
239 /* Wait MCK source ready */
\r
240 switch(mck_setting & PMC_MCKR_CSS_Msk) {
\r
241 case PMC_MCKR_CSS_PLLA_CLK:
\r
242 while (!(PMC->PMC_SR & PMC_SR_LOCKA));
\r
244 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
\r
245 case PMC_MCKR_CSS_PLLB_CLK:
\r
246 while (!(PMC->PMC_SR & PMC_SR_LOCKB));
\r
248 #elif (SAM3U || SAM3XA)
\r
249 case PMC_MCKR_CSS_UPLL_CLK:
\r
250 while (!(PMC->PMC_SR & PMC_SR_LOCKU));
\r
255 /* Switch to faster clock */
\r
256 mckr = PMC->PMC_MCKR;
\r
259 PMC->PMC_MCKR = (mckr & ~PMC_MCKR_PRES_Msk)
\r
260 | (mck_setting & PMC_MCKR_PRES_Msk);
\r
261 while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
\r
263 /* Restore flash wait states */
\r
264 EFC0->EEFC_FMR = fmr_setting;
\r
266 EFC1->EEFC_FMR = fmr_setting1;
\r
269 /* Set CSS and others */
\r
270 PMC->PMC_MCKR = mck_setting;
\r
271 while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
\r
273 /* Waiting all restored PLLs ready */
\r
274 while (!(PMC->PMC_SR & pll_sr));
\r
277 /** If clocks are switched for some sleep mode */
\r
278 static volatile bool b_is_sleep_clock_used = false;
\r
279 /** Callback invoked once when clocks are restored */
\r
280 static pmc_callback_wakeup_clocks_restored_t callback_clocks_restored = NULL;
\r
282 void pmc_sleep(int sleep_mode)
\r
284 switch (sleep_mode) {
\r
285 #if (!(SAMG51 || SAMG53 || SAMG54))
\r
286 case SAM_PM_SMODE_SLEEP_WFI:
\r
287 case SAM_PM_SMODE_SLEEP_WFE:
\r
288 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
\r
289 SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;
\r
294 PMC->PMC_FSMR &= (uint32_t)~PMC_FSMR_LPM;
\r
295 SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;
\r
297 if (sleep_mode == SAM_PM_SMODE_SLEEP_WFI)
\r
305 case SAM_PM_SMODE_WAIT_FAST:
\r
306 case SAM_PM_SMODE_WAIT: {
\r
307 uint32_t mor, pllr0, pllr1, mckr;
\r
312 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
\r
313 (sleep_mode == SAM_PM_SMODE_WAIT_FAST) ?
\r
314 pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_STANDBY) :
\r
315 pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_DEEP_POWERDOWN);
\r
318 b_is_sleep_clock_used = true;
\r
320 #if (SAM4C || SAM4CM || SAM4CP)
\r
321 /* Backup the sub-system 1 status and stop sub-system 1 */
\r
322 uint32_t cpclk_backup = PMC->PMC_SCSR &
\r
323 (PMC_SCSR_CPCK | PMC_SCSR_CPBMCK);
\r
324 PMC->PMC_SCDR = cpclk_backup | PMC_SCDR_CPKEY_PASSWD;
\r
326 pmc_save_clock_settings(&mor, &pllr0, &pllr1, &mckr, &fmr,
\r
330 (sleep_mode == SAM_PM_SMODE_WAIT));
\r
332 /* Enter wait mode */
\r
335 pmc_enable_waitmode();
\r
338 pmc_restore_clock_setting(mor, pllr0, pllr1, mckr, fmr
\r
344 #if (SAM4C || SAM4CM || SAM4CP)
\r
345 /* Restore the sub-system 1 */
\r
346 PMC->PMC_SCER = cpclk_backup | PMC_SCER_CPKEY_PASSWD;
\r
348 b_is_sleep_clock_used = false;
\r
349 if (callback_clocks_restored) {
\r
350 callback_clocks_restored();
\r
351 callback_clocks_restored = NULL;
\r
357 #if (!(SAMG51 || SAMG53 || SAMG54))
\r
358 case SAM_PM_SMODE_BACKUP:
\r
359 SCB->SCR |= SCR_SLEEPDEEP;
\r
360 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
\r
361 SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_VROFF_STOP_VREG;
\r
373 bool pmc_is_wakeup_clocks_restored(void)
\r
375 return !b_is_sleep_clock_used;
\r
378 void pmc_wait_wakeup_clocks_restore(
\r
379 pmc_callback_wakeup_clocks_restored_t callback)
\r
381 if (b_is_sleep_clock_used) {
\r
383 callback_clocks_restored = callback;
\r
384 } else if (callback) {
\r