1 /* --COPYRIGHT--,BSD 2 * Copyright (c) 2017, Texas Instruments Incorporated 3 * All rights reserved. 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 distribution. 15 * 16 * * Neither the name of Texas Instruments Incorporated nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 30 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 * --/COPYRIGHT--*/ 32 /* Standard Includes */ 33 #include <stdint.h> 34 35 /* DriverLib Includes */ 36 #include <ti/devices/msp432p4xx/driverlib/pcm.h> 37 #include <ti/devices/msp432p4xx/driverlib/debug.h> 38 #include <ti/devices/msp432p4xx/driverlib/interrupt.h> 39 #include <ti/devices/msp432p4xx/driverlib/wdt_a.h> 40 #include <ti/devices/msp432p4xx/driverlib/rtc_c.h> 41 #include <ti/devices/msp432p4xx/driverlib/cpu.h> 42 43 static bool __PCM_setCoreVoltageLevelAdvanced(uint_fast8_t voltageLevel, 44 uint32_t timeOut, bool blocking) 45 { 46 uint8_t powerMode, bCurrentVoltageLevel; 47 uint32_t regValue; 48 bool boolTimeout; 49 50 ASSERT(voltageLevel == PCM_VCORE0 || voltageLevel == PCM_VCORE1); 51 52 /* Getting current power mode and level */ 53 powerMode = PCM_getPowerMode(); 54 bCurrentVoltageLevel = PCM_getCoreVoltageLevel(); 55 56 boolTimeout = timeOut > 0 ? true : false; 57 58 /* If we are already at the power mode they requested, return */ 59 if (bCurrentVoltageLevel == voltageLevel) 60 return true; 61 62 while (bCurrentVoltageLevel != voltageLevel) 63 { 64 regValue = PCM->CTL0; 65 66 switch (PCM_getPowerState()) 67 { 68 case PCM_AM_LF_VCORE1: 69 case PCM_AM_DCDC_VCORE1: 70 case PCM_AM_LDO_VCORE0: 71 PCM->CTL0 = (PCM_KEY | (PCM_AM_LDO_VCORE1) 72 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 73 break; 74 case PCM_AM_LF_VCORE0: 75 case PCM_AM_DCDC_VCORE0: 76 case PCM_AM_LDO_VCORE1: 77 PCM->CTL0 = (PCM_KEY | (PCM_AM_LDO_VCORE0) 78 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 79 break; 80 default: 81 ASSERT(false); 82 } 83 84 if(blocking) 85 { 86 while (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) 87 { 88 if (boolTimeout && !(--timeOut)) 89 return false; 90 91 } 92 } 93 else 94 { 95 return true; 96 } 97 98 bCurrentVoltageLevel = PCM_getCoreVoltageLevel(); 99 } 100 101 /* Changing the power mode if we are stuck in LDO mode */ 102 if (powerMode != PCM_getPowerMode()) 103 { 104 if (powerMode == PCM_DCDC_MODE) 105 return PCM_setPowerMode(PCM_DCDC_MODE); 106 else 107 return PCM_setPowerMode(PCM_LF_MODE); 108 } 109 110 return true; 111 112 } 113 114 115 bool PCM_setCoreVoltageLevel(uint_fast8_t voltageLevel) 116 { 117 return __PCM_setCoreVoltageLevelAdvanced(voltageLevel, 0, true); 118 } 119 120 bool PCM_setCoreVoltageLevelWithTimeout(uint_fast8_t voltageLevel, 121 uint32_t timeOut) 122 { 123 return __PCM_setCoreVoltageLevelAdvanced(voltageLevel, timeOut, true); 124 } 125 126 bool PCM_setCoreVoltageLevelNonBlocking(uint_fast8_t voltageLevel) 127 { 128 return __PCM_setCoreVoltageLevelAdvanced(voltageLevel, 0, false); 129 } 130 131 uint8_t PCM_getPowerMode(void) 132 { 133 uint8_t currentPowerState; 134 135 currentPowerState = PCM_getPowerState(); 136 137 switch (currentPowerState) 138 { 139 case PCM_AM_LDO_VCORE0: 140 case PCM_AM_LDO_VCORE1: 141 case PCM_LPM0_LDO_VCORE0: 142 case PCM_LPM0_LDO_VCORE1: 143 return PCM_LDO_MODE; 144 case PCM_AM_DCDC_VCORE0: 145 case PCM_AM_DCDC_VCORE1: 146 case PCM_LPM0_DCDC_VCORE0: 147 case PCM_LPM0_DCDC_VCORE1: 148 return PCM_DCDC_MODE; 149 case PCM_LPM0_LF_VCORE0: 150 case PCM_LPM0_LF_VCORE1: 151 case PCM_AM_LF_VCORE1: 152 case PCM_AM_LF_VCORE0: 153 return PCM_LF_MODE; 154 default: 155 ASSERT(false); 156 return false; 157 158 } 159 } 160 161 uint8_t PCM_getCoreVoltageLevel(void) 162 { 163 uint8_t currentPowerState = PCM_getPowerState(); 164 165 switch (currentPowerState) 166 { 167 case PCM_AM_LDO_VCORE0: 168 case PCM_AM_DCDC_VCORE0: 169 case PCM_AM_LF_VCORE0: 170 case PCM_LPM0_LDO_VCORE0: 171 case PCM_LPM0_DCDC_VCORE0: 172 case PCM_LPM0_LF_VCORE0: 173 return PCM_VCORE0; 174 case PCM_AM_LDO_VCORE1: 175 case PCM_AM_DCDC_VCORE1: 176 case PCM_AM_LF_VCORE1: 177 case PCM_LPM0_LDO_VCORE1: 178 case PCM_LPM0_DCDC_VCORE1: 179 case PCM_LPM0_LF_VCORE1: 180 return PCM_VCORE1; 181 case PCM_LPM3: 182 return PCM_VCORELPM3; 183 default: 184 ASSERT(false); 185 return false; 186 187 } 188 } 189 190 static bool __PCM_setPowerModeAdvanced(uint_fast8_t powerMode, uint32_t timeOut, 191 bool blocking) 192 { 193 uint8_t bCurrentPowerMode, bCurrentPowerState; 194 uint32_t regValue; 195 bool boolTimeout; 196 197 ASSERT( 198 powerMode == PCM_LDO_MODE || powerMode == PCM_DCDC_MODE 199 || powerMode == PCM_LF_MODE); 200 201 /* Getting Current Power Mode */ 202 bCurrentPowerMode = PCM_getPowerMode(); 203 204 /* If the power mode being set it the same as the current mode, return */ 205 if (powerMode == bCurrentPowerMode) 206 return true; 207 208 bCurrentPowerState = PCM_getPowerState(); 209 210 boolTimeout = timeOut > 0 ? true : false; 211 212 /* Go through the while loop while we haven't achieved the power mode */ 213 while (bCurrentPowerMode != powerMode) 214 { 215 regValue = PCM->CTL0; 216 217 switch (bCurrentPowerState) 218 { 219 case PCM_AM_DCDC_VCORE0: 220 case PCM_AM_LF_VCORE0: 221 PCM->CTL0 = (PCM_KEY | PCM_AM_LDO_VCORE0 222 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 223 break; 224 case PCM_AM_LF_VCORE1: 225 case PCM_AM_DCDC_VCORE1: 226 PCM->CTL0 = (PCM_KEY | PCM_AM_LDO_VCORE1 227 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 228 break; 229 case PCM_AM_LDO_VCORE1: 230 { 231 if (powerMode == PCM_DCDC_MODE) 232 { 233 PCM->CTL0 = (PCM_KEY | PCM_AM_DCDC_VCORE1 234 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 235 } else if (powerMode == PCM_LF_MODE) 236 { 237 PCM->CTL0 = (PCM_KEY | PCM_AM_LF_VCORE1 238 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 239 } else 240 ASSERT(false); 241 242 break; 243 } 244 case PCM_AM_LDO_VCORE0: 245 { 246 if (powerMode == PCM_DCDC_MODE) 247 { 248 PCM->CTL0 = (PCM_KEY | PCM_AM_DCDC_VCORE0 249 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 250 } else if (powerMode == PCM_LF_MODE) 251 { 252 PCM->CTL0 = (PCM_KEY | PCM_AM_LF_VCORE0 253 | (regValue & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_AMR_MASK))); 254 } else 255 ASSERT(false); 256 257 break; 258 } 259 default: 260 ASSERT(false); 261 } 262 263 if (blocking) 264 { 265 while (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) 266 { 267 if (boolTimeout && !(--timeOut)) 268 return false; 269 270 } 271 } else 272 return true; 273 274 bCurrentPowerMode = PCM_getPowerMode(); 275 bCurrentPowerState = PCM_getPowerState(); 276 } 277 278 return true; 279 280 } 281 282 bool PCM_setPowerMode(uint_fast8_t powerMode) 283 { 284 return __PCM_setPowerModeAdvanced(powerMode, 0, true); 285 } 286 287 bool PCM_setPowerModeNonBlocking(uint_fast8_t powerMode) 288 { 289 return __PCM_setPowerModeAdvanced(powerMode, 0, false); 290 } 291 292 bool PCM_setPowerModeWithTimeout(uint_fast8_t powerMode, uint32_t timeOut) 293 { 294 return __PCM_setPowerModeAdvanced(powerMode, timeOut, true); 295 } 296 297 static bool __PCM_setPowerStateAdvanced(uint_fast8_t powerState, 298 uint32_t timeout, 299 bool blocking) 300 { 301 uint8_t bCurrentPowerState; 302 bCurrentPowerState = PCM_getPowerState(); 303 304 ASSERT( 305 powerState == PCM_AM_LDO_VCORE0 || powerState == PCM_AM_LDO_VCORE1 306 || powerState == PCM_AM_DCDC_VCORE0 || powerState == PCM_AM_DCDC_VCORE1 307 || powerState == PCM_AM_LF_VCORE0 || powerState == PCM_AM_LF_VCORE1 308 || powerState == PCM_LPM0_LDO_VCORE0 || powerState == PCM_LPM0_LDO_VCORE1 309 || powerState == PCM_LPM0_DCDC_VCORE0 || powerState == PCM_LPM0_DCDC_VCORE1 310 || powerState == PCM_LPM3 || powerState == PCM_LPM35_VCORE0 311 || powerState == PCM_LPM45 || powerState == PCM_LPM4); 312 313 if (bCurrentPowerState == powerState) 314 return true; 315 316 switch (powerState) 317 { 318 case PCM_AM_LDO_VCORE0: 319 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 320 && __PCM_setPowerModeAdvanced(PCM_LDO_MODE, timeout, blocking)); 321 case PCM_AM_LDO_VCORE1: 322 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 323 && __PCM_setPowerModeAdvanced(PCM_LDO_MODE, timeout, blocking)); 324 case PCM_AM_DCDC_VCORE0: 325 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 326 && __PCM_setPowerModeAdvanced(PCM_DCDC_MODE, timeout, blocking)); 327 case PCM_AM_DCDC_VCORE1: 328 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 329 && __PCM_setPowerModeAdvanced(PCM_DCDC_MODE, timeout, blocking)); 330 case PCM_AM_LF_VCORE0: 331 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 332 && __PCM_setPowerModeAdvanced(PCM_LF_MODE, timeout, blocking)); 333 case PCM_AM_LF_VCORE1: 334 return (__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 335 && __PCM_setPowerModeAdvanced(PCM_LF_MODE, timeout, blocking)); 336 case PCM_LPM0_LDO_VCORE0: 337 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 338 || !__PCM_setPowerModeAdvanced(PCM_LDO_MODE, timeout, blocking)) 339 break; 340 return PCM_gotoLPM0(); 341 case PCM_LPM0_LDO_VCORE1: 342 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 343 || !__PCM_setPowerModeAdvanced(PCM_LDO_MODE, timeout, blocking)) 344 break; 345 return PCM_gotoLPM0(); 346 case PCM_LPM0_DCDC_VCORE0: 347 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 348 || !__PCM_setPowerModeAdvanced(PCM_DCDC_MODE, timeout, 349 blocking)) 350 break; 351 return PCM_gotoLPM0(); 352 case PCM_LPM0_DCDC_VCORE1: 353 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 354 || !__PCM_setPowerModeAdvanced(PCM_DCDC_MODE, timeout, 355 blocking)) 356 break; 357 return PCM_gotoLPM0(); 358 case PCM_LPM0_LF_VCORE0: 359 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE0, timeout, blocking) 360 || !__PCM_setPowerModeAdvanced(PCM_LF_MODE, timeout, blocking)) 361 break; 362 return PCM_gotoLPM0(); 363 case PCM_LPM0_LF_VCORE1: 364 if (!__PCM_setCoreVoltageLevelAdvanced(PCM_VCORE1, timeout, blocking) 365 || !__PCM_setPowerModeAdvanced(PCM_LF_MODE, timeout, blocking)) 366 break; 367 return PCM_gotoLPM0(); 368 case PCM_LPM3: 369 return PCM_gotoLPM3(); 370 case PCM_LPM4: 371 return PCM_gotoLPM4(); 372 case PCM_LPM45: 373 return PCM_shutdownDevice(PCM_LPM45); 374 case PCM_LPM35_VCORE0: 375 return PCM_shutdownDevice(PCM_LPM35_VCORE0); 376 default: 377 ASSERT(false); 378 return false; 379 } 380 381 return false; 382 383 } 384 385 bool PCM_setPowerState(uint_fast8_t powerState) 386 { 387 return __PCM_setPowerStateAdvanced(powerState, 0, true); 388 } 389 390 bool PCM_setPowerStateWithTimeout(uint_fast8_t powerState, uint32_t timeout) 391 { 392 return __PCM_setPowerStateAdvanced(powerState, timeout, true); 393 } 394 395 bool PCM_setPowerStateNonBlocking(uint_fast8_t powerState) 396 { 397 return __PCM_setPowerStateAdvanced(powerState, 0, false); 398 } 399 400 bool PCM_shutdownDevice(uint32_t shutdownMode) 401 { 402 uint32_t shutdownModeBits = (shutdownMode == PCM_LPM45) ? 403 PCM_CTL0_LPMR_12 : PCM_CTL0_LPMR_10; 404 405 ASSERT( 406 shutdownMode == PCM_SHUTDOWN_PARTIAL 407 || shutdownMode == PCM_SHUTDOWN_COMPLETE); 408 409 /* If a power transition is occuring, return false */ 410 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) 411 return false; 412 413 /* Initiating the shutdown */ 414 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; 415 416 PCM->CTL0 = (PCM_KEY | shutdownModeBits 417 | (PCM->CTL0 & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_LPMR_MASK))); 418 419 CPU_wfi(); 420 421 return true; 422 } 423 424 bool PCM_gotoLPM4(void) 425 { 426 /* Disabling RTC_C and WDT_A */ 427 WDT_A_holdTimer(); 428 RTC_C_holdClock(); 429 430 /* LPM4 is just LPM3 with WDT_A/RTC_C disabled... */ 431 return PCM_gotoLPM3(); 432 } 433 434 bool PCM_gotoLPM4InterruptSafe(void) 435 { 436 bool slHappenedCorrect; 437 438 /* Disabling master interrupts. In Cortex M, if an interrupt is enabled but 439 master interrupts are disabled and a WFI happens the WFI will 440 immediately exit. */ 441 Interrupt_disableMaster(); 442 443 slHappenedCorrect = PCM_gotoLPM4(); 444 445 /* Enabling and Disabling Interrupts very quickly so that the 446 processor catches any pending interrupts */ 447 Interrupt_enableMaster(); 448 Interrupt_disableMaster(); 449 450 return slHappenedCorrect; 451 } 452 453 bool PCM_gotoLPM0(void) 454 { 455 /* If we are in the middle of a state transition, return false */ 456 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) 457 return false; 458 459 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; 460 461 CPU_wfi(); 462 463 return true; 464 } 465 466 bool PCM_gotoLPM0InterruptSafe(void) 467 { 468 bool slHappenedCorrect; 469 470 /* Disabling master interrupts. In Cortex M, if an interrupt is enabled but 471 master interrupts are disabled and a WFI happens the WFI will 472 immediately exit. */ 473 Interrupt_disableMaster(); 474 475 slHappenedCorrect = PCM_gotoLPM0(); 476 477 /* Enabling and Disabling Interrupts very quickly so that the 478 processor catches any pending interrupts */ 479 Interrupt_enableMaster(); 480 Interrupt_disableMaster(); 481 482 return slHappenedCorrect; 483 } 484 485 bool PCM_gotoLPM3(void) 486 { 487 uint_fast8_t bCurrentPowerState; 488 uint_fast8_t currentPowerMode; 489 490 /* If we are in the middle of a state transition, return false */ 491 if (BITBAND_PERI(PCM->CTL1, PCM_CTL1_PMR_BUSY_OFS)) 492 return false; 493 494 /* If we are in the middle of a shutdown, return false */ 495 if ((PCM->CTL0 & PCM_CTL0_LPMR_MASK) == PCM_CTL0_LPMR_10 496 || (PCM->CTL0 & PCM_CTL0_LPMR_MASK) == PCM_CTL0_LPMR_12) 497 return false; 498 499 currentPowerMode = PCM_getPowerMode(); 500 bCurrentPowerState = PCM_getPowerState(); 501 502 if (currentPowerMode == PCM_DCDC_MODE) 503 PCM_setPowerMode(PCM_LDO_MODE); 504 505 /* Clearing the SDR */ 506 PCM->CTL0 = (PCM->CTL0 & ~(PCM_CTL0_KEY_MASK | PCM_CTL0_LPMR_MASK)) | PCM_KEY; 507 508 /* Setting the sleep deep bit */ 509 SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; 510 511 CPU_wfi(); 512 513 SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; 514 515 return PCM_setPowerState(bCurrentPowerState); 516 } 517 518 bool PCM_gotoLPM3InterruptSafe(void) 519 { 520 bool lpmHappenedCorrect; 521 522 /* Disabling master interrupts. In Cortex M, if an interrupt is enabled but 523 master interrupts are disabled and a WFI happens the WFI will 524 immediately exit. */ 525 Interrupt_disableMaster(); 526 527 lpmHappenedCorrect = PCM_gotoLPM3(); 528 529 /* Enabling and Disabling Interrupts very quickly so that the 530 processor catches any pending interrupts */ 531 Interrupt_enableMaster(); 532 Interrupt_disableMaster(); 533 534 return lpmHappenedCorrect; 535 } 536 537 uint8_t PCM_getPowerState(void) 538 { 539 return (PCM->CTL0 & PCM_CTL0_CPM_MASK) >> PCM_CTL0_CPM_OFS; 540 } 541 542 void PCM_enableRudeMode(void) 543 { 544 545 PCM->CTL1 = (PCM->CTL1 & ~(PCM_CTL0_KEY_MASK)) | PCM_KEY 546 | PCM_CTL1_FORCE_LPM_ENTRY; 547 } 548 549 void PCM_disableRudeMode(void) 550 { 551 PCM->CTL1 = (PCM->CTL1 & ~(PCM_CTL0_KEY_MASK | PCM_CTL1_FORCE_LPM_ENTRY)) 552 | PCM_KEY; 553 } 554 555 void PCM_enableInterrupt(uint32_t flags) 556 { 557 PCM->IE |= flags; 558 } 559 560 void PCM_disableInterrupt(uint32_t flags) 561 { 562 PCM->IE &= ~flags; 563 } 564 565 uint32_t PCM_getInterruptStatus(void) 566 { 567 return PCM->IFG; 568 } 569 570 uint32_t PCM_getEnabledInterruptStatus(void) 571 { 572 return PCM_getInterruptStatus() & PCM->IE; 573 } 574 575 void PCM_clearInterruptFlag(uint32_t flags) 576 { 577 PCM->CLRIFG |= flags; 578 } 579 580 void PCM_registerInterrupt(void (*intHandler)(void)) 581 { 582 // 583 // Register the interrupt handler, returning an error if an error occurs. 584 // 585 Interrupt_registerInterrupt(INT_PCM, intHandler); 586 587 // 588 // Enable the system control interrupt. 589 // 590 Interrupt_enableInterrupt(INT_PCM); 591 } 592 593 void PCM_unregisterInterrupt(void) 594 { 595 // 596 // Disable the interrupt. 597 // 598 Interrupt_disableInterrupt(INT_PCM); 599 600 // 601 // Unregister the interrupt handler. 602 // 603 Interrupt_unregisterInterrupt(INT_PCM); 604 } 605