xref: /btstack/port/samv71-xplained-atwilc3000/ASF/sam/drivers/pmc/sleep.c (revision 1b2596b5303dd8caeea8565532c93cca8dab8cc4)
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