flash/nor: add support for TI MSP432 devices
[openocd.git] / 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

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)