flash/nor: add support for TI MSP432 devices
[fw/openocd] / contrib / loaders / flash / msp432 / driverlib.c
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  */
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. */
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
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
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
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
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
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
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
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
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
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 occuring, 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
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
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
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
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
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
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