/* Copyright (c) 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 "clock.h" #include "console.h" #include "cpu.h" #include "flash.h" #include "host_command.h" #include "registers.h" #include "panic.h" #include "system.h" #include "task.h" #include "util.h" #include "version.h" #include "watchdog.h" enum bkpdata_index { BKPDATA_INDEX_SCRATCHPAD, /* General-purpose scratchpad */ BKPDATA_INDEX_SAVED_RESET_FLAGS, /* Saved reset flags */ BKPDATA_INDEX_VBNV_CONTEXT0, BKPDATA_INDEX_VBNV_CONTEXT1, BKPDATA_INDEX_VBNV_CONTEXT2, BKPDATA_INDEX_VBNV_CONTEXT3, BKPDATA_INDEX_VBNV_CONTEXT4, BKPDATA_INDEX_VBNV_CONTEXT5, BKPDATA_INDEX_VBNV_CONTEXT6, BKPDATA_INDEX_VBNV_CONTEXT7, #ifdef CONFIG_SOFTWARE_PANIC BKPDATA_INDEX_SAVED_PANIC_REASON, /* Saved panic reason */ BKPDATA_INDEX_SAVED_PANIC_INFO, /* Saved panic data */ BKPDATA_INDEX_SAVED_PANIC_EXCEPTION, /* Saved panic exception code */ #endif BKPDATA_INDEX_PD0, /* USB-PD saved port0 state */ BKPDATA_INDEX_PD1, /* USB-PD saved port1 state */ }; /** * Read backup register at specified index. * * @return The value of the register or 0 if invalid index. */ static uint16_t bkpdata_read(enum bkpdata_index index) { if (index < 0 || index >= STM32_BKP_ENTRIES) return 0; #if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32F0) || \ defined(CHIP_FAMILY_STM32F3) if (index & 1) return STM32_BKP_DATA(index >> 1) >> 16; else return STM32_BKP_DATA(index >> 1) & 0xFFFF; #else return STM32_BKP_DATA(index); #endif } /** * Write hibernate register at specified index. * * @return nonzero if error. */ static int bkpdata_write(enum bkpdata_index index, uint16_t value) { if (index < 0 || index >= STM32_BKP_ENTRIES) return EC_ERROR_INVAL; #if defined(CHIP_FAMILY_STM32L) || defined(CHIP_FAMILY_STM32F0) || \ defined(CHIP_FAMILY_STM32F3) if (index & 1) { uint32_t val = STM32_BKP_DATA(index >> 1); val = (val & 0x0000FFFF) | (value << 16); STM32_BKP_DATA(index >> 1) = val; } else { uint32_t val = STM32_BKP_DATA(index >> 1); val = (val & 0xFFFF0000) | value; STM32_BKP_DATA(index >> 1) = val; } #else STM32_BKP_DATA(index) = value; #endif return EC_SUCCESS; } 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); } static void check_reset_cause(void) { uint32_t flags = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS); uint32_t raw_cause = STM32_RCC_CSR; uint32_t pwr_status = STM32_PWR_CSR; /* Clear the hardware reset cause by setting the RMVF bit */ STM32_RCC_CSR |= 1 << 24; /* Clear SBF in PWR_CSR */ STM32_PWR_CR |= 1 << 3; /* Clear saved reset flags */ bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, 0); if (raw_cause & 0x60000000) { /* * IWDG or WWDG, if the watchdog was not used as an hard reset * mechanism */ if (!(flags & RESET_FLAG_HARD)) flags |= RESET_FLAG_WATCHDOG; } if (raw_cause & 0x10000000) flags |= RESET_FLAG_SOFT; if (raw_cause & 0x08000000) flags |= RESET_FLAG_POWER_ON; if (raw_cause & 0x04000000) flags |= RESET_FLAG_RESET_PIN; if (pwr_status & 0x00000002) /* Hibernated and subsequently awakened */ flags |= RESET_FLAG_HIBERNATE; if (!flags && (raw_cause & 0xfe000000)) flags |= 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 & (RESET_FLAG_HIBERNATE | RESET_FLAG_WATCHDOG)) == (RESET_FLAG_HIBERNATE | RESET_FLAG_WATCHDOG)) { __enter_hibernate(0, 0); } system_set_reset_flags(flags); } void system_pre_init(void) { #ifdef CONFIG_SOFTWARE_PANIC uint16_t reason, info; uint8_t exception; #endif /* enable clock on Power module */ STM32_RCC_APB1ENR |= STM32_RCC_PWREN; #if defined(CHIP_FAMILY_STM32F4) /* enable backup registers */ STM32_RCC_AHB1ENR |= STM32_RCC_AHB1ENR_BKPSRAMEN; #else /* enable backup registers */ STM32_RCC_APB1ENR |= 1 << 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 |= 1 << 8; #ifdef CHIP_VARIANT_STM32L476 /* Enable Vddio2 */ STM32_PWR_CR2 |= 1 << 9; #endif /* switch on LSI */ STM32_RCC_CSR |= 1 << 0; /* Wait for LSI to be ready */ while (!(STM32_RCC_CSR & (1 << 1))) ; /* 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_STM32F4) if ((STM32_RCC_BDCR & 0x00018300) != 0x00008200) { /* the RTC settings are bad, we need to reset it */ STM32_RCC_BDCR |= 0x00010000; /* Enable RTC and use LSI as clock source */ STM32_RCC_BDCR = (STM32_RCC_BDCR & ~0x00018300) | 0x00008200; } #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); 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); } #endif } void system_reset(int flags) { uint32_t save_flags = 0; /* Disable interrupts to avoid task swaps during reboot */ interrupt_disable(); /* Save current reset reasons if necessary */ if (flags & SYSTEM_RESET_PRESERVE_FLAGS) save_flags = system_get_reset_flags() | RESET_FLAG_PRESERVED; if (flags & SYSTEM_RESET_LEAVE_AP_OFF) save_flags |= RESET_FLAG_AP_OFF; /* Remember that the software asked us to hard reboot */ if (flags & SYSTEM_RESET_HARD) save_flags |= RESET_FLAG_HARD; bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, save_flags); if (flags & SYSTEM_RESET_HARD) { #ifdef CONFIG_SOFTWARE_PANIC uint32_t reason, info; uint8_t exception; /* 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); #endif #ifdef CHIP_FAMILY_STM32L /* * Ask the flash module to reboot, so that we reload the * option bytes. */ 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 |= STM32_FLASH_CR_OBL_LAUNCH; #else /* Ask the watchdog to trigger a hard reboot */ STM32_IWDG_KR = 0x5555; STM32_IWDG_RLR = 0x1; STM32_IWDG_KR = 0xcccc; #endif /* wait for the chip to reboot */ while (1) ; } else { CPU_NVIC_APINT = 0x05fa0004; } /* 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); } uint32_t system_get_scratchpad(void) { return (uint32_t)bkpdata_read(BKPDATA_INDEX_SCRATCHPAD); } 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 ""; } static int bkpdata_index_lookup(enum system_bbram_idx idx, int *msb) { *msb = 0; if (idx >= SYSTEM_BBRAM_IDX_VBNVBLOCK0 && idx <= SYSTEM_BBRAM_IDX_VBNVBLOCK15) { *msb = (idx - SYSTEM_BBRAM_IDX_VBNVBLOCK0) % 2; return BKPDATA_INDEX_VBNV_CONTEXT0 + (idx - SYSTEM_BBRAM_IDX_VBNVBLOCK0) / 2; } #ifdef CONFIG_USB_PD_DUAL_ROLE if (idx == SYSTEM_BBRAM_IDX_PD0) return BKPDATA_INDEX_PD0; if (idx == SYSTEM_BBRAM_IDX_PD1) return BKPDATA_INDEX_PD1; #endif return -1; } 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) { #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_STM32F4) return ((STM32_RCC_AHB1ENR & STM32_RCC_AHB1ENR_GPIOMASK) == STM32_RCC_AHB1ENR_GPIOMASK); #endif }