blob: 746cbd80014ba17e11642cbbde32d7ad967ed6b5 [file] [log] [blame]
/* 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 "console.h"
#include "cpu.h"
#include "flash.h"
#include "registers.h"
#include "system.h"
#include "task.h"
#include "util.h"
#include "version.h"
#include "watchdog.h"
#define CONSOLE_BIT_MASK 0x8000
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,
};
/**
* 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;
return STM32_BKP_DATA(index);
}
/**
* 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;
STM32_BKP_DATA(index) = value;
return EC_SUCCESS;
}
void __no_hibernate(uint32_t seconds, uint32_t microseconds)
{
/*
* Hibernate not implemented on this platform.
*
* Until then, treat this as a request to hard-reboot.
*/
cprintf(CC_SYSTEM, "[%T hibernate not supported, so rebooting]\n");
cflush();
system_reset(SYSTEM_RESET_HARD);
}
void __enter_hibernate(uint32_t seconds, uint32_t microseconds)
__attribute__((weak, alias("__no_hibernate")));
void system_hibernate(uint32_t seconds, uint32_t microseconds)
{
/* Flush console before hibernating */
cflush();
/* 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;
uint32_t console_en = flags & CONSOLE_BIT_MASK;
flags &= ~CONSOLE_BIT_MASK;
/* 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 | console_en);
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)
{
/* enable clock on Power module */
STM32_RCC_APB1ENR |= 1 << 28;
/* enable backup registers */
STM32_RCC_APB1ENR |= 1 << 27;
/* Enable access to RCC CSR register and RTC backup registers */
STM32_PWR_CR |= 1 << 8;
/* 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_stm32f)
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();
}
void system_reset(int flags)
{
uint32_t save_flags = 0;
uint32_t console_en = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS) &
CONSOLE_BIT_MASK;
/* Disable interrupts to avoid task swaps during reboot */
interrupt_disable();
/*
* TODO: Check if a collision between reset flags and fake wp occurred.
* Remove this when we have real write protect pin.
*/
ASSERT(!(system_get_reset_flags() & 0x8000));
/* 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 | console_en);
if (flags & SYSTEM_RESET_HARD) {
#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
/* Ask the watchdog to trigger a hard reboot */
STM32_IWDG_KR = 0x5555;
STM32_IWDG_RLR = 0x1;
STM32_IWDG_KR = 0xcccc;
/* wait for the watchdog */
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)
{
if (system_get_console_force_enabled())
return STRINGIFY(CHIP_VARIANT-unsafe);
else
return STRINGIFY(CHIP_VARIANT);
}
const char *system_get_chip_revision(void)
{
return "";
}
int system_get_vbnvcontext(uint8_t *block)
{
enum bkpdata_index i;
uint16_t value;
for (i = BKPDATA_INDEX_VBNV_CONTEXT0;
i <= BKPDATA_INDEX_VBNV_CONTEXT7; i++) {
value = bkpdata_read(i);
*block++ = (uint8_t)(value & 0xff);
*block++ = (uint8_t)(value >> 8);
}
return EC_SUCCESS;
}
int system_set_vbnvcontext(const uint8_t *block)
{
enum bkpdata_index i;
uint16_t value;
int err;
for (i = BKPDATA_INDEX_VBNV_CONTEXT0;
i <= BKPDATA_INDEX_VBNV_CONTEXT7; i++) {
value = *block++;
value |= ((uint16_t)*block++) << 8;
err = bkpdata_write(i, value);
if (err)
return err;
}
return EC_SUCCESS;
}
int system_set_console_force_enabled(int val)
{
uint16_t flags = bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS);
if (val)
flags |= CONSOLE_BIT_MASK;
else
flags &= ~CONSOLE_BIT_MASK;
return bkpdata_write(BKPDATA_INDEX_SAVED_RESET_FLAGS, flags);
}
int system_get_console_force_enabled(void)
{
if (bkpdata_read(BKPDATA_INDEX_SAVED_RESET_FLAGS) & CONSOLE_BIT_MASK)
return 1;
else
return 0;
}