coreboot-libre-fam15h-rdimm/3rdparty/chromeec/chip/lm4/flash.c

294 lines
7.2 KiB
C
Raw Normal View History

2024-03-04 11:14:53 +01:00
/* 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.
*/
/* Flash memory module for Chrome EC */
#include "flash.h"
#include "registers.h"
#include "switch.h"
#include "system.h"
#include "timer.h"
#include "util.h"
#include "watchdog.h"
#define FLASH_FWB_WORDS 32
#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
#define BANK_SHIFT 5 /* bank registers have 32bits each, 2^32 */
#define BANK_MASK (BIT(BANK_SHIFT) - 1) /* 5 bits */
#define F_BANK(b) ((b) >> BANK_SHIFT)
#define F_BIT(b) (1 << ((b) & BANK_MASK))
/* Flash timeouts. These are 2x the spec sheet max. */
#define ERASE_TIMEOUT_MS 200
#define WRITE_TIMEOUT_US 300
int stuck_locked; /* Is physical flash stuck protected? */
int all_protected; /* Has all-flash protection been requested? */
/**
* Protect flash banks until reboot.
*
* @param start_bank Start bank to protect
* @param bank_count Number of banks to protect
*/
static void protect_banks(int start_bank, int bank_count)
{
int bank;
for (bank = start_bank; bank < start_bank + bank_count; bank++)
LM4_FLASH_FMPPE[F_BANK(bank)] &= ~F_BIT(bank);
}
/**
* Perform a write-buffer operation. Buffer (FWB) and address (FMA) must be
* pre-loaded.
*
* @return EC_SUCCESS, or nonzero if error.
*/
static int write_buffer(void)
{
int t;
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
if (!LM4_FLASH_FWBVAL)
return EC_SUCCESS; /* Nothing to do */
/* Clear previous error status */
LM4_FLASH_FCMISC = LM4_FLASH_FCRIS;
/* Start write operation at page boundary */
LM4_FLASH_FMC2 = 0xa4420001;
/*
* Reload the watchdog timer, so that writing a large amount of flash
* doesn't cause a watchdog reset.
*/
watchdog_reload();
/* Wait for write to complete */
for (t = 0; LM4_FLASH_FMC2 & 0x01; t += 10) {
if (t > WRITE_TIMEOUT_US)
return EC_ERROR_TIMEOUT;
udelay(10);
}
/* Check for error conditions - program failed, erase needed,
* voltage error. */
if (LM4_FLASH_FCRIS & 0x2e01)
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
}
/*****************************************************************************/
/* Physical layer APIs */
int flash_physical_write(int offset, int size, const char *data)
{
const uint32_t *data32 = (const uint32_t *)data;
int rv;
int i;
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
/* Fail if offset, size, and data aren't at least word-aligned */
if ((offset | size | (uint32_t)(uintptr_t)data) & 3)
return EC_ERROR_INVAL;
/* Get initial write buffer index and page */
LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1);
i = (offset >> 2) & (FLASH_FWB_WORDS - 1);
/* Copy words into buffer */
for (; size > 0; size -= 4) {
LM4_FLASH_FWB[i++] = *data32++;
if (i == FLASH_FWB_WORDS) {
rv = write_buffer();
if (rv != EC_SUCCESS)
return rv;
/* Advance to next page */
i = 0;
LM4_FLASH_FMA += FLASH_FWB_BYTES;
}
}
/* Handle final partial page, if any */
if (i > 0)
return write_buffer();
return EC_SUCCESS;
}
int flash_physical_erase(int offset, int size)
{
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */
for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
offset += CONFIG_FLASH_ERASE_SIZE) {
int t;
/* Do nothing if already erased */
if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE))
continue;
LM4_FLASH_FMA = offset;
/*
* Reload the watchdog timer, so that erasing many flash pages
* doesn't cause a watchdog reset. May not need this now that
* we're using msleep() below.
*/
watchdog_reload();
/* Start erase */
LM4_FLASH_FMC = 0xa4420002;
/* Wait for erase to complete */
for (t = 0; LM4_FLASH_FMC & 0x02; t++) {
if (t > ERASE_TIMEOUT_MS)
return EC_ERROR_TIMEOUT;
msleep(1);
}
/* Check for error conditions - erase failed, voltage error,
* protection error */
if (LM4_FLASH_FCRIS & 0x0a01)
return EC_ERROR_UNKNOWN;
}
return EC_SUCCESS;
}
int flash_physical_get_protect(int bank)
{
return (LM4_FLASH_FMPPE[F_BANK(bank)] & F_BIT(bank)) ? 0 : 1;
}
uint32_t flash_physical_get_protect_flags(void)
{
uint32_t flags = 0;
/* Read all-protected state from our shadow copy */
if (all_protected)
flags |= EC_FLASH_PROTECT_ALL_NOW;
/* Check if blocks were stuck locked at pre-init */
if (stuck_locked)
flags |= EC_FLASH_PROTECT_ERROR_STUCK;
return flags;
}
int flash_physical_protect_now(int all)
{
if (all) {
/* Protect the entire flash */
all_protected = 1;
protect_banks(0, CONFIG_FLASH_SIZE /
CONFIG_FLASH_BANK_SIZE);
} else
/* Protect the WP region (read-only section and pstate) */
protect_banks(WP_BANK_OFFSET, WP_BANK_COUNT);
return EC_SUCCESS;
}
uint32_t flash_physical_get_valid_flags(void)
{
return EC_FLASH_PROTECT_RO_AT_BOOT |
EC_FLASH_PROTECT_RO_NOW |
EC_FLASH_PROTECT_ALL_NOW;
}
uint32_t flash_physical_get_writable_flags(uint32_t cur_flags)
{
uint32_t ret = 0;
/* If RO protection isn't enabled, its at-boot state can be changed. */
if (!(cur_flags & EC_FLASH_PROTECT_RO_NOW))
ret |= EC_FLASH_PROTECT_RO_AT_BOOT;
/*
* If entire flash isn't protected at this boot, it can be enabled if
* the WP GPIO is asserted.
*/
if (!(cur_flags & EC_FLASH_PROTECT_ALL_NOW) &&
(cur_flags & EC_FLASH_PROTECT_GPIO_ASSERTED))
ret |= EC_FLASH_PROTECT_ALL_NOW;
return ret;
}
/*****************************************************************************/
/* High-level APIs */
int flash_pre_init(void)
{
uint32_t reset_flags = system_get_reset_flags();
uint32_t prot_flags = flash_get_protect();
uint32_t unwanted_prot_flags = EC_FLASH_PROTECT_ALL_NOW |
EC_FLASH_PROTECT_ERROR_INCONSISTENT;
/*
* If we have already jumped between images, an earlier image could
* have applied write protection. Nothing additional needs to be done.
*/
if (reset_flags & EC_RESET_FLAG_SYSJUMP)
return EC_SUCCESS;
if (prot_flags & EC_FLASH_PROTECT_GPIO_ASSERTED) {
/*
* Write protect is asserted. If we want RO flash protected,
* protect it now.
*/
if ((prot_flags & EC_FLASH_PROTECT_RO_AT_BOOT) &&
!(prot_flags & EC_FLASH_PROTECT_RO_NOW)) {
int rv = flash_set_protect(EC_FLASH_PROTECT_RO_NOW,
EC_FLASH_PROTECT_RO_NOW);
if (rv)
return rv;
/* Re-read flags */
prot_flags = flash_get_protect();
}
/* Update all-now flag if all flash is protected */
if (prot_flags & EC_FLASH_PROTECT_ALL_NOW)
all_protected = 1;
} else {
/* Don't want RO flash protected */
unwanted_prot_flags |= EC_FLASH_PROTECT_RO_NOW;
}
/* If there are no unwanted flags, done */
if (!(prot_flags & unwanted_prot_flags))
return EC_SUCCESS;
/*
* If the last reboot was a power-on reset, it should have cleared
* write-protect. If it didn't, then the flash write protect registers
* have been permanently committed and we can't fix that.
*/
if (reset_flags & EC_RESET_FLAG_POWER_ON) {
stuck_locked = 1;
return EC_ERROR_ACCESS_DENIED;
}
/* Otherwise, do a hard boot to clear the flash protection registers */
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
/* That doesn't return, so if we're still here that's an error */
return EC_ERROR_UNKNOWN;
}