Files
OpenCellular/chip/stm32/system.c
Vic Yang 82915c2502 Write protect support for STM32F0
On STM32F0, we cannot work around the hard fault triggered when trying
to protect the whole flash. Therefore, we need to go with the
ALL_AT_BOOT approach. When write protect is enabled, instead of setting
ALL_NOW flag to immediately lock down the entire flash, we need to set
ALL_AT_BOOT and then reboot to have the protection take effect.

BUG=chrome-os-partner:32745
TEST=Along with the next CL. On Ryu:
  1. Enable HW WP. Check the output of 'ectool flashprotect' and see
     correct flags.
  2. 'flashrom -p ec --wp-range 0 0x10000'. Check RO_AT_BOOT is set.
  3. Reboot EC and check RO_NOW is enabled.
  4. Boot the system and check ALL_NOW is set.
  5. Update BIOS and reboot. Check software sync updates EC-RW.
  6. 'flashrom -p ec --wp-disable' and check it fails.
  7. Disable HW WP and reboot EC. Check RO_NOW and ALL_NOW are cleared.
  8. 'flashrom -p ec --wp-disable' and check RO_AT_BOOT is cleared.
TEST=Enable/disable WP on Spring. Check RO_AT_BOOT/ALL_NOW can be set
properly.
BRANCH=samus

Change-Id: I1c7c4f98f2535f1c8a1c7daaa88d47412d015977
Signed-off-by: Vic Yang <victoryang@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/222622
Reviewed-by: Randall Spangler <rspangler@chromium.org>
2014-10-15 23:55:55 +00:00

343 lines
8.2 KiB
C

/* 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;
#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)
{
/* 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) || defined(CHIP_FAMILY_STM32F0) || \
defined(CHIP_FAMILY_STM32F3)
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();
/* 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
#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)
{
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;
}