diff options
Diffstat (limited to 'firmware/os/platform/stm32/pwr.c')
-rw-r--r-- | firmware/os/platform/stm32/pwr.c | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/firmware/os/platform/stm32/pwr.c b/firmware/os/platform/stm32/pwr.c new file mode 100644 index 00000000..39510a50 --- /dev/null +++ b/firmware/os/platform/stm32/pwr.c @@ -0,0 +1,278 @@ +/* + * 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; +} |