1 /**
2 * \file
3 *
4 * \brief Sleep mode access
5 *
6 * Copyright (c) 2012-2015 Atmel Corporation. All rights reserved.
7 *
8 * \asf_license_start
9 *
10 * \page License
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 *
15 * 1. Redistributions of source code must retain the above copyright notice,
16 * this list of conditions and the following disclaimer.
17 *
18 * 2. Redistributions in binary form must reproduce the above copyright notice,
19 * this list of conditions and the following disclaimer in the documentation
20 * and/or other materials provided with the distribution.
21 *
22 * 3. The name of Atmel may not be used to endorse or promote products derived
23 * from this software without specific prior written permission.
24 *
25 * 4. This software may only be redistributed and used in connection with an
26 * Atmel microcontroller product.
27 *
28 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38 * POSSIBILITY OF SUCH DAMAGE.
39 *
40 * \asf_license_stop
41 *
42 */
43 /*
44 * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45 */
46
47 #include <compiler.h>
48 #include "sleep.h"
49
50 /* SAM3 and SAM4 series */
51 #if (SAM3S || SAM3N || SAM3XA || SAM3U || SAM4S || SAM4E || SAM4N || SAM4C || \
52 SAM4CM || SAMG || SAM4CP || SAMV71 || SAMV70 || SAMS70 || SAME70)
53 # include "pmc.h"
54 # include "board.h"
55
56 /* Checking board configuration of main clock xtal statup time */
57 #if !defined(BOARD_OSC_STARTUP_US)
58 # warning The board main clock xtal statup time has not been defined. Using default settings.
59 # define BOARD_OSC_STARTUP_US (15625UL)
60 #endif
61
62 #if !defined(EFC0)
63 # define EFC0 EFC
64 #endif
65
66 /**
67 * Save clock settings and shutdown PLLs
68 */
pmc_save_clock_settings(uint32_t * p_osc_setting,uint32_t * p_pll0_setting,uint32_t * p_pll1_setting,uint32_t * p_mck_setting,uint32_t * p_fmr_setting,uint32_t * p_fmr_setting1,const bool disable_xtal)69 __always_inline static void pmc_save_clock_settings(
70 uint32_t *p_osc_setting,
71 uint32_t *p_pll0_setting,
72 uint32_t *p_pll1_setting,
73 uint32_t *p_mck_setting,
74 uint32_t *p_fmr_setting,
75 #if defined(EFC1)
76 uint32_t *p_fmr_setting1,
77 #endif
78 const bool disable_xtal)
79 {
80 uint32_t mor = PMC->CKGR_MOR;
81 uint32_t mckr = PMC->PMC_MCKR;
82 uint32_t fmr = EFC0->EEFC_FMR;
83 # if defined(EFC1)
84 uint32_t fmr1 = EFC1->EEFC_FMR;
85 # endif
86
87 if (p_osc_setting) {
88 *p_osc_setting = mor;
89 }
90 if (p_pll0_setting) {
91 *p_pll0_setting = PMC->CKGR_PLLAR;
92 }
93 if (p_pll1_setting) {
94 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
95 *p_pll1_setting = PMC->CKGR_PLLBR;
96 #elif (SAM3U || SAM3XA)
97 *p_pll1_setting = PMC->CKGR_UCKR;
98 #else
99 *p_pll1_setting = 0;
100 #endif
101 }
102 if (p_mck_setting) {
103 *p_mck_setting = mckr;
104 }
105 if (p_fmr_setting) {
106 *p_fmr_setting = fmr;
107 }
108 #if defined(EFC1)
109 if (p_fmr_setting1) {
110 *p_fmr_setting1 = fmr1;
111 }
112 #endif
113
114 /* Enable FAST RC */
115 PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor | CKGR_MOR_MOSCRCEN;
116 /* if MCK source is PLL, switch to mainck */
117 if ((mckr & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) {
118 /* MCK -> MAINCK */
119 mckr = (mckr & (~PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS_MAIN_CLK;
120 PMC->PMC_MCKR = mckr;
121 while(!(PMC->PMC_SR & PMC_SR_MCKRDY));
122 }
123 /* MCK prescale -> 1 */
124 if (mckr & PMC_MCKR_PRES_Msk) {
125 mckr = (mckr & (~PMC_MCKR_PRES_Msk));
126 PMC->PMC_MCKR = mckr;
127 while(!(PMC->PMC_SR & PMC_SR_MCKRDY));
128 }
129 /* Disable PLLs */
130 pmc_disable_pllack();
131 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
132 pmc_disable_pllbck();
133 #elif (SAM3U || SAM3XA)
134 pmc_disable_upll_clock();
135 #endif
136
137 /* Prepare for entering WAIT mode */
138 /* Wait fast RC ready */
139 while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));
140
141 /* Switch mainck to FAST RC */
142 #if SAMG
143 /**
144 * For the sleepwalking feature, we need an accurate RC clock. Only 24M and
145 * 16M are trimmed in production. Here we select the 24M.
146 * And so wait state need to be 1.
147 */
148 EFC0->EEFC_FMR = (fmr & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(1);
149
150 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_MOSCRCF_24_MHz |
151 CKGR_MOR_KEY_PASSWD;
152 #else
153 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) |
154 CKGR_MOR_KEY_PASSWD;
155 #endif
156 while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));
157
158 #if (!SAMG)
159 /* FWS update */
160 EFC0->EEFC_FMR = fmr & (~EEFC_FMR_FWS_Msk);
161 #if defined(EFC1)
162 EFC1->EEFC_FMR = fmr1 & (~EEFC_FMR_FWS_Msk);
163 #endif
164 #endif
165
166 /* Disable XTALs */
167 if (disable_xtal) {
168 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
169 CKGR_MOR_KEY_PASSWD;
170 }
171 }
172
173 /**
174 * Restore clock settings
175 */
pmc_restore_clock_setting(const uint32_t osc_setting,const uint32_t pll0_setting,const uint32_t pll1_setting,const uint32_t mck_setting,const uint32_t fmr_setting,const uint32_t fmr_setting1)176 __always_inline static void pmc_restore_clock_setting(
177 const uint32_t osc_setting,
178 const uint32_t pll0_setting,
179 const uint32_t pll1_setting,
180 const uint32_t mck_setting,
181 const uint32_t fmr_setting
182 #if defined(EFC1)
183 , const uint32_t fmr_setting1
184 #endif
185 )
186 {
187 uint32_t mckr;
188 uint32_t pll_sr = 0;
189
190 /* Switch mainck to external xtal */
191 if (CKGR_MOR_MOSCXTBY == (osc_setting & CKGR_MOR_MOSCXTBY)) {
192 /* Bypass mode */
193 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) |
194 CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY |
195 CKGR_MOR_MOSCSEL;
196 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &
197 ~CKGR_MOR_MOSCRCF_Msk)
198 | CKGR_MOR_KEY_PASSWD;
199 } else if (CKGR_MOR_MOSCXTEN == (osc_setting & CKGR_MOR_MOSCXTEN)) {
200 /* Enable External XTAL */
201 if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCXTEN)) {
202 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY) |
203 CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN;
204 /* Wait the Xtal to stabilize */
205 while (!(PMC->PMC_SR & PMC_SR_MOSCXTS));
206 }
207 /* Select External XTAL */
208 if (!(PMC->CKGR_MOR & CKGR_MOR_MOSCSEL)) {
209 PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
210 while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));
211 }
212 /* Disable Fast RC */
213 PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN &
214 ~CKGR_MOR_MOSCRCF_Msk)
215 | CKGR_MOR_KEY_PASSWD;
216 }
217
218 if (pll0_setting & CKGR_PLLAR_MULA_Msk) {
219 #if (SAM4C || SAM4CM || SAMG || SAM4CP)
220 PMC->CKGR_PLLAR = pll0_setting;
221 #else
222 PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | pll0_setting;
223 #endif
224 pll_sr |= PMC_SR_LOCKA;
225 }
226 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
227 if (pll1_setting & CKGR_PLLBR_MULB_Msk) {
228 PMC->CKGR_PLLBR = pll1_setting;
229 pll_sr |= PMC_SR_LOCKB;
230 }
231 #elif (SAM3U || SAM3XA)
232 if (pll1_setting & CKGR_UCKR_UPLLEN) {
233 PMC->CKGR_UCKR = pll1_setting;
234 pll_sr |= PMC_SR_LOCKU;
235 }
236 #else
237 UNUSED(pll1_setting);
238 #endif
239 /* Wait MCK source ready */
240 switch(mck_setting & PMC_MCKR_CSS_Msk) {
241 case PMC_MCKR_CSS_PLLA_CLK:
242 while (!(PMC->PMC_SR & PMC_SR_LOCKA));
243 break;
244 #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP)
245 case PMC_MCKR_CSS_PLLB_CLK:
246 while (!(PMC->PMC_SR & PMC_SR_LOCKB));
247 break;
248 #elif (SAM3U || SAM3XA)
249 case PMC_MCKR_CSS_UPLL_CLK:
250 while (!(PMC->PMC_SR & PMC_SR_LOCKU));
251 break;
252 #endif
253 }
254
255 /* Switch to faster clock */
256 mckr = PMC->PMC_MCKR;
257
258 /* Set PRES */
259 PMC->PMC_MCKR = (mckr & ~PMC_MCKR_PRES_Msk)
260 | (mck_setting & PMC_MCKR_PRES_Msk);
261 while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
262
263 /* Restore flash wait states */
264 EFC0->EEFC_FMR = fmr_setting;
265 #if defined(EFC1)
266 EFC1->EEFC_FMR = fmr_setting1;
267 #endif
268
269 /* Set CSS and others */
270 PMC->PMC_MCKR = mck_setting;
271 while (!(PMC->PMC_SR & PMC_SR_MCKRDY));
272
273 /* Waiting all restored PLLs ready */
274 while (!(PMC->PMC_SR & pll_sr));
275 }
276
277 /** If clocks are switched for some sleep mode */
278 static volatile bool b_is_sleep_clock_used = false;
279 /** Callback invoked once when clocks are restored */
280 static pmc_callback_wakeup_clocks_restored_t callback_clocks_restored = NULL;
281
pmc_sleep(int sleep_mode)282 void pmc_sleep(int sleep_mode)
283 {
284 switch (sleep_mode) {
285 #if (!(SAMG51 || SAMG53 || SAMG54))
286 case SAM_PM_SMODE_SLEEP_WFI:
287 case SAM_PM_SMODE_SLEEP_WFE:
288 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
289 SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;
290 cpu_irq_enable();
291 __WFI();
292 break;
293 #else
294 PMC->PMC_FSMR &= (uint32_t)~PMC_FSMR_LPM;
295 SCB->SCR &= (uint32_t)~SCR_SLEEPDEEP;
296 cpu_irq_enable();
297 if (sleep_mode == SAM_PM_SMODE_SLEEP_WFI)
298 __WFI();
299 else
300 __WFE();
301 break;
302 #endif
303 #endif
304
305 case SAM_PM_SMODE_WAIT_FAST:
306 case SAM_PM_SMODE_WAIT: {
307 uint32_t mor, pllr0, pllr1, mckr;
308 uint32_t fmr;
309 #if defined(EFC1)
310 uint32_t fmr1;
311 #endif
312 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
313 (sleep_mode == SAM_PM_SMODE_WAIT_FAST) ?
314 pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_STANDBY) :
315 pmc_set_flash_in_wait_mode(PMC_FSMR_FLPM_FLASH_DEEP_POWERDOWN);
316 #endif
317 cpu_irq_disable();
318 b_is_sleep_clock_used = true;
319
320 #if (SAM4C || SAM4CM || SAM4CP)
321 /* Backup the sub-system 1 status and stop sub-system 1 */
322 uint32_t cpclk_backup = PMC->PMC_SCSR &
323 (PMC_SCSR_CPCK | PMC_SCSR_CPBMCK);
324 PMC->PMC_SCDR = cpclk_backup | PMC_SCDR_CPKEY_PASSWD;
325 #endif
326 pmc_save_clock_settings(&mor, &pllr0, &pllr1, &mckr, &fmr,
327 #if defined(EFC1)
328 &fmr1,
329 #endif
330 (sleep_mode == SAM_PM_SMODE_WAIT));
331
332 /* Enter wait mode */
333 cpu_irq_enable();
334
335 pmc_enable_waitmode();
336
337 cpu_irq_disable();
338 pmc_restore_clock_setting(mor, pllr0, pllr1, mckr, fmr
339 #if defined(EFC1)
340 , fmr1
341 #endif
342 );
343
344 #if (SAM4C || SAM4CM || SAM4CP)
345 /* Restore the sub-system 1 */
346 PMC->PMC_SCER = cpclk_backup | PMC_SCER_CPKEY_PASSWD;
347 #endif
348 b_is_sleep_clock_used = false;
349 if (callback_clocks_restored) {
350 callback_clocks_restored();
351 callback_clocks_restored = NULL;
352 }
353 cpu_irq_enable();
354
355 break;
356 }
357 #if (!(SAMG51 || SAMG53 || SAMG54))
358 case SAM_PM_SMODE_BACKUP:
359 SCB->SCR |= SCR_SLEEPDEEP;
360 #if (SAM4S || SAM4E || SAM4N || SAM4C || SAM4CM || SAM4CP || SAMG55 || SAMV71 || SAMV70 || SAMS70 || SAME70)
361 SUPC->SUPC_CR = SUPC_CR_KEY_PASSWD | SUPC_CR_VROFF_STOP_VREG;
362 cpu_irq_enable();
363 __WFI() ;
364 #else
365 cpu_irq_enable();
366 __WFE() ;
367 #endif
368 break;
369 #endif
370 }
371 }
372
pmc_is_wakeup_clocks_restored(void)373 bool pmc_is_wakeup_clocks_restored(void)
374 {
375 return !b_is_sleep_clock_used;
376 }
377
pmc_wait_wakeup_clocks_restore(pmc_callback_wakeup_clocks_restored_t callback)378 void pmc_wait_wakeup_clocks_restore(
379 pmc_callback_wakeup_clocks_restored_t callback)
380 {
381 if (b_is_sleep_clock_used) {
382 cpu_irq_disable();
383 callback_clocks_restored = callback;
384 } else if (callback) {
385 callback();
386 }
387 }
388
389 #endif
390