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