mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
Factor out common flash code for STM32F and STM32F0
This is a preparatory work for the following change for write protection support on STM32F0. BUG=chrome-os-partner:32745 TEST=make buildall BRANCH=samus Change-Id: Ic4deea06e26c4a6ac024a5388e1a5783b40e9876 Signed-off-by: Vic Yang <victoryang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/222660 Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
committed by
chrome-internal-fetch
parent
b8f73a451d
commit
4a9cabc3f9
7
Makefile
7
Makefile
@@ -92,14 +92,17 @@ _rw_size_str:=$(shell echo "CONFIG_FW_RW_SIZE" | $(CPP) $(CPPFLAGS) -P \
|
||||
-Ichip/$(CHIP) -Iboard/$(BOARD) -imacros include/config.h)
|
||||
_rw_size:=$(shell echo "$$(($(_rw_size_str)))")
|
||||
|
||||
$(eval BOARD_$(UC_BOARD)=y)
|
||||
$(eval CHIP_$(UC_CHIP)=y)
|
||||
$(eval CHIP_VARIANT_$(UC_CHIP_VARIANT)=y)
|
||||
$(eval CHIP_FAMILY_$(UC_CHIP_FAMILY)=y)
|
||||
|
||||
# Get build configuration from sub-directories
|
||||
# Note that this re-includes the board and chip makefiles
|
||||
include board/$(BOARD)/build.mk
|
||||
include chip/$(CHIP)/build.mk
|
||||
include core/$(CORE)/build.mk
|
||||
|
||||
$(eval BOARD_$(UC_BOARD)=y)
|
||||
|
||||
include common/build.mk
|
||||
include driver/build.mk
|
||||
include power/build.mk
|
||||
|
||||
@@ -35,6 +35,9 @@ chip-$(HAS_TASK_CONSOLE)+=uart.o
|
||||
chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o
|
||||
chip-$(HAS_TASK_POWERLED)+=power_led.o
|
||||
chip-$(CONFIG_FLASH)+=flash-$(CHIP_FAMILY).o
|
||||
chip-$(CHIP_FAMILY_STM32F)+=flash-f.o
|
||||
chip-$(CHIP_FAMILY_STM32F0)+=flash-f.o
|
||||
chip-$(CHIP_FAMILY_STM32F3)+=flash-f.o
|
||||
chip-$(CONFIG_ADC)+=adc-$(CHIP_FAMILY).o
|
||||
chip-$(CONFIG_PWM)+=pwm.o
|
||||
chip-$(CONFIG_USB)+=usb.o usb_endpoints.o
|
||||
|
||||
448
chip/stm32/flash-f.c
Normal file
448
chip/stm32/flash-f.c
Normal file
@@ -0,0 +1,448 @@
|
||||
/* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
/* Common flash memory module for STM32F and STM32F0 */
|
||||
|
||||
#include "battery.h"
|
||||
#include "console.h"
|
||||
#include "flash.h"
|
||||
#include "hooks.h"
|
||||
#include "registers.h"
|
||||
#include "panic.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "util.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
/*
|
||||
* Approximate number of CPU cycles per iteration of the loop when polling
|
||||
* the flash status
|
||||
*/
|
||||
#define CYCLE_PER_FLASH_LOOP 10
|
||||
|
||||
/* Flash page programming timeout. This is 2x the datasheet max. */
|
||||
#define FLASH_TIMEOUT_US 16000
|
||||
#define FLASH_TIMEOUT_LOOP \
|
||||
(FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)
|
||||
|
||||
/* Flash unlocking keys */
|
||||
#define KEY1 0x45670123
|
||||
#define KEY2 0xCDEF89AB
|
||||
|
||||
/* Lock bits for FLASH_CR register */
|
||||
#define PG (1<<0)
|
||||
#define PER (1<<1)
|
||||
#define OPTPG (1<<4)
|
||||
#define OPTER (1<<5)
|
||||
#define STRT (1<<6)
|
||||
#define CR_LOCK (1<<7)
|
||||
#define PRG_LOCK 0
|
||||
#define OPT_LOCK (1<<9)
|
||||
|
||||
static int write_optb(int byte, uint8_t value);
|
||||
|
||||
static int wait_busy(void)
|
||||
{
|
||||
int timeout = FLASH_TIMEOUT_LOOP;
|
||||
while (STM32_FLASH_SR & (1 << 0) && timeout-- > 0)
|
||||
udelay(CYCLE_PER_FLASH_LOOP);
|
||||
return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
static int unlock(int locks)
|
||||
{
|
||||
/*
|
||||
* We may have already locked the flash module and get a bus fault
|
||||
* in the attempt to unlock. Need to disable bus fault handler now.
|
||||
*/
|
||||
ignore_bus_fault(1);
|
||||
|
||||
/* unlock CR if needed */
|
||||
if (STM32_FLASH_CR & CR_LOCK) {
|
||||
STM32_FLASH_KEYR = KEY1;
|
||||
STM32_FLASH_KEYR = KEY2;
|
||||
}
|
||||
/* unlock option memory if required */
|
||||
if ((locks & OPT_LOCK) && !(STM32_FLASH_CR & OPT_LOCK)) {
|
||||
STM32_FLASH_OPTKEYR = KEY1;
|
||||
STM32_FLASH_OPTKEYR = KEY2;
|
||||
}
|
||||
|
||||
/* Re-enable bus fault handler */
|
||||
ignore_bus_fault(0);
|
||||
|
||||
return ((STM32_FLASH_CR ^ OPT_LOCK) & (locks | CR_LOCK)) ?
|
||||
EC_ERROR_UNKNOWN : EC_SUCCESS;
|
||||
}
|
||||
|
||||
static void lock(void)
|
||||
{
|
||||
STM32_FLASH_CR = CR_LOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Option byte organization
|
||||
*
|
||||
* [31:24] [23:16] [15:8] [7:0]
|
||||
*
|
||||
* 0x1FFF_F800 nUSER USER nRDP RDP
|
||||
*
|
||||
* 0x1FFF_F804 nData1 Data1 nData0 Data0
|
||||
*
|
||||
* 0x1FFF_F808 nWRP1 WRP1 nWRP0 WRP0
|
||||
*
|
||||
* 0x1FFF_F80C nWRP3 WRP2 nWRP2 WRP2
|
||||
*
|
||||
* Note that the variable with n prefix means the complement.
|
||||
*/
|
||||
static uint8_t read_optb(int byte)
|
||||
{
|
||||
return *(uint8_t *)(STM32_OPTB_BASE + byte);
|
||||
}
|
||||
|
||||
static int erase_optb(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv = unlock(OPT_LOCK);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Must be set in 2 separate lines. */
|
||||
STM32_FLASH_CR |= OPTER;
|
||||
STM32_FLASH_CR |= STRT;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
lock();
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the option byte erase is WHOLE erase, this function is to keep
|
||||
* rest of bytes, but make this byte 0xff.
|
||||
* Note that this could make a recursive call to write_optb().
|
||||
*/
|
||||
static int preserve_optb(int byte)
|
||||
{
|
||||
int i, rv;
|
||||
uint8_t optb[8];
|
||||
|
||||
/* The byte has been reset, no need to run preserve. */
|
||||
if (*(uint16_t *)(STM32_OPTB_BASE + byte) == 0xffff)
|
||||
return EC_SUCCESS;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(optb); ++i)
|
||||
optb[i] = read_optb(i * 2);
|
||||
|
||||
optb[byte / 2] = 0xff;
|
||||
|
||||
rv = erase_optb();
|
||||
if (rv)
|
||||
return rv;
|
||||
for (i = 0; i < ARRAY_SIZE(optb); ++i) {
|
||||
rv = write_optb(i * 2, optb[i]);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int write_optb(int byte, uint8_t value)
|
||||
{
|
||||
volatile int16_t *hword = (uint16_t *)(STM32_OPTB_BASE + byte);
|
||||
int rv;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* The target byte is the value we want to write. */
|
||||
if (*(uint8_t *)hword == value)
|
||||
return EC_SUCCESS;
|
||||
|
||||
/* Try to erase that byte back to 0xff. */
|
||||
rv = preserve_optb(byte);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* The value is 0xff after erase. No need to write 0xff again. */
|
||||
if (value == 0xff)
|
||||
return EC_SUCCESS;
|
||||
|
||||
rv = unlock(OPT_LOCK);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* set OPTPG bit */
|
||||
STM32_FLASH_CR |= OPTPG;
|
||||
|
||||
*hword = ((~value) << STM32_OPTB_COMPL_SHIFT) | value;
|
||||
|
||||
/* reset OPTPG bit */
|
||||
STM32_FLASH_CR &= ~OPTPG;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
lock();
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Physical layer APIs */
|
||||
|
||||
int flash_physical_write(int offset, int size, const char *data)
|
||||
{
|
||||
uint16_t *address = (uint16_t *)(CONFIG_FLASH_BASE + offset);
|
||||
int res = EC_SUCCESS;
|
||||
int i;
|
||||
|
||||
if (unlock(PRG_LOCK) != EC_SUCCESS) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_wr;
|
||||
}
|
||||
|
||||
/* Clear previous error status */
|
||||
STM32_FLASH_SR = 0x34;
|
||||
|
||||
/* set PG bit */
|
||||
STM32_FLASH_CR |= PG;
|
||||
|
||||
for (; size > 0; size -= sizeof(uint16_t)) {
|
||||
/*
|
||||
* Reload the watchdog timer to avoid watchdog reset when doing
|
||||
* long writing with interrupt disabled.
|
||||
*/
|
||||
watchdog_reload();
|
||||
|
||||
/* wait to be ready */
|
||||
for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
|
||||
i++)
|
||||
;
|
||||
|
||||
/* write the half word */
|
||||
*address++ = data[0] + (data[1] << 8);
|
||||
data += 2;
|
||||
|
||||
/* Wait for writes to complete */
|
||||
for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
|
||||
i++)
|
||||
;
|
||||
|
||||
if (STM32_FLASH_SR & 1) {
|
||||
res = EC_ERROR_TIMEOUT;
|
||||
goto exit_wr;
|
||||
}
|
||||
|
||||
/* Check for error conditions - erase failed, voltage error,
|
||||
* protection error */
|
||||
if (STM32_FLASH_SR & 0x14) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_wr;
|
||||
}
|
||||
}
|
||||
|
||||
exit_wr:
|
||||
/* Disable PG bit */
|
||||
STM32_FLASH_CR &= ~PG;
|
||||
|
||||
lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int flash_physical_erase(int offset, int size)
|
||||
{
|
||||
int res = EC_SUCCESS;
|
||||
|
||||
if (unlock(PRG_LOCK) != EC_SUCCESS)
|
||||
return EC_ERROR_UNKNOWN;
|
||||
|
||||
/* Clear previous error status */
|
||||
STM32_FLASH_SR = 0x34;
|
||||
|
||||
/* set PER bit */
|
||||
STM32_FLASH_CR |= PER;
|
||||
|
||||
for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
|
||||
offset += CONFIG_FLASH_ERASE_SIZE) {
|
||||
timestamp_t deadline;
|
||||
|
||||
/* Do nothing if already erased */
|
||||
if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE))
|
||||
continue;
|
||||
|
||||
/* select page to erase */
|
||||
STM32_FLASH_AR = CONFIG_FLASH_BASE + offset;
|
||||
|
||||
/* set STRT bit : start erase */
|
||||
STM32_FLASH_CR |= STRT;
|
||||
|
||||
/*
|
||||
* Reload the watchdog timer to avoid watchdog reset during a
|
||||
* long erase operation.
|
||||
*/
|
||||
watchdog_reload();
|
||||
|
||||
deadline.val = get_time().val + FLASH_TIMEOUT_US;
|
||||
/* Wait for erase to complete */
|
||||
while ((STM32_FLASH_SR & 1) &&
|
||||
(get_time().val < deadline.val)) {
|
||||
usleep(300);
|
||||
}
|
||||
if (STM32_FLASH_SR & 1) {
|
||||
res = EC_ERROR_TIMEOUT;
|
||||
goto exit_er;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for error conditions - erase failed, voltage error,
|
||||
* protection error
|
||||
*/
|
||||
if (STM32_FLASH_SR & 0x14) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_er;
|
||||
}
|
||||
}
|
||||
|
||||
exit_er:
|
||||
/* reset PER bit */
|
||||
STM32_FLASH_CR &= ~PER;
|
||||
|
||||
lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int flash_physical_get_protect_at_boot(int block)
|
||||
{
|
||||
uint8_t val = read_optb(STM32_OPTB_WRP_OFF(block/8));
|
||||
return (!(val & (1 << (block % 8)))) ? 1 : 0;
|
||||
}
|
||||
|
||||
int flash_physical_protect_at_boot(enum flash_wp_range range)
|
||||
{
|
||||
int block;
|
||||
int i;
|
||||
int original_val[4], val[4];
|
||||
enum flash_wp_range cur_range;
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
original_val[i] = val[i] = read_optb(i * 2 + 8);
|
||||
|
||||
for (block = RO_BANK_OFFSET;
|
||||
block < RO_BANK_OFFSET + PHYSICAL_BANKS;
|
||||
block++) {
|
||||
int byte_off = STM32_OPTB_WRP_OFF(block/8) / 2 - 4;
|
||||
|
||||
if (block >= RO_BANK_OFFSET + RO_BANK_COUNT + PSTATE_BANK_COUNT)
|
||||
cur_range = FLASH_WP_ALL;
|
||||
else
|
||||
cur_range = FLASH_WP_RO;
|
||||
|
||||
if (cur_range <= range)
|
||||
val[byte_off] = val[byte_off] & (~(1 << (block % 8)));
|
||||
else
|
||||
val[byte_off] = val[byte_off] | (1 << (block % 8));
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
if (original_val[i] != val[i])
|
||||
write_optb(i * 2 + 8, val[i]);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if write protect register state is inconsistent with RO_AT_BOOT and
|
||||
* ALL_AT_BOOT state.
|
||||
*
|
||||
* @return zero if consistent, non-zero if inconsistent.
|
||||
*/
|
||||
static int registers_need_reset(void)
|
||||
{
|
||||
uint32_t flags = flash_get_protect();
|
||||
int i;
|
||||
int ro_at_boot = (flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? 1 : 0;
|
||||
int ro_wp_region_start = RO_BANK_OFFSET;
|
||||
int ro_wp_region_end =
|
||||
RO_BANK_OFFSET + RO_BANK_COUNT + PSTATE_BANK_COUNT;
|
||||
|
||||
for (i = ro_wp_region_start; i < ro_wp_region_end; i++)
|
||||
if (flash_physical_get_protect_at_boot(i) != ro_at_boot)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unprotect_all_blocks(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 4; i < 8; ++i)
|
||||
write_optb(i * 2, 0xff);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* High-level APIs */
|
||||
|
||||
int flash_pre_init(void)
|
||||
{
|
||||
uint32_t prot_flags = flash_get_protect();
|
||||
int need_reset = 0;
|
||||
|
||||
if (flash_physical_restore_state())
|
||||
return EC_SUCCESS;
|
||||
|
||||
if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) {
|
||||
if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
|
||||
!(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
|
||||
/*
|
||||
* Pstate wants RO protected at boot, but the write
|
||||
* protect register wasn't set to protect it. Force an
|
||||
* update to the write protect register and reboot so
|
||||
* it takes effect.
|
||||
*/
|
||||
flash_physical_protect_at_boot(FLASH_WP_RO);
|
||||
need_reset = 1;
|
||||
}
|
||||
|
||||
if (registers_need_reset()) {
|
||||
/*
|
||||
* Write protect register was in an inconsistent state.
|
||||
* Set it back to a good state and reboot.
|
||||
*
|
||||
* TODO(crosbug.com/p/23798): this seems really similar
|
||||
* to the check above. One of them should be able to
|
||||
* go away.
|
||||
*/
|
||||
flash_protect_ro_at_boot(
|
||||
prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT);
|
||||
need_reset = 1;
|
||||
}
|
||||
} else {
|
||||
if (prot_flags & EC_FLASH_PROTECT_RO_NOW) {
|
||||
/*
|
||||
* Write protect pin unasserted but some section is
|
||||
* protected. Drop it and reboot.
|
||||
*/
|
||||
unprotect_all_blocks();
|
||||
need_reset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_reset)
|
||||
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
@@ -1,46 +1,16 @@
|
||||
/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
|
||||
/* Copyright 2014 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.
|
||||
*/
|
||||
|
||||
/* Flash memory module for Chrome EC */
|
||||
|
||||
#include "battery.h"
|
||||
#include "console.h"
|
||||
#include "common.h"
|
||||
#include "flash.h"
|
||||
#include "hooks.h"
|
||||
#include "registers.h"
|
||||
#include "panic.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "util.h"
|
||||
#include "watchdog.h"
|
||||
|
||||
/*
|
||||
* Approximate number of CPU cycles per iteration of the loop when polling
|
||||
* the flash status
|
||||
*/
|
||||
#define CYCLE_PER_FLASH_LOOP 10
|
||||
|
||||
/* Flash page programming timeout. This is 2x the datasheet max. */
|
||||
#define FLASH_TIMEOUT_US 16000
|
||||
#define FLASH_TIMEOUT_LOOP \
|
||||
(FLASH_TIMEOUT_US * (CPU_CLOCK / SECOND) / CYCLE_PER_FLASH_LOOP)
|
||||
|
||||
/* Flash unlocking keys */
|
||||
#define KEY1 0x45670123
|
||||
#define KEY2 0xCDEF89AB
|
||||
|
||||
/* Lock bits for FLASH_CR register */
|
||||
#define PG (1<<0)
|
||||
#define PER (1<<1)
|
||||
#define OPTPG (1<<4)
|
||||
#define OPTER (1<<5)
|
||||
#define STRT (1<<6)
|
||||
#define CR_LOCK (1<<7)
|
||||
#define PRG_LOCK 0
|
||||
#define OPT_LOCK (1<<9)
|
||||
#include "panic.h"
|
||||
|
||||
/* Flag indicating whether we have locked down entire flash */
|
||||
static int entire_flash_locked;
|
||||
@@ -57,301 +27,14 @@ struct flash_wp_state {
|
||||
int entire_flash_locked;
|
||||
};
|
||||
|
||||
static int write_optb(int byte, uint8_t value);
|
||||
|
||||
static int wait_busy(void)
|
||||
{
|
||||
int timeout = FLASH_TIMEOUT_LOOP;
|
||||
while (STM32_FLASH_SR & (1 << 0) && timeout-- > 0)
|
||||
udelay(CYCLE_PER_FLASH_LOOP);
|
||||
return (timeout > 0) ? EC_SUCCESS : EC_ERROR_TIMEOUT;
|
||||
}
|
||||
|
||||
static int unlock(int locks)
|
||||
{
|
||||
/*
|
||||
* We may have already locked the flash module and get a bus fault
|
||||
* in the attempt to unlock. Need to disable bus fault handler now.
|
||||
*/
|
||||
ignore_bus_fault(1);
|
||||
|
||||
/* unlock CR if needed */
|
||||
if (STM32_FLASH_CR & CR_LOCK) {
|
||||
STM32_FLASH_KEYR = KEY1;
|
||||
STM32_FLASH_KEYR = KEY2;
|
||||
}
|
||||
/* unlock option memory if required */
|
||||
if ((locks & OPT_LOCK) && !(STM32_FLASH_CR & OPT_LOCK)) {
|
||||
STM32_FLASH_OPTKEYR = KEY1;
|
||||
STM32_FLASH_OPTKEYR = KEY2;
|
||||
}
|
||||
|
||||
/* Re-enable bus fault handler */
|
||||
ignore_bus_fault(0);
|
||||
|
||||
return ((STM32_FLASH_CR ^ OPT_LOCK) & (locks | CR_LOCK)) ?
|
||||
EC_ERROR_UNKNOWN : EC_SUCCESS;
|
||||
}
|
||||
|
||||
static void lock(void)
|
||||
{
|
||||
STM32_FLASH_CR = CR_LOCK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Option byte organization
|
||||
*
|
||||
* [31:24] [23:16] [15:8] [7:0]
|
||||
*
|
||||
* 0x1FFF_F800 nUSER USER nRDP RDP
|
||||
*
|
||||
* 0x1FFF_F804 nData1 Data1 nData0 Data0
|
||||
*
|
||||
* 0x1FFF_F808 nWRP1 WRP1 nWRP0 WRP0
|
||||
*
|
||||
* 0x1FFF_F80C nWRP3 WRP2 nWRP2 WRP2
|
||||
*
|
||||
* Note that the variable with n prefix means the complement.
|
||||
*/
|
||||
static uint8_t read_optb(int byte)
|
||||
{
|
||||
return *(uint8_t *)(STM32_OPTB_BASE + byte);
|
||||
}
|
||||
|
||||
static int erase_optb(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
rv = unlock(OPT_LOCK);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* Must be set in 2 separate lines. */
|
||||
STM32_FLASH_CR |= OPTER;
|
||||
STM32_FLASH_CR |= STRT;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
lock();
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Since the option byte erase is WHOLE erase, this function is to keep
|
||||
* rest of bytes, but make this byte 0xff.
|
||||
* Note that this could make a recursive call to write_optb().
|
||||
*/
|
||||
static int preserve_optb(int byte)
|
||||
{
|
||||
int i, rv;
|
||||
uint8_t optb[8];
|
||||
|
||||
/* The byte has been reset, no need to run preserve. */
|
||||
if (*(uint16_t *)(STM32_OPTB_BASE + byte) == 0xffff)
|
||||
return EC_SUCCESS;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(optb); ++i)
|
||||
optb[i] = read_optb(i * 2);
|
||||
|
||||
optb[byte / 2] = 0xff;
|
||||
|
||||
rv = erase_optb();
|
||||
if (rv)
|
||||
return rv;
|
||||
for (i = 0; i < ARRAY_SIZE(optb); ++i) {
|
||||
rv = write_optb(i * 2, optb[i]);
|
||||
if (rv)
|
||||
return rv;
|
||||
}
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int write_optb(int byte, uint8_t value)
|
||||
{
|
||||
volatile int16_t *hword = (uint16_t *)(STM32_OPTB_BASE + byte);
|
||||
int rv;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* The target byte is the value we want to write. */
|
||||
if (*(uint8_t *)hword == value)
|
||||
return EC_SUCCESS;
|
||||
|
||||
/* Try to erase that byte back to 0xff. */
|
||||
rv = preserve_optb(byte);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* The value is 0xff after erase. No need to write 0xff again. */
|
||||
if (value == 0xff)
|
||||
return EC_SUCCESS;
|
||||
|
||||
rv = unlock(OPT_LOCK);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
/* set OPTPG bit */
|
||||
STM32_FLASH_CR |= OPTPG;
|
||||
|
||||
*hword = value;
|
||||
|
||||
/* reset OPTPG bit */
|
||||
STM32_FLASH_CR &= ~OPTPG;
|
||||
|
||||
rv = wait_busy();
|
||||
if (rv)
|
||||
return rv;
|
||||
lock();
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Physical layer APIs */
|
||||
|
||||
int flash_physical_write(int offset, int size, const char *data)
|
||||
{
|
||||
uint16_t *address = (uint16_t *)(CONFIG_FLASH_BASE + offset);
|
||||
int res = EC_SUCCESS;
|
||||
int i;
|
||||
|
||||
if (unlock(PRG_LOCK) != EC_SUCCESS) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_wr;
|
||||
}
|
||||
|
||||
/* Clear previous error status */
|
||||
STM32_FLASH_SR = 0x34;
|
||||
|
||||
/* set PG bit */
|
||||
STM32_FLASH_CR |= PG;
|
||||
|
||||
for (; size > 0; size -= sizeof(uint16_t)) {
|
||||
/*
|
||||
* Reload the watchdog timer to avoid watchdog reset when doing
|
||||
* long writing with interrupt disabled.
|
||||
*/
|
||||
watchdog_reload();
|
||||
|
||||
/* wait to be ready */
|
||||
for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
|
||||
i++)
|
||||
;
|
||||
|
||||
/* write the half word */
|
||||
*address++ = data[0] + (data[1] << 8);
|
||||
data += 2;
|
||||
|
||||
/* Wait for writes to complete */
|
||||
for (i = 0; (STM32_FLASH_SR & 1) && (i < FLASH_TIMEOUT_LOOP);
|
||||
i++)
|
||||
;
|
||||
|
||||
if (STM32_FLASH_SR & 1) {
|
||||
res = EC_ERROR_TIMEOUT;
|
||||
goto exit_wr;
|
||||
}
|
||||
|
||||
/* Check for error conditions - erase failed, voltage error,
|
||||
* protection error */
|
||||
if (STM32_FLASH_SR & 0x14) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_wr;
|
||||
}
|
||||
}
|
||||
|
||||
exit_wr:
|
||||
/* Disable PG bit */
|
||||
STM32_FLASH_CR &= ~PG;
|
||||
|
||||
lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int flash_physical_erase(int offset, int size)
|
||||
{
|
||||
int res = EC_SUCCESS;
|
||||
|
||||
if (unlock(PRG_LOCK) != EC_SUCCESS)
|
||||
return EC_ERROR_UNKNOWN;
|
||||
|
||||
/* Clear previous error status */
|
||||
STM32_FLASH_SR = 0x34;
|
||||
|
||||
/* set PER bit */
|
||||
STM32_FLASH_CR |= PER;
|
||||
|
||||
for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
|
||||
offset += CONFIG_FLASH_ERASE_SIZE) {
|
||||
timestamp_t deadline;
|
||||
|
||||
/* Do nothing if already erased */
|
||||
if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE))
|
||||
continue;
|
||||
|
||||
/* select page to erase */
|
||||
STM32_FLASH_AR = CONFIG_FLASH_BASE + offset;
|
||||
|
||||
/* set STRT bit : start erase */
|
||||
STM32_FLASH_CR |= STRT;
|
||||
|
||||
/*
|
||||
* Reload the watchdog timer to avoid watchdog reset during a
|
||||
* long erase operation.
|
||||
*/
|
||||
watchdog_reload();
|
||||
|
||||
deadline.val = get_time().val + FLASH_TIMEOUT_US;
|
||||
/* Wait for erase to complete */
|
||||
while ((STM32_FLASH_SR & 1) &&
|
||||
(get_time().val < deadline.val)) {
|
||||
usleep(300);
|
||||
}
|
||||
if (STM32_FLASH_SR & 1) {
|
||||
res = EC_ERROR_TIMEOUT;
|
||||
goto exit_er;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for error conditions - erase failed, voltage error,
|
||||
* protection error
|
||||
*/
|
||||
if (STM32_FLASH_SR & 0x14) {
|
||||
res = EC_ERROR_UNKNOWN;
|
||||
goto exit_er;
|
||||
}
|
||||
}
|
||||
|
||||
exit_er:
|
||||
/* reset PER bit */
|
||||
STM32_FLASH_CR &= ~PER;
|
||||
|
||||
lock();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int flash_physical_get_protect(int block)
|
||||
{
|
||||
return entire_flash_locked || !(STM32_FLASH_WRPR & (1 << block));
|
||||
}
|
||||
|
||||
static int flash_physical_get_protect_at_boot(int block)
|
||||
{
|
||||
uint8_t val = read_optb(STM32_OPTB_WRP_OFF(block/8));
|
||||
return (!(val & (1 << (block % 8)))) ? 1 : 0;
|
||||
}
|
||||
|
||||
uint32_t flash_physical_get_protect_flags(void)
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
@@ -363,32 +46,6 @@ uint32_t flash_physical_get_protect_flags(void)
|
||||
return flags;
|
||||
}
|
||||
|
||||
int flash_physical_protect_ro_at_boot(int enable)
|
||||
{
|
||||
int block;
|
||||
int i;
|
||||
int original_val[4], val[4];
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
original_val[i] = val[i] = read_optb(i * 2 + 8);
|
||||
|
||||
for (block = RO_BANK_OFFSET;
|
||||
block < RO_BANK_OFFSET + RO_BANK_COUNT + PSTATE_BANK_COUNT;
|
||||
block++) {
|
||||
int byte_off = STM32_OPTB_WRP_OFF(block/8) / 2 - 4;
|
||||
if (enable)
|
||||
val[byte_off] = val[byte_off] & (~(1 << (block % 8)));
|
||||
else
|
||||
val[byte_off] = val[byte_off] | (1 << (block % 8));
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; ++i)
|
||||
if (original_val[i] != val[i])
|
||||
write_optb(i * 2 + 8, val[i]);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
int flash_physical_protect_now(int all)
|
||||
{
|
||||
if (all) {
|
||||
@@ -410,44 +67,11 @@ int flash_physical_protect_now(int all)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if write protect register state is inconsistent with RO_AT_BOOT and
|
||||
* ALL_AT_BOOT state.
|
||||
*
|
||||
* @return zero if consistent, non-zero if inconsistent.
|
||||
*/
|
||||
static int registers_need_reset(void)
|
||||
{
|
||||
uint32_t flags = flash_get_protect();
|
||||
int i;
|
||||
int ro_at_boot = (flags & EC_FLASH_PROTECT_RO_AT_BOOT) ? 1 : 0;
|
||||
int ro_wp_region_start = RO_BANK_OFFSET;
|
||||
int ro_wp_region_end =
|
||||
RO_BANK_OFFSET + RO_BANK_COUNT + PSTATE_BANK_COUNT;
|
||||
|
||||
for (i = ro_wp_region_start; i < ro_wp_region_end; i++)
|
||||
if (flash_physical_get_protect_at_boot(i) != ro_at_boot)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unprotect_all_blocks(void)
|
||||
{
|
||||
int i;
|
||||
for (i = 4; i < 8; ++i)
|
||||
write_optb(i * 2, 0xff);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* High-level APIs */
|
||||
|
||||
int flash_pre_init(void)
|
||||
int flash_physical_restore_state(void)
|
||||
{
|
||||
uint32_t reset_flags = system_get_reset_flags();
|
||||
uint32_t prot_flags = flash_get_protect();
|
||||
int need_reset = 0;
|
||||
const struct flash_wp_state *prev;
|
||||
int version, size;
|
||||
const struct flash_wp_state *prev;
|
||||
|
||||
/*
|
||||
* If we have already jumped between images, an earlier image could
|
||||
@@ -459,50 +83,10 @@ int flash_pre_init(void)
|
||||
if (prev && version == FLASH_HOOK_VERSION &&
|
||||
size == sizeof(*prev))
|
||||
entire_flash_locked = prev->entire_flash_locked;
|
||||
return EC_SUCCESS;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) {
|
||||
if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
|
||||
!(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
|
||||
/*
|
||||
* Pstate wants RO protected at boot, but the write
|
||||
* protect register wasn't set to protect it. Force an
|
||||
* update to the write protect register and reboot so
|
||||
* it takes effect.
|
||||
*/
|
||||
flash_physical_protect_ro_at_boot(1);
|
||||
need_reset = 1;
|
||||
}
|
||||
|
||||
if (registers_need_reset()) {
|
||||
/*
|
||||
* Write protect register was in an inconsistent state.
|
||||
* Set it back to a good state and reboot.
|
||||
*
|
||||
* TODO(crosbug.com/p/23798): this seems really similar
|
||||
* to the check above. One of them should be able to
|
||||
* go away.
|
||||
*/
|
||||
flash_protect_ro_at_boot(
|
||||
prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT);
|
||||
need_reset = 1;
|
||||
}
|
||||
} else {
|
||||
if (prot_flags & EC_FLASH_PROTECT_RO_NOW) {
|
||||
/*
|
||||
* Write protect pin unasserted but some section is
|
||||
* protected. Drop it and reboot.
|
||||
*/
|
||||
unprotect_all_blocks();
|
||||
need_reset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (need_reset)
|
||||
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
|
||||
|
||||
return EC_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -316,18 +316,21 @@ int flash_physical_get_protect(int block)
|
||||
return STM32_FLASH_WRPR & (1 << block);
|
||||
}
|
||||
|
||||
int flash_physical_protect_ro_at_boot(int enable)
|
||||
int flash_physical_protect_at_boot(enum flash_wp_range range)
|
||||
{
|
||||
uint32_t prot;
|
||||
uint32_t mask = ((1 << (RO_BANK_COUNT + PSTATE_BANK_COUNT)) - 1)
|
||||
<< RO_BANK_OFFSET;
|
||||
int rv;
|
||||
|
||||
if (range == FLASH_WP_ALL)
|
||||
return EC_ERROR_UNIMPLEMENTED;
|
||||
|
||||
/* Read the current protection status */
|
||||
prot = read_optb_wrp();
|
||||
|
||||
/* Set/clear bits */
|
||||
if (enable)
|
||||
if (range == FLASH_WP_RO)
|
||||
prot |= mask;
|
||||
else
|
||||
prot &= ~mask;
|
||||
|
||||
@@ -188,7 +188,7 @@ int flash_protect_ro_at_boot(int enable)
|
||||
* This assumes PSTATE immediately follows RO, which it does on
|
||||
* all STM32 platforms (which are the only ones with this config).
|
||||
*/
|
||||
flash_physical_protect_ro_at_boot(new_flags);
|
||||
flash_physical_protect_at_boot(new_flags ? FLASH_WP_RO : FLASH_WP_NONE);
|
||||
#endif
|
||||
|
||||
return EC_SUCCESS;
|
||||
|
||||
@@ -28,6 +28,13 @@
|
||||
#define PSTATE_BANK (PSTATE_OFFSET / CONFIG_FLASH_BANK_SIZE)
|
||||
#define PSTATE_BANK_COUNT (PSTATE_SIZE / CONFIG_FLASH_BANK_SIZE)
|
||||
|
||||
/* Range of write protection */
|
||||
enum flash_wp_range {
|
||||
FLASH_WP_NONE = 0,
|
||||
FLASH_WP_RO,
|
||||
FLASH_WP_ALL,
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Low-level methods, for use by flash_common. */
|
||||
|
||||
@@ -70,12 +77,12 @@ int flash_physical_get_protect(int bank);
|
||||
uint32_t flash_physical_get_protect_flags(void);
|
||||
|
||||
/**
|
||||
* Enable/disable protecting RO firmware and pstate at boot.
|
||||
* Enable/disable protecting firmware/pstate at boot.
|
||||
*
|
||||
* @param enable Enable (non-zero) or disable (zero) protection
|
||||
* @param range The range to protect
|
||||
* @return non-zero if error.
|
||||
*/
|
||||
int flash_physical_protect_ro_at_boot(int enable);
|
||||
int flash_physical_protect_at_boot(enum flash_wp_range range);
|
||||
|
||||
/**
|
||||
* Protect flash now.
|
||||
@@ -96,6 +103,13 @@ int flash_physical_protect_now(int all);
|
||||
*/
|
||||
int flash_physical_force_reload(void);
|
||||
|
||||
/**
|
||||
* Restore flash physical layer state after sysjump.
|
||||
*
|
||||
* @return non-zero if restored.
|
||||
*/
|
||||
int flash_physical_restore_state(void);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Low-level common code for use by flash modules. */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user