1 /**
2  * \file
3  *
4  * \brief Power Management Controller register related functionality.
5  *
6  * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Subject to your compliance with these terms, you may use Microchip
13  * software and any derivatives exclusively with Microchip products.
14  * It is your responsibility to comply with third party license terms applicable
15  * to your use of third party software (including open source software) that
16  * may accompany Microchip software.
17  *
18  * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
19  * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
20  * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
21  * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
22  * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
23  * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
24  * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
25  * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
26  * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
27  * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
28  * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
29  *
30  * \asf_license_stop
31  *
32  */
33 #include <compiler.h>
34 #include <hpl_sleep.h>
35 
36 /* Expected sleep mode for \ref _go_to_sleep() to enter */
37 static uint8_t _sleep_mode = 0;
38 
39 /* Wait mode backup for MOR register */
40 static uint32_t _mor;
41 /* Wait mode backup for MCKR register */
42 static uint32_t _mckr;
43 /* Wait mode backup for FMR register */
44 static uint32_t _fmr;
45 /* Wait mode backup for PLLAR register */
46 static uint32_t _pllar;
47 /* Wait mode backup for UCKR register */
48 static uint32_t _uckr;
49 
50 /**
51  * \brief Set the sleep mode for the device
52  */
_set_sleep_mode(const uint8_t mode)53 int32_t _set_sleep_mode(const uint8_t mode)
54 {
55 	switch (mode) {
56 	case 0: /* Sleep Mode */
57 		break;
58 	case 1: /* Wait Mode - USB suspend */
59 	case 2: /* Wait Mode */
60 		SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
61 		break;
62 	case 3: /* Backup Mode */
63 		SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
64 		hri_supc_write_CR_reg(SUPC, SUPC_CR_KEY_PASSWD | SUPC_CR_VROFF_STOP_VREG);
65 		break;
66 	default:
67 		return ERR_INVALID_ARG;
68 	}
69 	_sleep_mode = mode;
70 	return ERR_NONE;
71 }
72 
73 /**
74  * \brief Save clock settings and shutdown PLLs/OSCs
75  */
_save_clocks(void)76 static inline void _save_clocks(void)
77 {
78 	uint32_t mor = _mor = hri_pmc_read_CKGR_MOR_reg(PMC);
79 	uint32_t mckr = _mckr = hri_pmc_read_MCKR_reg(PMC);
80 	_fmr                  = hri_efc_read_EEFC_FMR_reg(EFC);
81 	_pllar                = hri_pmc_read_CKGR_PLLAR_reg(PMC);
82 	_uckr                 = hri_pmc_read_CKGR_UCKR_reg(PMC);
83 
84 	/* Enable FAST RC */
85 	mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN;
86 	hri_pmc_write_CKGR_MOR_reg(PMC, mor);
87 
88 	/* If MCK source is PLL, switch to MAINCK */
89 	if ((mckr & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) {
90 		/* MCK -> MAINCK */
91 		mckr = (mckr & (~PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS_MAIN_CLK;
92 		hri_pmc_write_MCKR_reg(PMC, mckr);
93 		while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MCKRDY)) {
94 			/* Wait MCK ready */
95 		}
96 	}
97 
98 	/* MCK PRES -> 1 */
99 	if (mckr & PMC_MCKR_PRES_Msk) {
100 		mckr = (mckr & (~PMC_MCKR_PRES_Msk));
101 		hri_pmc_write_MCKR_reg(PMC, mckr);
102 		while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MCKRDY)) {
103 			/* Wait MCK ready */
104 		}
105 	}
106 
107 	/* Disable PLL(s) */
108 	hri_pmc_write_CKGR_PLLAR_reg(PMC, CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(0));
109 	hri_pmc_write_CKGR_UCKR_reg(PMC, 0);
110 
111 	/* Prepare for entering WAIT mode */
112 	while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MOSCRCS)) {
113 		/* Wait fast RC ready */
114 	}
115 
116 	/* Switch MAINCK to FAST RC */
117 	mor &= ~CKGR_MOR_MOSCSEL;
118 	hri_pmc_write_CKGR_MOR_reg(PMC, mor);
119 	while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MOSCSELS)) {
120 		/* Wait switch to FAST RC */
121 	}
122 
123 	/* Update FWS */
124 	hri_efc_write_EEFC_FMR_reg(EFC, _fmr & (~EEFC_FMR_FWS_Msk));
125 
126 	/* Disable XTALs */
127 	if (_sleep_mode == 2) {
128 		mor &= ~CKGR_MOR_MOSCXTEN;
129 		hri_pmc_write_CKGR_MOR_reg(PMC, mor);
130 	}
131 }
132 
_restore_clocks(void)133 static inline void _restore_clocks(void)
134 {
135 	uint32_t mor;
136 	uint32_t mckr;
137 	uint32_t pll_sr = 0;
138 
139 	/* Switch MAINCK to external XTAL */
140 	if (CKGR_MOR_MOSCXTBY == (_mor & CKGR_MOR_MOSCXTBY)) {
141 		/* Bypass mode */
142 		/* Select bypass mode */
143 		mor = hri_pmc_read_CKGR_MOR_reg(PMC);
144 		mor = (mor & (~CKGR_MOR_MOSCXTEN)) | CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCSEL;
145 		hri_pmc_write_CKGR_MOR_reg(PMC, mor);
146 		/* Fully restore (Disable XTALs) */
147 		hri_pmc_write_CKGR_MOR_reg(PMC, _mor | CKGR_MOR_KEY_PASSWD);
148 	} else if (CKGR_MOR_MOSCXTEN == (_mor & CKGR_MOR_MOSCXTEN)) {
149 		/* Enable External XTAL */
150 		if (!hri_pmc_get_CKGR_MOR_reg(PMC, CKGR_MOR_MOSCXTEN)) {
151 			mor = hri_pmc_read_CKGR_MOR_reg(PMC);
152 			mor = (mor & (~CKGR_MOR_MOSCXTBY)) | CKGR_MOR_MOSCXTEN;
153 			hri_pmc_write_CKGR_MOR_reg(PMC, mor);
154 			while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MOSCXTS)) {
155 				/* Wait the XTAL to stabilize */
156 			}
157 		}
158 		/* Select External XTAL */
159 		if (!hri_pmc_get_CKGR_MOR_reg(PMC, CKGR_MOR_MOSCSEL)) {
160 			mor = hri_pmc_read_CKGR_MOR_reg(PMC);
161 			mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
162 			hri_pmc_write_CKGR_MOR_reg(PMC, mor);
163 			while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MOSCSELS)) {
164 				/* Wait the XTAL switch */
165 			}
166 		}
167 		/* Fully restore (Disable Fast RC) */
168 		hri_pmc_write_CKGR_MOR_reg(PMC, _mor | CKGR_MOR_KEY_PASSWD);
169 	}
170 
171 	if (_pllar & CKGR_PLLAR_MULA_Msk) {
172 		hri_pmc_write_CKGR_PLLAR_reg(PMC, _pllar | CKGR_PLLAR_ONE);
173 		pll_sr |= PMC_SR_LOCKA;
174 	}
175 	if (_uckr & CKGR_UCKR_UPLLEN) {
176 		hri_pmc_write_CKGR_UCKR_reg(PMC, _uckr);
177 		pll_sr |= PMC_SR_LOCKU;
178 	}
179 
180 	/* Wait MCK source ready */
181 	switch (_mckr & PMC_MCKR_CSS_Msk) {
182 	case PMC_MCKR_CSS_PLLA_CLK:
183 		while (!hri_pmc_get_SR_reg(PMC, PMC_SR_LOCKA)) {
184 			/* Wait PLLA lock */
185 		}
186 		break;
187 	case PMC_MCKR_CSS_UPLL_CLK:
188 		while (!hri_pmc_get_SR_reg(PMC, PMC_SR_LOCKU)) {
189 			/* Wait UPLL lock */
190 		}
191 	}
192 
193 	/* Switch to faster clock */
194 	mckr = hri_pmc_read_MCKR_reg(PMC);
195 	mckr = (mckr & (~PMC_MCKR_PRES_Msk)) | (_mckr & PMC_MCKR_PRES_Msk);
196 	hri_pmc_write_MCKR_reg(PMC, mckr);
197 	while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MCKRDY)) {
198 		/* Wait MCK ready */
199 	}
200 
201 	/* Restore FWS */
202 	hri_efc_write_EEFC_FMR_reg(EFC, _fmr);
203 
204 	/* Fully restore MCKR (CSS and others) */
205 	hri_pmc_write_MCKR_reg(PMC, _mckr);
206 	while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MCKRDY)) {
207 		/* Wait MCK ready */
208 	}
209 
210 	while (!hri_pmc_get_SR_reg(PMC, pll_sr)) {
211 		/* Waiting all restored PLLs ready */
212 	}
213 }
214 
215 /**
216  * \brief Put MCU to sleep
217  */
_go_to_sleep(void)218 void _go_to_sleep(void)
219 {
220 	bool irq_enabled = (__get_PRIMASK() == 0);
221 	if (_sleep_mode == 1 || _sleep_mode == 2) {
222 		__disable_irq();
223 		_save_clocks();
224 		__enable_irq();
225 
226 		/* Flash in wait mode: PMC_FSMR_FLPM_FLASH_STANDBY/_DEEP_POWERDOWN */
227 		hri_pmc_write_FSMR_FLPM_bf(PMC, _sleep_mode - 1);
228 		/* Set the WAITMODE bit = 1 */
229 		__DSB();
230 		hri_pmc_set_CKGR_MOR_reg(PMC, CKGR_MOR_KEY_PASSWD | CKGR_MOR_WAITMODE);
231 		while (!hri_pmc_get_SR_reg(PMC, PMC_SR_MCKRDY)) {
232 			/* Waiting for Master Clock Ready MCKRDY = 1 */
233 		}
234 		/* Waiting for MOSCRCEN bit cleared is strongly recommended
235 		 * to ensure that the core will not execute undesired instructions
236 		 */
237 		while (!hri_pmc_get_CKGR_MOR_reg(PMC, CKGR_MOR_MOSCRCEN)) {
238 			/* Waiting for MOSCRCEN */
239 		}
240 		/* Flash in idle mode: PMC_FSMR_FLPM_FLASH_IDLE */
241 		hri_pmc_write_FSMR_FLPM_bf(PMC, 2);
242 
243 		__disable_irq();
244 		_restore_clocks();
245 		if (irq_enabled) {
246 			__enable_irq();
247 		}
248 	} else {
249 		__DSB();
250 		__WFI();
251 	}
252 }
253