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

679 lines
18 KiB
C
Raw Permalink Normal View History

2024-03-04 11:14:53 +01:00
/* Copyright 2015 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.
*/
#include "common.h"
#include "console.h"
#include "flash.h"
#include "flash_chip.h"
#include "host_command.h"
#include "intc.h"
#include "system.h"
#include "util.h"
#include "watchdog.h"
#include "registers.h"
#include "task.h"
#include "shared_mem.h"
#include "uart.h"
#define FLASH_DMA_START ((uint32_t) &__flash_dma_start)
#define FLASH_DMA_CODE __attribute__((section(".flash_direct_map")))
#define FLASH_SECTOR_ERASE_SIZE 0x00000400
/* Flash sector erase (1K bytes) command */
#define FLASH_CMD_SECTOR_ERASE 0xD7
/* Write status register */
#define FLASH_CMD_WRSR 0x01
/* Write disable */
#define FLASH_CMD_WRDI 0x04
/* Write enable */
#define FLASH_CMD_WREN 0x06
/* Read status register */
#define FLASH_CMD_RS 0x05
/* Auto address increment programming */
#define FLASH_CMD_AAI_WORD 0xAD
#define FLASH_TEXT_START ((uint32_t) &__flash_text_start)
/* The default tag index of immu. */
#define IMMU_TAG_INDEX_BY_DEFAULT 0x7E000
/* immu cache size is 8K bytes. */
#define IMMU_SIZE 0x2000
#if CONFIG_FLASH_SIZE == 0x80000
/* Apply workaround of the issue (b:111808417) */
#define IMMU_CACHE_TAG_INVALID
#endif
static int stuck_locked;
static int inconsistent_locked;
static int all_protected;
static int flash_dma_code_enabled;
#define FWP_REG(bank) (bank / 8)
#define FWP_MASK(bank) (1 << (bank % 8))
enum flash_wp_interface {
FLASH_WP_HOST = 0x01,
FLASH_WP_DBGR = 0x02,
FLASH_WP_EC = 0x04,
};
enum flash_wp_status {
FLASH_WP_STATUS_PROTECT_RO = EC_FLASH_PROTECT_RO_NOW,
FLASH_WP_STATUS_PROTECT_ALL = EC_FLASH_PROTECT_ALL_NOW,
};
enum flash_status_mask {
FLASH_SR_NO_BUSY = 0,
/* Internal write operation is in progress */
FLASH_SR_BUSY = 0x01,
/* Device is memory Write enabled */
FLASH_SR_WEL = 0x02,
FLASH_SR_ALL = (FLASH_SR_BUSY | FLASH_SR_WEL),
};
enum dlm_address_view {
SCAR0_ILM0_DLM13 = 0x8D000, /* DLM ~ 0x8DFFF H2RAM map LPC I/O */
SCAR1_ILM1_DLM11 = 0x8B000, /* DLM ~ 0x8BFFF ram 44K ~ 48K */
SCAR2_ILM2_DLM14 = 0x8E000, /* DLM ~ 0x8EFFF RO/RW flash code DMA */
SCAR3_ILM3_DLM6 = 0x86000, /* DLM ~ 0x86FFF ram 24K ~ 28K */
SCAR4_ILM4_DLM7 = 0x87000, /* DLM ~ 0x87FFF ram 28K ~ 32K */
SCAR5_ILM5_DLM8 = 0x88000, /* DLM ~ 0x88FFF ram 32K ~ 36K */
SCAR6_ILM6_DLM9 = 0x89000, /* DLM ~ 0x89FFF ram 36K ~ 40K */
SCAR7_ILM7_DLM10 = 0x8A000, /* DLM ~ 0x8AFFF ram 40K ~ 44K */
SCAR8_ILM8_DLM4 = 0x84000, /* DLM ~ 0x84FFF ram 16K ~ 20K */
SCAR9_ILM9_DLM5 = 0x85000, /* DLM ~ 0x85FFF ram 20K ~ 24K */
SCAR10_ILM10_DLM2 = 0x82000, /* DLM ~ 0x82FFF ram 8K ~ 12K */
SCAR11_ILM11_DLM3 = 0x83000, /* DLM ~ 0x83FFF ram 12K ~ 16K */
SCAR12_ILM12_DLM12 = 0x8C000, /* DLM ~ 0x8CFFF immu cache */
};
void FLASH_DMA_CODE dma_reset_immu(int fill_immu)
{
/* Immu tag sram reset */
IT83XX_GCTRL_MCCR |= 0x10;
/* Make sure the immu(dynamic cache) is reset */
data_serialization_barrier();
IT83XX_GCTRL_MCCR &= ~0x10;
data_serialization_barrier();
#ifdef IMMU_CACHE_TAG_INVALID
/*
* Workaround for (b:111808417):
* After immu reset, we will fill the immu cache with 8KB data
* that are outside address 0x7e000 ~ 0x7ffff.
* When CPU tries to fetch contents from address 0x7e000 ~ 0x7ffff,
* immu will re-fetch the missing contents inside 0x7e000 ~ 0x7ffff.
*/
if (fill_immu) {
volatile int immu __unused;
const uint32_t *ptr = (uint32_t *)FLASH_TEXT_START;
int i = 0;
while (i < IMMU_SIZE) {
immu = *ptr++;
i += sizeof(*ptr);
}
}
#endif
}
void FLASH_DMA_CODE dma_flash_follow_mode(void)
{
/*
* ECINDAR3-0 are EC-indirect memory address registers.
*
* Enter follow mode by writing 0xf to low nibble of ECINDAR3 register,
* and set high nibble as 0x4 to select internal flash.
*/
IT83XX_SMFI_ECINDAR3 = (EC_INDIRECT_READ_INTERNAL_FLASH | 0xf);
/* Set FSCE# as high level by writing 0 to address xfff_fe00h */
IT83XX_SMFI_ECINDAR2 = 0xFF;
IT83XX_SMFI_ECINDAR1 = 0xFE;
IT83XX_SMFI_ECINDAR0 = 0x00;
/* EC-indirect memory data register */
IT83XX_SMFI_ECINDDR = 0x00;
}
void FLASH_DMA_CODE dma_flash_follow_mode_exit(void)
{
/* Exit follow mode, and keep the setting of selecting internal flash */
IT83XX_SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
IT83XX_SMFI_ECINDAR2 = 0x00;
}
void FLASH_DMA_CODE dma_flash_fsce_high(void)
{
/* FSCE# high level */
IT83XX_SMFI_ECINDAR1 = 0xFE;
IT83XX_SMFI_ECINDDR = 0x00;
}
uint8_t FLASH_DMA_CODE dma_flash_read_dat(void)
{
/* Read data from FMISO */
return IT83XX_SMFI_ECINDDR;
}
void FLASH_DMA_CODE dma_flash_write_dat(uint8_t wdata)
{
/* Write data to FMOSI */
IT83XX_SMFI_ECINDDR = wdata;
}
void FLASH_DMA_CODE dma_flash_transaction(int wlen, uint8_t *wbuf,
int rlen, uint8_t *rbuf, int cmd_end)
{
int i;
/* FSCE# with low level */
IT83XX_SMFI_ECINDAR1 = 0xFD;
/* Write data to FMOSI */
for (i = 0; i < wlen; i++)
IT83XX_SMFI_ECINDDR = wbuf[i];
/* Read data from FMISO */
for (i = 0; i < rlen; i++)
rbuf[i] = IT83XX_SMFI_ECINDDR;
/* FSCE# high level if transaction done */
if (cmd_end)
dma_flash_fsce_high();
}
void FLASH_DMA_CODE dma_flash_cmd_read_status(enum flash_status_mask mask,
enum flash_status_mask target)
{
uint8_t status[1];
uint8_t cmd_rs[] = {FLASH_CMD_RS};
/*
* We prefer no timeout here. We can always get the status
* we want, or wait for watchdog triggered to check
* e-flash's status instead of breaking loop.
* This will avoid fetching unknown instruction from e-flash
* and causing exception.
*/
while (1) {
/* read status */
dma_flash_transaction(sizeof(cmd_rs), cmd_rs, 1, status, 1);
/* only bit[1:0] valid */
if ((status[0] & mask) == target)
break;
}
}
void FLASH_DMA_CODE dma_flash_cmd_write_enable(void)
{
uint8_t cmd_we[] = {FLASH_CMD_WREN};
/* enter EC-indirect follow mode */
dma_flash_follow_mode();
/* send write enable command */
dma_flash_transaction(sizeof(cmd_we), cmd_we, 0, NULL, 1);
/* read status and make sure busy bit cleared and write enabled. */
dma_flash_cmd_read_status(FLASH_SR_ALL, FLASH_SR_WEL);
/* exit EC-indirect follow mode */
dma_flash_follow_mode_exit();
}
void FLASH_DMA_CODE dma_flash_cmd_write_disable(void)
{
uint8_t cmd_wd[] = {FLASH_CMD_WRDI};
/* enter EC-indirect follow mode */
dma_flash_follow_mode();
/* send write disable command */
dma_flash_transaction(sizeof(cmd_wd), cmd_wd, 0, NULL, 1);
/* make sure busy bit cleared. */
dma_flash_cmd_read_status(FLASH_SR_ALL, FLASH_SR_NO_BUSY);
/* exit EC-indirect follow mode */
dma_flash_follow_mode_exit();
}
void FLASH_DMA_CODE dma_flash_cmd_erase(int addr, int cmd)
{
uint8_t cmd_erase[] = {cmd, ((addr >> 16) & 0xFF),
((addr >> 8) & 0xFF), (addr & 0xFF)};
/* enter EC-indirect follow mode */
dma_flash_follow_mode();
/* send erase command */
dma_flash_transaction(sizeof(cmd_erase), cmd_erase, 0, NULL, 1);
/* make sure busy bit cleared. */
dma_flash_cmd_read_status(FLASH_SR_BUSY, FLASH_SR_NO_BUSY);
/* exit EC-indirect follow mode */
dma_flash_follow_mode_exit();
}
void FLASH_DMA_CODE dma_flash_cmd_aai_write(int addr, int wlen, uint8_t *wbuf)
{
int i;
uint8_t aai_write[] = {FLASH_CMD_AAI_WORD, ((addr >> 16) & 0xFF),
((addr >> 8) & 0xFF), (addr & 0xFF)};
/* enter EC-indirect follow mode */
dma_flash_follow_mode();
/* send aai word command */
dma_flash_transaction(sizeof(aai_write), aai_write, 0, NULL, 0);
for (i = 0; i < wlen; i += 2) {
dma_flash_write_dat(wbuf[i]);
dma_flash_write_dat(wbuf[i + 1]);
dma_flash_fsce_high();
/* make sure busy bit cleared. */
dma_flash_cmd_read_status(FLASH_SR_BUSY, FLASH_SR_NO_BUSY);
/* resend aai word command without address field */
if ((i + 2) < wlen)
dma_flash_transaction(1, aai_write, 0, NULL, 0);
}
/* exit EC-indirect follow mode */
dma_flash_follow_mode_exit();
}
uint8_t FLASH_DMA_CODE dma_flash_indirect_fast_read(int addr)
{
IT83XX_SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
IT83XX_SMFI_ECINDAR2 = (addr >> 16) & 0xFF;
IT83XX_SMFI_ECINDAR1 = (addr >> 8) & 0xFF;
IT83XX_SMFI_ECINDAR0 = (addr & 0xFF);
return IT83XX_SMFI_ECINDDR;
}
int FLASH_DMA_CODE dma_flash_verify(int addr, int size, const char *data)
{
int i;
uint8_t *wbuf = (uint8_t *)data;
uint8_t *flash = (uint8_t *)addr;
/* verify for erase */
if (data == NULL) {
for (i = 0; i < size; i++) {
if (flash[i] != 0xFF)
return EC_ERROR_UNKNOWN;
}
/* verify for write */
} else {
for (i = 0; i < size; i++) {
if (flash[i] != wbuf[i])
return EC_ERROR_UNKNOWN;
}
}
return EC_SUCCESS;
}
void FLASH_DMA_CODE dma_flash_aai_write(int addr, int wlen, const char *wbuf)
{
dma_flash_cmd_write_enable();
dma_flash_cmd_aai_write(addr, wlen, (uint8_t *)wbuf);
dma_flash_cmd_write_disable();
}
void FLASH_DMA_CODE dma_flash_erase(int addr, int cmd)
{
dma_flash_cmd_write_enable();
dma_flash_cmd_erase(addr, cmd);
dma_flash_cmd_write_disable();
}
static enum flash_wp_status flash_check_wp(void)
{
enum flash_wp_status wp_status;
int all_bank_count, bank;
all_bank_count = CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE;
for (bank = 0; bank < all_bank_count; bank++) {
if (!(IT83XX_GCTRL_EWPR0PFEC(FWP_REG(bank)) & FWP_MASK(bank)))
break;
}
if (bank == WP_BANK_COUNT)
wp_status = FLASH_WP_STATUS_PROTECT_RO;
else if (bank == (WP_BANK_COUNT + PSTATE_BANK_COUNT))
wp_status = FLASH_WP_STATUS_PROTECT_RO;
else if (bank == all_bank_count)
wp_status = FLASH_WP_STATUS_PROTECT_ALL;
else
wp_status = 0;
return wp_status;
}
/**
* Protect flash banks until reboot.
*
* @param start_bank Start bank to protect
* @param bank_count Number of banks to protect
*/
static void flash_protect_banks(int start_bank,
int bank_count,
enum flash_wp_interface wp_if)
{
int bank;
for (bank = start_bank; bank < start_bank + bank_count; bank++) {
if (wp_if & FLASH_WP_EC)
IT83XX_GCTRL_EWPR0PFEC(FWP_REG(bank)) |= FWP_MASK(bank);
if (wp_if & FLASH_WP_HOST)
IT83XX_GCTRL_EWPR0PFH(FWP_REG(bank)) |= FWP_MASK(bank);
if (wp_if & FLASH_WP_DBGR)
IT83XX_GCTRL_EWPR0PFD(FWP_REG(bank)) |= FWP_MASK(bank);
}
}
int FLASH_DMA_CODE flash_physical_read(int offset, int size, char *data)
{
int i;
for (i = 0; i < size; i++) {
data[i] = dma_flash_indirect_fast_read(offset);
offset++;
}
return EC_SUCCESS;
}
/**
* Write to physical flash.
*
* Offset and size must be a multiple of CONFIG_FLASH_WRITE_SIZE.
*
* @param offset Flash offset to write.
* @param size Number of bytes to write.
* @param data Data to write to flash. Must be 32-bit aligned.
*/
int FLASH_DMA_CODE flash_physical_write(int offset, int size, const char *data)
{
int ret = EC_ERROR_UNKNOWN;
if (flash_dma_code_enabled == 0)
return EC_ERROR_ACCESS_DENIED;
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
watchdog_reload();
/*
* CPU can't fetch instruction from flash while use
* EC-indirect follow mode to access flash, interrupts need to be
* disabled.
*/
interrupt_disable();
dma_flash_aai_write(offset, size, data);
dma_reset_immu((offset + size) >= IMMU_TAG_INDEX_BY_DEFAULT);
ret = dma_flash_verify(offset, size, data);
interrupt_enable();
return ret;
}
/**
* Erase physical flash.
*
* Offset and size must be a multiple of CONFIG_FLASH_ERASE_SIZE.
*
* @param offset Flash offset to erase.
* @param size Number of bytes to erase.
*/
int FLASH_DMA_CODE flash_physical_erase(int offset, int size)
{
int v_size = size, v_addr = offset, ret = EC_ERROR_UNKNOWN;
if (flash_dma_code_enabled == 0)
return EC_ERROR_ACCESS_DENIED;
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
/*
* CPU can't fetch instruction from flash while use
* EC-indirect follow mode to access flash, interrupts need to be
* disabled.
*/
interrupt_disable();
/* Always use sector erase command (1K bytes) */
for (; size > 0; size -= FLASH_SECTOR_ERASE_SIZE) {
dma_flash_erase(offset, FLASH_CMD_SECTOR_ERASE);
offset += FLASH_SECTOR_ERASE_SIZE;
}
dma_reset_immu((v_addr + v_size) >= IMMU_TAG_INDEX_BY_DEFAULT);
ret = dma_flash_verify(v_addr, v_size, NULL);
interrupt_enable();
return ret;
}
/**
* Read physical write protect setting for a flash bank.
*
* @param bank Bank index to check.
* @return non-zero if bank is protected until reboot.
*/
int flash_physical_get_protect(int bank)
{
return IT83XX_GCTRL_EWPR0PFEC(FWP_REG(bank)) & FWP_MASK(bank);
}
/**
* Protect flash now.
*
* @param all Protect all (=1) or just read-only and pstate (=0).
* @return non-zero if error.
*/
int flash_physical_protect_now(int all)
{
if (all) {
/* Protect the entire flash */
flash_protect_banks(0,
CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE,
FLASH_WP_EC);
all_protected = 1;
} else {
/* Protect the read-only section and persistent state */
flash_protect_banks(WP_BANK_OFFSET,
WP_BANK_COUNT, FLASH_WP_EC);
#ifdef PSTATE_BANK
flash_protect_banks(PSTATE_BANK,
PSTATE_BANK_COUNT, FLASH_WP_EC);
#endif
}
/*
* bit[0], eflash protect lock register which can only be write 1 and
* only be cleared by power-on reset.
*/
IT83XX_GCTRL_EPLR |= 0x01;
return EC_SUCCESS;
}
/**
* Return flash protect state flags from the physical layer.
*
* This should only be called by flash_get_protect().
*
* Uses the EC_FLASH_PROTECT_* flags from ec_commands.h
*/
uint32_t flash_physical_get_protect_flags(void)
{
uint32_t flags = 0;
flags |= flash_check_wp();
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;
/* Check if flash protection is in inconsistent state at pre-init */
if (inconsistent_locked)
flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT;
return flags;
}
/**
* Return the valid flash protect flags.
*
* @return A combination of EC_FLASH_PROTECT_* flags from ec_commands.h
*/
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;
}
/**
* Return the writable flash protect flags.
*
* @param cur_flags The current flash protect flags.
* @return A combination of EC_FLASH_PROTECT_* flags from ec_commands.h
*/
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;
}
static void flash_code_static_dma(void)
{
/* Make sure no interrupt while enable static DMA */
interrupt_disable();
/* invalid static DMA first */
if (IS_ENABLED(CHIP_CORE_RISCV))
IT83XX_GCTRL_RVILMCR0 &= ~ILMCR_ILM2_ENABLE;
IT83XX_SMFI_SCAR2H = 0x08;
/* Copy to DLM */
IT83XX_GCTRL_MCCR2 |= 0x20;
memcpy((void *)CHIP_RAMCODE_BASE, (const void *)FLASH_DMA_START,
IT83XX_ILM_BLOCK_SIZE);
if (IS_ENABLED(CHIP_CORE_RISCV))
IT83XX_GCTRL_RVILMCR0 |= ILMCR_ILM2_ENABLE;
IT83XX_GCTRL_MCCR2 &= ~0x20;
/*
* Enable ILM
* Set the logic memory address(flash code of RO/RW) in eflash
* by programming the register SCARx bit19-bit0.
*/
IT83XX_SMFI_SCAR2L = FLASH_DMA_START & 0xFF;
IT83XX_SMFI_SCAR2M = (FLASH_DMA_START >> 8) & 0xFF;
IT83XX_SMFI_SCAR2H = (FLASH_DMA_START >> 16) & 0x0F;
/*
* Validate Direct-map SRAM function by programming
* register SCARx bit20=0
*/
IT83XX_SMFI_SCAR2H &= ~0x10;
flash_dma_code_enabled = 0x01;
interrupt_enable();
}
/**
* Initialize the module.
*
* Applies at-boot protection settings if necessary.
*/
int flash_pre_init(void)
{
int32_t reset_flags, prot_flags, unwanted_prot_flags;
/* By default, select internal flash for indirect fast read. */
IT83XX_SMFI_ECINDAR3 = EC_INDIRECT_READ_INTERNAL_FLASH;
flash_code_static_dma();
reset_flags = system_get_reset_flags();
prot_flags = flash_get_protect();
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) {
/* Protect the entire flash of host interface */
flash_protect_banks(0,
CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE,
FLASH_WP_HOST);
/* Protect the entire flash of DBGR interface */
flash_protect_banks(0,
CONFIG_FLASH_SIZE / CONFIG_FLASH_BANK_SIZE,
FLASH_WP_DBGR);
/*
* 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();
}
} 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;
} else {
/*
* Set inconsistent flag, because there is no software
* reset can clear write-protect.
*/
inconsistent_locked = 1;
return EC_ERROR_ACCESS_DENIED;
}
/* That doesn't return, so if we're still here that's an error */
return EC_ERROR_UNKNOWN;
}