279 lines
8.2 KiB
C
279 lines
8.2 KiB
C
/*
|
|
* 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;
|
|
}
|