blob: d17b9115e8e81e4d31146416e69da37c65f3323c [file] [log] [blame]
/* Copyright 2012 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* System module for Chrome EC : hardware specific implementation */
#include "bkpdata.h"
#include "clock.h"
#include "console.h"
#include "cpu.h"
#include "cros_version.h"
#include "flash.h"
#include "gpio_chip.h"
#include "hooks.h"
#include "host_command.h"
#include "panic.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "util.h"
#include "watchdog.h"
#ifdef CONFIG_STM32_CLOCK_LSE
#define BDCR_SRC BDCR_SRC_LSE
#define BDCR_RDY STM32_RCC_BDCR_LSERDY
#else
#define BDCR_SRC BDCR_SRC_LSI
#define BDCR_RDY 0
#endif
#define BDCR_ENABLE_VALUE \
(STM32_RCC_BDCR_RTCEN | BDCR_RTCSEL(BDCR_SRC) | BDCR_RDY)
#define BDCR_ENABLE_MASK \
(BDCR_ENABLE_VALUE | BDCR_RTCSEL_MASK | STM32_RCC_BDCR_BDRST)
#ifdef CONFIG_USB_PD_DUAL_ROLE
BUILD_ASSERT(CONFIG_USB_PD_PORT_MAX_COUNT <= 3);
#endif
void __no_hibernate(uint32_t seconds, uint32_t microseconds)
{
#ifdef CONFIG_COMMON_RUNTIME
/*
* Hibernate not implemented on this platform.
*
* Until then, treat this as a request to hard-reboot.
*/
cprints(CC_SYSTEM, "hibernate not supported, so rebooting");
cflush();
system_reset(SYSTEM_RESET_HARD);
#endif
}
void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
__attribute__((weak, alias("__no_hibernate")));
void system_hibernate(uint32_t seconds, uint32_t microseconds)
{
#ifdef CONFIG_HOSTCMD_PD
/* Inform the PD MCU that we are going to hibernate. */
host_command_pd_request_hibernate();
/* Wait to ensure exchange with PD before hibernating. */
crec_msleep(100);
#endif
/* Flush console before hibernating */
cflush();
if (board_hibernate)
board_hibernate();
/* chip specific standby mode */
__enter_hibernate(seconds, microseconds);
}
uint32_t chip_read_reset_flags(void)
{
return bkpdata_read_reset_flags();
}
void chip_save_reset_flags(uint32_t flags)
{
bkpdata_write_reset_flags(flags);
}
static void check_reset_cause(void)
{
uint32_t flags = chip_read_reset_flags();
uint32_t raw_cause = STM32_RCC_RESET_CAUSE;
#ifdef STM32_PWR_RESET_CAUSE
uint32_t pwr_status = STM32_PWR_RESET_CAUSE;
#endif
/* Clear the hardware reset cause by setting the RMVF bit */
STM32_RCC_RESET_CAUSE |= RESET_CAUSE_RMVF;
#ifdef STM32_PWR_RESET_CAUSE
/* Clear SBF in PWR_CSR */
STM32_PWR_RESET_CAUSE_CLR |= RESET_CAUSE_SBF_CLR;
#endif
/* Clear saved reset flags */
chip_save_reset_flags(0);
if (raw_cause & RESET_CAUSE_WDG) {
/*
* IWDG or WWDG, if the watchdog was not used as an hard reset
* mechanism
*/
if (!(flags & EC_RESET_FLAG_HARD))
flags |= EC_RESET_FLAG_WATCHDOG;
}
if (raw_cause & RESET_CAUSE_SFT)
flags |= EC_RESET_FLAG_SOFT;
if (raw_cause & RESET_CAUSE_POR)
flags |= EC_RESET_FLAG_POWER_ON;
if (raw_cause & RESET_CAUSE_PIN)
flags |= EC_RESET_FLAG_RESET_PIN;
#ifdef STM32_PWR_RESET_CAUSE
if (pwr_status & RESET_CAUSE_SBF)
/* Hibernated and subsequently awakened */
flags |= EC_RESET_FLAG_HIBERNATE;
#endif
if (!flags && (raw_cause & RESET_CAUSE_OTHER))
flags |= EC_RESET_FLAG_OTHER;
/*
* WORKAROUND: as we cannot de-activate the watchdog during
* long hibernation, we are woken-up once by the watchdog and
* go back to hibernate if we detect that condition, without
* watchdog initialized this time.
* The RTC deadline (if any) is already set.
*/
if ((flags & EC_RESET_FLAG_HIBERNATE) &&
(flags & EC_RESET_FLAG_WATCHDOG)) {
__enter_hibernate(0, 0);
}
system_set_reset_flags(flags);
}
/* Stop all timers and WDGs we might use when JTAG stops the CPU. */
void chip_pre_init(void)
{
uint32_t apb1fz_reg = 0;
uint32_t apb2fz_reg = 0;
#if defined(CHIP_FAMILY_STM32F0)
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_WWDG | STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM15 | STM32_RCC_PB2_TIM16 |
STM32_RCC_PB2_TIM17 | STM32_RCC_PB2_TIM1;
/* enable clock to debug module before writing */
STM32_RCC_APB2ENR |= STM32_RCC_DBGMCUEN;
#elif defined(CHIP_FAMILY_STM32F3)
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM4 | STM32_RCC_PB1_TIM5 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_WWDG | STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM15 | STM32_RCC_PB2_TIM16 |
STM32_RCC_PB2_TIM17;
#elif defined(CHIP_FAMILY_STM32F4)
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM4 | STM32_RCC_PB1_TIM5 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_TIM12 | STM32_RCC_PB1_TIM13 |
STM32_RCC_PB1_TIM14 | STM32_RCC_PB1_RTC |
STM32_RCC_PB1_WWDG | STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM1 | STM32_RCC_PB2_TIM8 |
STM32_RCC_PB2_TIM9 | STM32_RCC_PB2_TIM10 |
STM32_RCC_PB2_TIM11;
#elif defined(CHIP_FAMILY_STM32L4)
#ifdef CHIP_VARIANT_STM32L431X
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_WWDG |
STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM1 | STM32_RCC_PB2_TIM15 |
STM32_RCC_PB2_TIM16;
#else
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM4 | STM32_RCC_PB1_TIM5 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_WWDG | STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM1 | STM32_RCC_PB2_TIM8;
#endif
#elif defined(CHIP_FAMILY_STM32L5)
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM4 | STM32_RCC_PB1_TIM5 |
STM32_RCC_PB1_TIM6 | STM32_RCC_PB1_TIM7 |
STM32_RCC_PB1_WWDG | STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM1 | STM32_RCC_PB2_TIM8 |
STM32_RCC_PB2_TIM15 | STM32_RCC_PB2_TIM16 |
STM32_RCC_PB2_TIM17;
#elif defined(CHIP_FAMILY_STM32L)
apb1fz_reg = STM32_RCC_PB1_TIM2 | STM32_RCC_PB1_TIM3 |
STM32_RCC_PB1_TIM4 | STM32_RCC_PB1_WWDG |
STM32_RCC_PB1_IWDG;
apb2fz_reg = STM32_RCC_PB2_TIM9 | STM32_RCC_PB2_TIM10 |
STM32_RCC_PB2_TIM11;
#elif defined(CHIP_FAMILY_STM32G4)
apb1fz_reg = STM32_DBGMCU_APB1FZ_TIM2 | STM32_DBGMCU_APB1FZ_TIM3 |
STM32_DBGMCU_APB1FZ_TIM4 | STM32_DBGMCU_APB1FZ_TIM5 |
STM32_DBGMCU_APB1FZ_TIM6 | STM32_DBGMCU_APB1FZ_TIM7 |
STM32_DBGMCU_APB1FZ_RTC | STM32_DBGMCU_APB1FZ_WWDG |
STM32_DBGMCU_APB1FZ_IWDG;
apb2fz_reg = STM32_DBGMCU_APB2FZ_TIM1 | STM32_DBGMCU_APB2FZ_TIM8 |
STM32_DBGMCU_APB2FZ_TIM15 | STM32_DBGMCU_APB2FZ_TIM16 |
STM32_DBGMCU_APB2FZ_TIM17 | STM32_DBGMCU_APB2FZ_TIM20;
#elif defined(CHIP_FAMILY_STM32H7)
/* TODO(b/67081508) */
#endif
if (apb1fz_reg)
STM32_DBGMCU_APB1FZ |= apb1fz_reg;
if (apb2fz_reg)
STM32_DBGMCU_APB2FZ |= apb2fz_reg;
}
#ifdef CONFIG_PVD
/******************************************************************************
* Detects sagging Vdd voltage and resets the system via the programmable
* voltage detector interrupt.
*/
static void configure_pvd(void)
{
/* Clear Interrupt Enable Mask Register. */
STM32_EXTI_IMR &= ~EXTI_PVD_EVENT;
/* Clear Rising and Falling Trigger Selection Registers. */
STM32_EXTI_RTSR &= ~EXTI_PVD_EVENT;
STM32_EXTI_FTSR &= ~EXTI_PVD_EVENT;
/* Clear the value of the PVD Level Selection. */
STM32_PWR_CR &= ~STM32_PWD_PVD_LS_MASK;
/* Set the new value of the PVD Level Selection. */
STM32_PWR_CR |= STM32_PWD_PVD_LS(PVD_THRESHOLD);
/* Enable Power Clock. */
STM32_RCC_APB1ENR |= STM32_RCC_PB1_PWREN;
/* Configure the NVIC for PVD. */
task_enable_irq(STM32_IRQ_PVD);
/* Configure interrupt mode. */
STM32_EXTI_IMR |= EXTI_PVD_EVENT;
STM32_EXTI_RTSR |= EXTI_PVD_EVENT;
/* Enable the PVD Output. */
STM32_PWR_CR |= STM32_PWR_PVDE;
}
static void pvd_interrupt(void)
{
/* Clear Pending Register */
STM32_EXTI_PR = EXTI_PVD_EVENT;
/* Handle recovery by rebooting the system */
system_reset(0);
}
DECLARE_IRQ(STM32_IRQ_PVD, pvd_interrupt, HOOK_PRIO_FIRST);
#endif /* CONFIG_PVD */
void system_pre_init(void)
{
uint16_t reason, info;
uint8_t exception, panic_flags;
struct panic_data *pdata;
/* enable clock on Power module */
#ifndef CHIP_FAMILY_STM32H7
#if defined(CHIP_FAMILY_STM32L4) || defined(CHIP_FAMILY_STM32L5)
STM32_RCC_APB1ENR1 |= STM32_RCC_PWREN;
#else
STM32_RCC_APB1ENR |= STM32_RCC_PWREN;
#endif
#endif
#if defined(CHIP_FAMILY_STM32F4)
/* enable backup registers */
STM32_RCC_AHB1ENR |= STM32_RCC_AHB1ENR_BKPSRAMEN;
#elif defined(CHIP_FAMILY_STM32H7)
/* enable backup registers */
STM32_RCC_AHB4ENR |= BIT(28);
#elif defined(CHIP_FAMILY_STM32L4) || defined(CHIP_FAMILY_STM32L5)
/* enable RTC APB clock */
STM32_RCC_APB1ENR1 |= STM32_RCC_APB1ENR1_RTCAPBEN;
#else
/* enable backup registers */
STM32_RCC_APB1ENR |= BIT(27);
#endif
/* Delay 1 APB clock cycle after the clock is enabled */
clock_wait_bus_cycles(BUS_APB, 1);
/* Enable access to RCC CSR register and RTC backup registers */
STM32_PWR_CR |= BIT(8);
#ifdef CHIP_VARIANT_STM32L476
/* Enable Vddio2 */
STM32_PWR_CR2 |= BIT(9);
#endif
/* switch on LSI */
STM32_RCC_CSR |= BIT(0);
/* Wait for LSI to be ready */
while (!(STM32_RCC_CSR & BIT(1)))
;
#if defined(CHIP_FAMILY_STM32G4)
/* Make sure PWR clock is enabled */
STM32_RCC_APB1ENR1 |= STM32_RCC_APB1ENR1_PWREN;
/* Enable access to backup domain registers */
STM32_PWR_CR1 |= STM32_PWR_CR1_DBP;
#endif
/* re-configure RTC if needed */
#ifdef CHIP_FAMILY_STM32L
if ((STM32_RCC_CSR & 0x00C30000) != 0x00420000) {
/* The RTC settings are bad, we need to reset it */
STM32_RCC_CSR |= 0x00800000;
/* Enable RTC and use LSI as clock source */
STM32_RCC_CSR = (STM32_RCC_CSR & ~0x00C30000) | 0x00420000;
}
#elif defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3) || \
defined(CHIP_FAMILY_STM32L4) || defined(CHIP_FAMILY_STM32L5) || \
defined(CHIP_FAMILY_STM32F4) || defined(CHIP_FAMILY_STM32H7) || \
defined(CHIP_FAMILY_STM32G4)
if ((STM32_RCC_BDCR & BDCR_ENABLE_MASK) != BDCR_ENABLE_VALUE) {
/* The RTC settings are bad, we need to reset it */
STM32_RCC_BDCR |= STM32_RCC_BDCR_BDRST;
STM32_RCC_BDCR = STM32_RCC_BDCR & ~BDCR_ENABLE_MASK;
#ifdef CONFIG_STM32_CLOCK_LSE
/* Turn on LSE */
STM32_RCC_BDCR |= STM32_RCC_BDCR_LSEON;
/* Wait for LSE to be ready */
while (!(STM32_RCC_BDCR & STM32_RCC_BDCR_LSERDY))
;
#endif
/* Select clock source and enable RTC */
STM32_RCC_BDCR |= BDCR_RTCSEL(BDCR_SRC) | STM32_RCC_BDCR_RTCEN;
}
#else
#error "Unsupported chip family"
#endif
check_reset_cause();
/* Restore then clear saved panic reason */
reason = bkpdata_read(BKPDATA_INDEX_SAVED_PANIC_REASON);
info = bkpdata_read(BKPDATA_INDEX_SAVED_PANIC_INFO);
exception = bkpdata_read(BKPDATA_INDEX_SAVED_PANIC_EXCEPTION);
if (reason || info || exception) {
panic_set_reason(reason, info, exception);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_REASON, 0);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_INFO, 0);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_EXCEPTION, 0);
}
/*
* Older ROs restore reason, info, and exception, but do not support
* the saved panic flags. In that case, we will let RW handle restoring
* the panic flags. If we get to this point in the code and the panic
* data does not exist, it doesn't make sense to try to only restore
* the panic flags, the information was lost.
*/
pdata = panic_get_data();
panic_flags = bkpdata_read(BKPDATA_INDEX_SAVED_PANIC_FLAGS);
if (pdata && panic_flags) {
pdata->flags = panic_flags;
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_FLAGS, 0);
}
#ifdef CONFIG_PVD
configure_pvd();
#endif
}
void system_reset(int flags)
{
uint32_t save_flags = 0;
/* Disable interrupts to avoid task swaps during reboot */
interrupt_disable();
/*
* TODO(crbug.com/1045283): Change this part of code to use
* system_encode_save_flags, like all other system_reset functions.
*
* system_encode_save_flags(flags, &save_flags);
*/
/* Save current reset reasons if necessary */
if (flags & SYSTEM_RESET_PRESERVE_FLAGS)
save_flags = system_get_reset_flags() | EC_RESET_FLAG_PRESERVED;
if (flags & SYSTEM_RESET_LEAVE_AP_OFF)
save_flags |= EC_RESET_FLAG_AP_OFF;
/* Remember that the software asked us to hard reboot */
if (flags & SYSTEM_RESET_HARD)
save_flags |= EC_RESET_FLAG_HARD;
/* Add in stay in RO flag into saved flags. */
if (flags & SYSTEM_RESET_STAY_IN_RO)
save_flags |= EC_RESET_FLAG_STAY_IN_RO;
if (flags & SYSTEM_RESET_AP_WATCHDOG)
save_flags |= EC_RESET_FLAG_AP_WATCHDOG;
chip_save_reset_flags(save_flags);
#ifdef CONFIG_ARMV7M_CACHE
/*
* Disable caches (D-cache is also flushed and invalidated)
* so changes that lives in cache are saved in memory now.
* Any subsequent writes will be done immediately.
*/
cpu_disable_caches();
#endif
if (flags & SYSTEM_RESET_HARD) {
/* Panic data will be wiped by hard reset, so save it */
uint32_t reason, info;
uint8_t exception, panic_flags;
struct panic_data *pdata = panic_get_data();
if (pdata) {
panic_flags = pdata->flags;
panic_get_reason(&reason, &info, &exception);
/*
* 16 bits stored - upper 16 bits of reason / info
* are lost.
*/
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_REASON, reason);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_INFO, info);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_EXCEPTION,
exception);
bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_FLAGS,
panic_flags);
}
#if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32L4)
/*
* Ask the flash module to reboot, so that we reload the
* option bytes.
*/
crec_flash_physical_force_reload();
/* Fall through to watchdog if that fails */
#endif
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3)
/*
* On some chips, a reboot doesn't always reload the option
* bytes, and we need to explicitly request for a reload.
* The reload request triggers a chip reset, so let's just
* use this for hard reset.
*/
STM32_FLASH_CR |= FLASH_CR_OBL_LAUNCH;
#elif defined(CHIP_FAMILY_STM32G4)
STM32_FLASH_KEYR = FLASH_KEYR_KEY1;
STM32_FLASH_KEYR = FLASH_KEYR_KEY2;
STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY1;
STM32_FLASH_OPTKEYR = FLASH_OPTKEYR_KEY2;
STM32_FLASH_CR |= FLASH_CR_OBL_LAUNCH;
#else
/*
* RM0433 Rev 6
* Section 44.3.3
* https://www.st.com/resource/en/reference_manual/dm00314099.pdf#page=1898
*
* When the window option is not used, the IWDG can be
* configured as follows:
*
* 1. Enable the IWDG by writing 0x0000 CCCC in the Key
* register (IWDG_KR).
* 2. Enable register access by writing 0x0000 5555 in the Key
* register (IWDG_KR).
* 3. Write the prescaler by programming the Prescaler register
* (IWDG_PR) from 0 to 7.
* 4. Write the Reload register (IWDG_RLR).
* 5. Wait for the registers to be updated
* (IWDG_SR = 0x0000 0000).
* 6. Refresh the counter value with IWDG_RLR
* (IWDG_KR = 0x0000 AAAA)
*/
/*
* RM0433 Rev 7
* Section 45.4.4 Page 1920
* https://www.st.com/resource/en/reference_manual/dm00314099.pdf
* If several reload, prescaler, or window values are used by
* the application, it is mandatory to wait until RVU bit is
* reset before changing the reload value, to wait until PVU bit
* is reset before changing the prescaler value, and to wait
* until WVU bit is reset before changing the window value.
*
* Here we should wait to finish previous IWDG_RLR register
* update (see watchdog_init()) before starting next update,
* otherwise new IWDG_RLR value will be lost.
*/
while (STM32_IWDG_SR & STM32_IWDG_SR_RVU)
;
/*
* Enable IWDG, which shouldn't be necessary since the IWDG
* only needs to be started once, but STM32F412 hangs unless
* this is added.
*
* See http://b/137045370.
*/
STM32_IWDG_KR = STM32_IWDG_KR_START;
/* Ask the watchdog to trigger a hard reboot */
STM32_IWDG_KR = STM32_IWDG_KR_UNLOCK;
STM32_IWDG_RLR = 0x1;
/* Wait for value to be updated. */
while (STM32_IWDG_SR & STM32_IWDG_SR_RVU)
;
/* Reload IWDG counter, it also locks registers */
STM32_IWDG_KR = STM32_IWDG_KR_RELOAD;
#endif
/* wait for the chip to reboot */
while (1)
;
} else {
if (flags & SYSTEM_RESET_WAIT_EXT) {
int i;
/* Wait 10 seconds for external reset */
for (i = 0; i < 1000; i++) {
watchdog_reload();
udelay(10000);
}
}
/* Request a soft system reset from the core. */
CPU_NVIC_APINT = CPU_NVIC_APINT_KEY_WR | CPU_NVIC_APINT_SYSRST;
}
/* Spin and wait for reboot; should never return */
while (1)
;
}
int system_set_scratchpad(uint32_t value)
{
/* Check if value fits in 16 bits */
if (value & 0xffff0000)
return EC_ERROR_INVAL;
return bkpdata_write(BKPDATA_INDEX_SCRATCHPAD, (uint16_t)value);
}
int system_get_scratchpad(uint32_t *value)
{
*value = (uint32_t)bkpdata_read(BKPDATA_INDEX_SCRATCHPAD);
return EC_SUCCESS;
}
const char *system_get_chip_vendor(void)
{
return "stm";
}
const char *system_get_chip_name(void)
{
return STRINGIFY(CHIP_VARIANT);
}
const char *system_get_chip_revision(void)
{
return "";
}
int system_get_chip_unique_id(uint8_t **id)
{
*id = (uint8_t *)STM32_UNIQUE_ID_ADDRESS;
return STM32_UNIQUE_ID_LENGTH;
}
int system_get_bbram(enum system_bbram_idx idx, uint8_t *value)
{
int msb = 0;
int bkpdata_index = bkpdata_index_lookup(idx, &msb);
if (bkpdata_index < 0)
return EC_ERROR_INVAL;
*value = (bkpdata_read(bkpdata_index) >> (8 * msb)) & 0xff;
return EC_SUCCESS;
}
int system_set_bbram(enum system_bbram_idx idx, uint8_t value)
{
uint16_t read;
int msb = 0;
int bkpdata_index = bkpdata_index_lookup(idx, &msb);
if (bkpdata_index < 0)
return EC_ERROR_INVAL;
read = bkpdata_read(bkpdata_index);
if (msb)
read = (read & 0xff) | (value << 8);
else
read = (read & 0xff00) | value;
bkpdata_write(bkpdata_index, read);
return EC_SUCCESS;
}
int system_is_reboot_warm(void)
{
/*
* Detecting if the system is warm is relevant for a
* few reasons.
* One such reason is that some firmwares transition from
* RO to RW images. When this happens, we may not need to
* restart certain clocks. On the flip side, we may need
* to restart the clocks if the RW requires a different
* set of clocks. Thus, the clock configurations need to
* be checked for a perfect match.
*/
#if defined(CHIP_FAMILY_STM32F0) || defined(CHIP_FAMILY_STM32F3)
return ((STM32_RCC_AHBENR & 0x7e0000) == 0x7e0000);
#elif defined(CHIP_FAMILY_STM32L)
return ((STM32_RCC_AHBENR & 0x3f) == 0x3f);
#elif defined(CHIP_FAMILY_STM32L4)
return ((STM32_RCC_AHB2ENR & STM32_RCC_AHB2ENR_GPIOMASK) ==
STM32_RCC_AHB2ENR_GPIOMASK);
#elif defined(CHIP_FAMILY_STM32L5)
return ((STM32_RCC_AHB2ENR & STM32_RCC_AHB2ENR_GPIOMASK) ==
STM32_RCC_AHB2ENR_GPIOMASK);
#elif defined(CHIP_FAMILY_STM32F4)
return ((STM32_RCC_AHB1ENR & STM32_RCC_AHB1ENR_GPIOMASK) ==
gpio_required_clocks());
#elif defined(CHIP_FAMILY_STM32G4)
return ((STM32_RCC_AHB2ENR & STM32_RCC_AHB2ENR_GPIOMASK) ==
gpio_required_clocks());
#elif defined(CHIP_FAMILY_STM32H7)
return ((STM32_RCC_AHB4ENR & STM32_RCC_AHB4ENR_GPIOMASK) ==
STM32_RCC_AHB4ENR_GPIOMASK);
#endif
}