| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <cpu/barrier.h> |
| #include <plat/cmsis.h> |
| #include <plat/pwr.h> |
| #include <plat/rtc.h> |
| #include <reset.h> |
| #include <stddef.h> |
| |
| struct StmRcc { |
| volatile uint32_t CR; |
| volatile uint32_t PLLCFGR; |
| volatile uint32_t CFGR; |
| volatile uint32_t CIR; |
| volatile uint32_t AHB1RSTR; |
| volatile uint32_t AHB2RSTR; |
| volatile uint32_t AHB3RSTR; |
| uint8_t unused0[4]; |
| volatile uint32_t APB1RSTR; |
| volatile uint32_t APB2RSTR; |
| uint8_t unused1[8]; |
| volatile uint32_t AHB1ENR; |
| volatile uint32_t AHB2ENR; |
| volatile uint32_t AHB3ENR; |
| uint8_t unused2[4]; |
| volatile uint32_t APB1ENR; |
| volatile uint32_t APB2ENR; |
| uint8_t unused3[8]; |
| volatile uint32_t AHB1LPENR; |
| volatile uint32_t AHB2LPENR; |
| volatile uint32_t AHB3LPENR; |
| uint8_t unused4[4]; |
| volatile uint32_t APB1LPENR; |
| volatile uint32_t APB2LPENR; |
| uint8_t unused5[8]; |
| volatile uint32_t BDCR; |
| volatile uint32_t CSR; |
| uint8_t unused6[8]; |
| volatile uint32_t SSCGR; |
| volatile uint32_t PLLI2SCFGR; |
| }; |
| |
| struct StmPwr { |
| volatile uint32_t CR; |
| volatile uint32_t CSR; |
| }; |
| |
| #define RCC ((struct StmRcc*)RCC_BASE) |
| #define PWR ((struct StmPwr*)PWR_BASE) |
| |
| /* RCC bit definitions */ |
| #define RCC_BDCR_LSEON 0x00000001UL |
| #define RCC_BDCR_LSERDY 0x00000002UL |
| #define RCC_BDCR_LSEBYP 0x00000004UL |
| #define RCC_BDCR_LSEMOD 0x00000008UL |
| #define RCC_BDCR_RTCSEL_LSE 0x00000100UL |
| #define RCC_BDCR_RTCSEL_LSI 0x00000200UL |
| #define RCC_BDCR_RTCEN 0x00008000UL |
| #define RCC_BDCR_BDRST 0x00010000UL |
| |
| #define RCC_CSR_LSION 0x00000001UL |
| #define RCC_CSR_LSIRDY 0x00000002UL |
| #define RCC_CSR_RMVF 0x01000000UL |
| #define RCC_CSR_BORRSTF 0x02000000UL |
| #define RCC_CSR_PINRSTF 0x04000000UL |
| #define RCC_CSR_PORRSTF 0x08000000UL |
| #define RCC_CSR_SFTRSTF 0x10000000UL |
| #define RCC_CSR_IWDGRSTF 0x20000000UL |
| #define RCC_CSR_WWDGRSTF 0x40000000UL |
| #define RCC_CSR_LPWRRSTF 0x80000000UL |
| |
| /* PWR bit definitions */ |
| #define PWR_CR_MRVLDS 0x00000800UL |
| #define PWR_CR_LPLVDS 0x00000400UL |
| #define PWR_CR_FPDS 0x00000200UL |
| #define PWR_CR_DBP 0x00000100UL |
| #define PWR_CR_PDDS 0x00000002UL |
| #define PWR_CR_LPDS 0x00000001UL |
| |
| |
| static uint32_t mResetReason; |
| static uint32_t mSysClk = 16000000UL; |
| |
| #define RCC_REG(_bus, _type) ({ \ |
| static const uint32_t clockRegOfsts[] = { \ |
| offsetof(struct StmRcc, AHB1##_type), \ |
| offsetof(struct StmRcc, AHB2##_type), \ |
| offsetof(struct StmRcc, AHB3##_type), \ |
| offsetof(struct StmRcc, APB1##_type), \ |
| offsetof(struct StmRcc, APB2##_type) \ |
| }; /* indexed by PERIPH_BUS_* */ \ |
| (volatile uint32_t *)(RCC_BASE + clockRegOfsts[_bus]); \ |
| }) \ |
| |
| void pwrUnitClock(uint32_t bus, uint32_t unit, bool on) |
| { |
| volatile uint32_t *reg = RCC_REG(bus, ENR); |
| |
| if (on) |
| *reg |= unit; |
| else |
| *reg &=~ unit; |
| } |
| |
| void pwrUnitReset(uint32_t bus, uint32_t unit, bool on) |
| { |
| volatile uint32_t *reg = RCC_REG(bus, RSTR); |
| |
| if (on) |
| *reg |= unit; |
| else |
| *reg &=~ unit; |
| } |
| |
| uint32_t pwrGetBusSpeed(uint32_t bus) |
| { |
| uint32_t cfg = RCC->CFGR; |
| uint32_t ahbDiv, apb1Div, apb2Div; |
| uint32_t ahbSpeed, apb1Speed, apb2Speed; |
| static const uint8_t ahbSpeedShifts[] = {1, 2, 3, 4, 6, 7, 8, 9}; |
| |
| ahbDiv = (cfg >> 4) & 0x0F; |
| apb1Div = (cfg >> 10) & 0x07; |
| apb2Div = (cfg >> 13) & 0x07; |
| |
| ahbSpeed = (ahbDiv & 0x08) ? (mSysClk >> ahbSpeedShifts[ahbDiv & 0x07]) : mSysClk; |
| apb1Speed = (apb1Div & 0x04) ? (ahbSpeed >> ((apb1Div & 0x03) + 1)) : ahbSpeed; |
| apb2Speed = (apb2Div & 0x04) ? (ahbSpeed >> ((apb2Div & 0x03) + 1)) : ahbSpeed; |
| |
| if (bus == PERIPH_BUS_AHB1 || bus == PERIPH_BUS_AHB2 || bus == PERIPH_BUS_AHB3) |
| return ahbSpeed; |
| |
| if (bus == PERIPH_BUS_APB1) |
| return apb1Speed; |
| |
| if (bus == PERIPH_BUS_APB2) |
| return apb2Speed; |
| |
| /* WTF...? */ |
| return 0; |
| } |
| |
| static uint32_t pwrParseCsr(uint32_t csr) |
| { |
| uint32_t reason = 0; |
| |
| if (csr & RCC_CSR_LPWRRSTF) |
| reason |= RESET_POWER_MANAGEMENT; |
| if (csr & RCC_CSR_WWDGRSTF) |
| reason |= RESET_WINDOW_WATCHDOG; |
| if (csr & RCC_CSR_IWDGRSTF) |
| reason |= RESET_INDEPENDENT_WATCHDOG; |
| if (csr & RCC_CSR_SFTRSTF) |
| reason |= RESET_SOFTWARE; |
| if (csr & RCC_CSR_PORRSTF) |
| reason |= RESET_POWER_ON; |
| if (csr & RCC_CSR_PINRSTF) |
| reason |= RESET_HARDWARE; |
| if (csr & RCC_CSR_BORRSTF) |
| reason |= RESET_BROWN_OUT; |
| |
| return reason; |
| } |
| |
| void pwrEnableAndClockRtc(enum RtcClock rtcClock) |
| { |
| uint32_t backupRegs[RTC_NUM_BACKUP_REGS], i, *regs = rtcGetBackupStorage(); |
| |
| /* Enable power clock */ |
| pwrUnitClock(PERIPH_BUS_APB1, PERIPH_APB1_PWR, true); |
| |
| /* Enable write permission for backup domain */ |
| pwrEnableWriteBackupDomainRegs(); |
| /* Prevent compiler reordering across this boundary. */ |
| mem_reorder_barrier(); |
| |
| /* backup the backup regs (they have valuable data we want to persist) */ |
| for (i = 0; i < RTC_NUM_BACKUP_REGS; i++) |
| backupRegs[i] = regs[i]; |
| |
| /* save and reset reset flags */ |
| mResetReason = pwrParseCsr(RCC->CSR); |
| RCC->CSR |= RCC_CSR_RMVF; |
| |
| /* Reset backup domain */ |
| RCC->BDCR |= RCC_BDCR_BDRST; |
| /* Exit reset of backup domain */ |
| RCC->BDCR &= ~RCC_BDCR_BDRST; |
| |
| /* restore the backup regs */ |
| for (i = 0; i < RTC_NUM_BACKUP_REGS; i++) |
| regs[i] = backupRegs[i]; |
| |
| if (rtcClock == RTC_CLK_LSE || rtcClock == RTC_CLK_LSE_BYPASS) { |
| /* Disable LSI */ |
| RCC->CSR &= ~RCC_CSR_LSION; |
| if (rtcClock == RTC_CLK_LSE) { |
| /* Set LSE as backup domain clock source */ |
| RCC->BDCR |= RCC_BDCR_LSEON; |
| } else { |
| /* Set LSE as backup domain clock source and enable bypass */ |
| RCC->BDCR |= RCC_BDCR_LSEON | RCC_BDCR_LSEBYP; |
| } |
| /* Wait for LSE to be ready */ |
| while ((RCC->BDCR & RCC_BDCR_LSERDY) == 0); |
| /* Set LSE as RTC clock source */ |
| RCC->BDCR |= RCC_BDCR_RTCSEL_LSE; |
| } else { |
| /* Enable LSI */ |
| RCC->CSR |= RCC_CSR_LSION; |
| /* Wait for LSI to be ready */ |
| while ((RCC->CSR & RCC_CSR_LSIRDY) == 0); |
| /* Set LSI as RTC clock source */ |
| RCC->BDCR |= RCC_BDCR_RTCSEL_LSI; |
| } |
| /* Enable RTC */ |
| RCC->BDCR |= RCC_BDCR_RTCEN; |
| } |
| |
| void pwrEnableWriteBackupDomainRegs(void) |
| { |
| PWR->CR |= PWR_CR_DBP; |
| } |
| |
| void pwrSetSleepType(enum Stm32F4xxSleepType sleepType) |
| { |
| uint32_t cr = PWR->CR &~ (PWR_CR_MRVLDS | PWR_CR_LPLVDS | PWR_CR_FPDS | PWR_CR_PDDS | PWR_CR_LPDS); |
| |
| switch (sleepType) { |
| case stm32f411SleepModeSleep: |
| SCB->SCR &=~ SCB_SCR_SLEEPDEEP_Msk; |
| break; |
| case stm32f411SleepModeStopMR: |
| SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; |
| break; |
| case stm32f411SleepModeStopMRFPD: |
| SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; |
| cr |= PWR_CR_FPDS; |
| break; |
| case stm32f411SleepModeStopLPFD: |
| SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; |
| cr |= PWR_CR_FPDS | PWR_CR_LPDS; |
| break; |
| case stm32f411SleepModeStopLPLV: |
| SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; |
| cr |= PWR_CR_LPLVDS | PWR_CR_LPDS; |
| break; |
| } |
| |
| PWR->CR = cr; |
| } |
| |
| void pwrSystemInit(void) |
| { |
| RCC->CR |= 1; //HSI on |
| while (!(RCC->CR & 2)); //wait for HSI |
| RCC->CFGR = 0x00000000; //all busses at HSI speed |
| RCC->CR &= 0x0000FFF1; //HSI on, all else off |
| } |
| |
| uint32_t pwrResetReason(void) |
| { |
| return mResetReason; |
| } |