/* Copyright 2012 The Chromium OS Authors. All rights reserved. * 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. */ 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_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 defined(CHIP_FAMILY_STM32L5) (void)apb1fz_reg; (void)apb2fz_reg; #else if (apb1fz_reg) STM32_DBGMCU_APB1FZ |= apb1fz_reg; if (apb2fz_reg) STM32_DBGMCU_APB2FZ |= apb2fz_reg; #endif } #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; } 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) { #ifdef CONFIG_SOFTWARE_PANIC uint16_t reason, info; uint8_t exception, panic_flags; #endif /* enable clock on Power module */ #ifndef CHIP_FAMILY_STM32H7 #ifdef CHIP_FAMILY_STM32L4 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) /* 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(); #ifdef CONFIG_SOFTWARE_PANIC /* 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); panic_flags = bkpdata_read(BKPDATA_INDEX_SAVED_PANIC_FLAGS); if (reason || info || exception || panic_flags) { panic_set_reason(reason, info, exception); panic_get_data()->flags = panic_flags; bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_REASON, 0); bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_INFO, 0); bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_EXCEPTION, 0); bkpdata_write(BKPDATA_INDEX_SAVED_PANIC_FLAGS, 0); } #endif #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) { #ifdef CONFIG_SOFTWARE_PANIC uint32_t reason, info; uint8_t exception; uint8_t panic_flags = panic_get_data()->flags; /* Panic data will be wiped by hard reset, so save it */ 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); #endif #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 }