1 /******************************************************************************
2 *
3 * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * Neither the name of Texas Instruments Incorporated nor the names of
18 * its contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 ******************************************************************************/
34
35 #include <stdint.h>
36 #include <stdbool.h>
37 #include "driverlib.h"
38
39 /*
40 * Wrapper function for the CPSID instruction.
41 * Returns the state of PRIMASK on entry.
42 */
cpu_cpsid(void)43 uint32_t __attribute__((naked)) cpu_cpsid(void)
44 {
45 uint32_t ret;
46
47 /* Read PRIMASK and disable interrupts. */
48 __asm(" mrs r0, PRIMASK\n"
49 " cpsid i\n"
50 " bx lr\n"
51 : "=r" (ret));
52
53 /*
54 * The return is handled in the inline assembly, but the compiler will
55 * still complain if there is not an explicit return here (despite the fact
56 * that this does not result in any code being produced because of the
57 * naked attribute).
58 */
59 return ret;
60 }
61
62 /* Wrapper function for the CPUWFI instruction. */
cpu_wfi(void)63 void __attribute__((naked)) cpu_wfi(void)
64 {
65 /* Wait for the next interrupt. */
66 __asm(" wfi\n"
67 " bx lr\n");
68 }
69
70 /* Power Control Module APIs */
71 #if defined(PCM)
72
__pcm_set_core_voltage_level_advanced(uint_fast8_t voltage_level,uint32_t time_out,bool blocking)73 static bool __pcm_set_core_voltage_level_advanced(uint_fast8_t voltage_level,
74 uint32_t time_out, bool blocking)
75 {
76 uint8_t power_mode;
77 uint8_t current_voltage_level;
78 uint32_t reg_value;
79 bool bool_timeout;
80
81 /* Getting current power mode and level */
82 power_mode = pcm_get_power_mode();
83 current_voltage_level = pcm_get_core_voltage_level();
84
85 bool_timeout = time_out > 0 ? true : false;
86
87 /* If we are already at the power mode they requested, return */
88 if (current_voltage_level == voltage_level)
89 return true;
90
91 while (current_voltage_level != voltage_level) {
92
93 reg_value = PCM->CTL0;
94
95 switch (pcm_get_power_state()) {
96 case PCM_AM_LF_VCORE1:
97 case PCM_AM_DCDC_VCORE1:
98 case PCM_AM_LDO_VCORE0:
99 PCM->CTL0 = (PCM_KEY | (PCM_AM_LDO_VCORE1)
100 | (reg_value & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK)));
101 break;
102 case PCM_AM_LF_VCORE0:
103 case PCM_AM_DCDC_VCORE0:
104 case PCM_AM_LDO_VCORE1:
105 PCM->CTL0 = (PCM_KEY | (PCM_AM_LDO_VCORE0)
106 | (reg_value & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK)));
107 break;
108 default:
109 break;
110 }
111
112 if (blocking) {
113 while (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) {
114 if (bool_timeout && !(--time_out))
115 return false;
116 }
117 } else
118 return true;
119
120 current_voltage_level = pcm_get_core_voltage_level();
121 }
122
123 /* Changing the power mode if we are stuck in LDO mode */
124 if (power_mode != pcm_get_power_mode()) {
125 if (power_mode == PCM_DCDC_MODE)
126 return pcm_set_power_mode(PCM_DCDC_MODE);
127 else
128 return pcm_set_power_mode(PCM_LF_MODE);
129 }
130
131 return true;
132 }
133
pcm_set_core_voltage_level(uint_fast8_t voltage_level)134 bool pcm_set_core_voltage_level(uint_fast8_t voltage_level)
135 {
136 return __pcm_set_core_voltage_level_advanced(voltage_level, 0, true);
137 }
138
pcm_get_power_mode(void)139 uint8_t pcm_get_power_mode(void)
140 {
141 uint8_t current_power_state;
142
143 current_power_state = pcm_get_power_state();
144
145 switch (current_power_state) {
146 case PCM_AM_LDO_VCORE0:
147 case PCM_AM_LDO_VCORE1:
148 case PCM_LPM0_LDO_VCORE0:
149 case PCM_LPM0_LDO_VCORE1:
150 default:
151 return PCM_LDO_MODE;
152 case PCM_AM_DCDC_VCORE0:
153 case PCM_AM_DCDC_VCORE1:
154 case PCM_LPM0_DCDC_VCORE0:
155 case PCM_LPM0_DCDC_VCORE1:
156 return PCM_DCDC_MODE;
157 case PCM_LPM0_LF_VCORE0:
158 case PCM_LPM0_LF_VCORE1:
159 case PCM_AM_LF_VCORE1:
160 case PCM_AM_LF_VCORE0:
161 return PCM_LF_MODE;
162 }
163 }
164
pcm_get_core_voltage_level(void)165 uint8_t pcm_get_core_voltage_level(void)
166 {
167 uint8_t current_power_state = pcm_get_power_state();
168
169 switch (current_power_state) {
170 case PCM_AM_LDO_VCORE0:
171 case PCM_AM_DCDC_VCORE0:
172 case PCM_AM_LF_VCORE0:
173 case PCM_LPM0_LDO_VCORE0:
174 case PCM_LPM0_DCDC_VCORE0:
175 case PCM_LPM0_LF_VCORE0:
176 default:
177 return PCM_VCORE0;
178 case PCM_AM_LDO_VCORE1:
179 case PCM_AM_DCDC_VCORE1:
180 case PCM_AM_LF_VCORE1:
181 case PCM_LPM0_LDO_VCORE1:
182 case PCM_LPM0_DCDC_VCORE1:
183 case PCM_LPM0_LF_VCORE1:
184 return PCM_VCORE1;
185 case PCM_LPM3:
186 return PCM_VCORELPM3;
187 }
188 }
189
__pcm_set_power_mode_advanced(uint_fast8_t power_mode,uint32_t time_out,bool blocking)190 static bool __pcm_set_power_mode_advanced(uint_fast8_t power_mode,
191 uint32_t time_out, bool blocking)
192 {
193 uint8_t current_power_mode;
194 uint8_t current_power_state;
195 uint32_t reg_value;
196 bool bool_timeout;
197
198 /* Getting Current Power Mode */
199 current_power_mode = pcm_get_power_mode();
200
201 /* If the power mode being set it the same as the current mode, return */
202 if (power_mode == current_power_mode)
203 return true;
204
205 current_power_state = pcm_get_power_state();
206
207 bool_timeout = time_out > 0 ? true : false;
208
209 /* Go through the while loop while we haven't achieved the power mode */
210 while (current_power_mode != power_mode) {
211
212 reg_value = PCM->CTL0;
213
214 switch (current_power_state) {
215 case PCM_AM_DCDC_VCORE0:
216 case PCM_AM_LF_VCORE0:
217 PCM->CTL0 = (PCM_KEY | PCM_AM_LDO_VCORE0
218 | (reg_value & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK)));
219 break;
220 case PCM_AM_LF_VCORE1:
221 case PCM_AM_DCDC_VCORE1:
222 PCM->CTL0 = (PCM_KEY | PCM_AM_LDO_VCORE1
223 | (reg_value & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK)));
224 break;
225 case PCM_AM_LDO_VCORE1: {
226 if (power_mode == PCM_DCDC_MODE) {
227 PCM->CTL0 = (PCM_KEY | PCM_AM_DCDC_VCORE1
228 | (reg_value & ~(PCM_CTL0_KEY_MASK
229 | PCM_CTL0_AMR_MASK)));
230 } else if (power_mode == PCM_LF_MODE) {
231 PCM->CTL0 = (PCM_KEY | PCM_AM_LF_VCORE1
232 | (reg_value & ~(PCM_CTL0_KEY_MASK
233 | PCM_CTL0_AMR_MASK)));
234 } else
235 return false;
236 break;
237 }
238 case PCM_AM_LDO_VCORE0: {
239 if (power_mode == PCM_DCDC_MODE) {
240 PCM->CTL0 = (PCM_KEY | PCM_AM_DCDC_VCORE0
241 | (reg_value & ~(PCM_CTL0_KEY_MASK
242 | PCM_CTL0_AMR_MASK)));
243 } else if (power_mode == PCM_LF_MODE) {
244 PCM->CTL0 = (PCM_KEY | PCM_AM_LF_VCORE0
245 | (reg_value & ~(PCM_CTL0_KEY_MASK
246 | PCM_CTL0_AMR_MASK)));
247 } else
248 return false;
249 break;
250 }
251 default:
252 break;
253 }
254
255 if (blocking) {
256 while (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) {
257 if (bool_timeout && !(--time_out))
258 return false;
259 }
260 } else
261 return true;
262
263 current_power_mode = pcm_get_power_mode();
264 current_power_state = pcm_get_power_state();
265 }
266
267 return true;
268 }
269
pcm_set_power_mode(uint_fast8_t power_mode)270 bool pcm_set_power_mode(uint_fast8_t power_mode)
271 {
272 return __pcm_set_power_mode_advanced(power_mode, 0, true);
273 }
274
__pcm_set_power_state_advanced(uint_fast8_t power_state,uint32_t timeout,bool blocking)275 static bool __pcm_set_power_state_advanced(uint_fast8_t power_state,
276 uint32_t timeout, bool blocking)
277 {
278 uint8_t current_power_state;
279 current_power_state = pcm_get_power_state();
280
281 if (current_power_state == power_state)
282 return true;
283
284 switch (power_state) {
285 case PCM_AM_LDO_VCORE0:
286 return __pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
287 blocking) && __pcm_set_power_mode_advanced(PCM_LDO_MODE,
288 timeout, blocking);
289 case PCM_AM_LDO_VCORE1:
290 return __pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
291 blocking) && __pcm_set_power_mode_advanced(PCM_LDO_MODE,
292 timeout, blocking);
293 case PCM_AM_DCDC_VCORE0:
294 return __pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
295 blocking) && __pcm_set_power_mode_advanced(PCM_DCDC_MODE,
296 timeout, blocking);
297 case PCM_AM_DCDC_VCORE1:
298 return __pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
299 blocking) && __pcm_set_power_mode_advanced(PCM_DCDC_MODE,
300 timeout, blocking);
301 case PCM_AM_LF_VCORE0:
302 return __pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
303 blocking) && __pcm_set_power_mode_advanced(PCM_LF_MODE,
304 timeout, blocking);
305 case PCM_AM_LF_VCORE1:
306 return __pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
307 blocking) && __pcm_set_power_mode_advanced(PCM_LF_MODE,
308 timeout, blocking);
309 case PCM_LPM0_LDO_VCORE0:
310 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
311 blocking) || !__pcm_set_power_mode_advanced(PCM_LDO_MODE,
312 timeout, blocking))
313 break;
314 return pcm_goto_lpm0();
315 case PCM_LPM0_LDO_VCORE1:
316 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
317 blocking) || !__pcm_set_power_mode_advanced(PCM_LDO_MODE,
318 timeout, blocking))
319 break;
320 return pcm_goto_lpm0();
321 case PCM_LPM0_DCDC_VCORE0:
322 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
323 blocking) || !__pcm_set_power_mode_advanced(PCM_DCDC_MODE,
324 timeout, blocking))
325 break;
326 return pcm_goto_lpm0();
327 case PCM_LPM0_DCDC_VCORE1:
328 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
329 blocking) || !__pcm_set_power_mode_advanced(PCM_DCDC_MODE,
330 timeout, blocking))
331 break;
332 return pcm_goto_lpm0();
333 case PCM_LPM0_LF_VCORE0:
334 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE0, timeout,
335 blocking) || !__pcm_set_power_mode_advanced(PCM_LF_MODE,
336 timeout, blocking))
337 break;
338 return pcm_goto_lpm0();
339 case PCM_LPM0_LF_VCORE1:
340 if (!__pcm_set_core_voltage_level_advanced(PCM_VCORE1, timeout,
341 blocking) || !__pcm_set_power_mode_advanced(PCM_LF_MODE,
342 timeout, blocking))
343 break;
344 return pcm_goto_lpm0();
345 case PCM_LPM3:
346 return pcm_goto_lpm3();
347 case PCM_LPM4:
348 return pcm_goto_lpm4();
349 case PCM_LPM45:
350 return pcm_shutdown_device(PCM_LPM45);
351 case PCM_LPM35_VCORE0:
352 return pcm_shutdown_device(PCM_LPM35_VCORE0);
353 default:
354 return false;
355 }
356
357 return false;
358 }
359
pcm_set_power_state(uint_fast8_t power_state)360 bool pcm_set_power_state(uint_fast8_t power_state)
361 {
362 return __pcm_set_power_state_advanced(power_state, 0, true);
363 }
364
pcm_shutdown_device(uint32_t shutdown_mode)365 bool pcm_shutdown_device(uint32_t shutdown_mode)
366 {
367 uint32_t shutdown_mode_bits = (shutdown_mode == PCM_LPM45) ?
368 PCM_CTL0_LPMR_12 : PCM_CTL0_LPMR_10;
369
370 /* If a power transition is occurring, return false */
371 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS))
372 return false;
373
374 /* Initiating the shutdown */
375 SCB->SCR |= SCB_SCR_SLEEPDEEP_MSK;
376
377 PCM->CTL0 = (PCM_KEY | shutdown_mode_bits
378 | (PCM->CTL0 & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_LPMR_MASK)));
379
380 cpu_wfi();
381
382 return true;
383 }
384
pcm_goto_lpm4(void)385 bool pcm_goto_lpm4(void)
386 {
387 /* Disabling RTC_C and WDT_A */
388 wdt_a_hold_timer();
389 rtc_c_hold_clock();
390
391 /* LPM4 is just LPM3 with WDT_A/RTC_C disabled... */
392 return pcm_goto_lpm3();
393 }
394
pcm_goto_lpm0(void)395 bool pcm_goto_lpm0(void)
396 {
397 /* If we are in the middle of a state transition, return false */
398 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS))
399 return false;
400
401 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_MSK;
402
403 cpu_wfi();
404
405 return true;
406 }
407
pcm_goto_lpm3(void)408 bool pcm_goto_lpm3(void)
409 {
410 uint_fast8_t current_power_state;
411 uint_fast8_t current_power_mode;
412
413 /* If we are in the middle of a state transition, return false */
414 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS))
415 return false;
416
417 /* If we are in the middle of a shutdown, return false */
418 if ((PCM->CTL0 & PCM_CTL0_LPMR_MASK) == PCM_CTL0_LPMR_10
419 || (PCM->CTL0 & PCM_CTL0_LPMR_MASK) == PCM_CTL0_LPMR_12)
420 return false;
421
422 current_power_mode = pcm_get_power_mode();
423 current_power_state = pcm_get_power_state();
424
425 if (current_power_mode == PCM_DCDC_MODE)
426 pcm_set_power_mode(PCM_LDO_MODE);
427
428 /* Clearing the SDR */
429 PCM->CTL0 =
430 (PCM->CTL0 & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_LPMR_MASK)) | PCM_KEY;
431
432 /* Setting the sleep deep bit */
433 SCB->SCR |= SCB_SCR_SLEEPDEEP_MSK;
434
435 cpu_wfi();
436
437 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_MSK;
438
439 return pcm_set_power_state(current_power_state);
440 }
441
pcm_get_power_state(void)442 uint8_t pcm_get_power_state(void)
443 {
444 return (PCM->CTL0 & PCM_CTL0_CPM_MASK) >> PCM_CTL0_CPM_OFS;
445 }
446
447 #endif
448
449 /* Real Time Clock APIs */
450 #if defined(RTC_C)
451
rtc_c_hold_clock(void)452 void rtc_c_hold_clock(void)
453 {
454 RTC_C->CTL0 = (RTC_C->CTL0 & ~RTC_C_CTL0_KEY_MASK) | RTC_C_KEY;
455 BITBAND_PERI(RTC_C->CTL13, RTC_C_CTL13_HOLD_OFS) = 1;
456 BITBAND_PERI(RTC_C->CTL0, RTC_C_CTL0_KEY_OFS) = 0;
457 }
458
459 #endif
460
461 /* Watch Dog Timer APIs */
462 #if defined(WDT_A)
463
wdt_a_hold_timer(void)464 void wdt_a_hold_timer(void)
465 {
466 /* Set Hold bit */
467 uint8_t new_wdt_status = (WDT_A->CTL | WDT_A_CTL_HOLD);
468
469 WDT_A->CTL = WDT_A_CTL_PW + new_wdt_status;
470 }
471
472 #endif
473