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