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

818 lines
18 KiB
C

/* 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 "flash.h"
#include "host_command.h"
#include "registers.h"
#include "spi_flash_reg.h"
#include "switch.h"
#include "system.h"
#include "timer.h"
#include "util.h"
#include "task.h"
#include "watchdog.h"
#include "console.h"
#include "hwtimer_chip.h"
static int all_protected; /* Has all-flash protection been requested? */
static int addr_prot_start;
static int addr_prot_length;
static uint8_t flag_prot_inconsistent;
/* SR regs aren't readable when UMA lock is on, so save a copy */
static uint8_t saved_sr1;
static uint8_t saved_sr2;
#ifdef CONFIG_EXTERNAL_STORAGE
#define TRISTATE_FLASH(x)
#else
#define TRISTATE_FLASH(x) flash_tristate(x)
#endif
/* Ensure only one task is accessing flash at a time. */
static struct mutex flash_lock;
/*****************************************************************************/
/* flash internal functions */
#if !defined(NPCX_INT_FLASH_SUPPORT)
static void flash_pinmux(int enable)
{
/* Select pin-mux for FIU*/
UPDATE_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_NO_F_SPI, !enable);
/* CS0/1 pinmux */
if (enable) {
#if (FIU_CHIP_SELECT == 1)
SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1);
#elif (FIU_CHIP_SELECT == 2)
SET_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2);
#endif
} else {
CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_1);
CLEAR_BIT(NPCX_DEVALT(0), NPCX_DEVALT0_F_SPI_CS1_2);
}
}
#endif
static void flash_execute_cmd(uint8_t code, uint8_t cts)
{
/* Flash mutex must be held while executing UMA commands. */
ASSERT(flash_lock.lock);
/* set UMA_CODE */
NPCX_UMA_CODE = code;
/* execute UMA flash transaction */
NPCX_UMA_CTS = cts;
while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE))
;
}
static void flash_cs_level(int level)
{
/* Set chip select to high/low level */
UPDATE_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1, level);
}
static int flash_wait_ready(void)
{
uint8_t mask = SPI_FLASH_SR1_BUSY;
const timestamp_t start = get_time();
const uint32_t timeout_us = 10 * SECOND;
const timestamp_t deadline = {
.val = start.val + timeout_us,
};
/* Chip Select down. */
flash_cs_level(0);
/* Command for Read status register */
flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_ONLY);
do {
/* Read status register */
NPCX_UMA_CTS = MASK_RD_1BYTE;
while (IS_BIT_SET(NPCX_UMA_CTS, NPCX_UMA_CTS_EXEC_DONE))
;
/* Busy bit is clear */
if ((NPCX_UMA_DB0 & mask) == 0)
break;
usleep(10);
} while (!timestamp_expired(deadline, NULL)); /* Wait for Busy clear */
/* Chip Select high. */
flash_cs_level(1);
if (timestamp_expired(deadline, NULL))
return EC_ERROR_TIMEOUT;
return EC_SUCCESS;
}
static int flash_write_enable(void)
{
uint8_t mask = SPI_FLASH_SR1_WEL;
int rv;
/* Wait for previous operation to complete */
rv = flash_wait_ready();
if (rv)
return rv;
/* Write enable command */
flash_execute_cmd(CMD_WRITE_EN, MASK_CMD_ONLY);
/* Wait for flash is not busy */
rv = flash_wait_ready();
if (rv)
return rv;
if (NPCX_UMA_DB0 & mask)
return EC_SUCCESS;
else
return EC_ERROR_BUSY;
}
static void flash_set_address(uint32_t dest_addr)
{
uint8_t *addr = (uint8_t *)&dest_addr;
/* Write address */
NPCX_UMA_AB2 = addr[2];
NPCX_UMA_AB1 = addr[1];
NPCX_UMA_AB0 = addr[0];
}
static uint8_t flash_get_status1(void)
{
uint8_t ret;
if (all_protected)
return saved_sr1;
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Read status register1 */
flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_RD_1BYTE);
/* Enable tri-state */
TRISTATE_FLASH(1);
ret = NPCX_UMA_DB0;
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
return ret;
}
static uint8_t flash_get_status2(void)
{
uint8_t ret;
if (all_protected)
return saved_sr2;
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Read status register2 */
flash_execute_cmd(CMD_READ_STATUS_REG2, MASK_CMD_RD_1BYTE);
/* Enable tri-state */
TRISTATE_FLASH(1);
ret = NPCX_UMA_DB0;
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
return ret;
}
#ifdef NPCX_INT_FLASH_SUPPORT
static int is_int_flash_protected(void)
{
return IS_BIT_SET(NPCX_DEV_CTL4, NPCX_DEV_CTL4_WP_IF);
}
static void flash_protect_int_flash(int enable)
{
/*
* Please notice the type of WP_IF bit is R/W1S. Once it's set,
* only rebooting EC can clear it.
*/
if (enable && !is_int_flash_protected())
SET_BIT(NPCX_DEV_CTL4, NPCX_DEV_CTL4_WP_IF);
}
#endif
#ifdef CONFIG_HOSTCMD_FLASH_SPI_INFO
void flash_get_mfr_dev_id(uint8_t *dest)
{
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Read manufacturer and device ID. Send cmd=0x90 + 24-bit address=0 */
flash_set_address(0);
flash_execute_cmd(CMD_READ_MAN_DEV_ID,
MASK_CMD_RD_2BYTE | MASK(A_SIZE));
/* Enable tri-state */
TRISTATE_FLASH(1);
dest[0] = NPCX_UMA_DB0;
dest[1] = NPCX_UMA_DB1;
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
}
#endif /* CONFIG_HOSTCMD_FLASH_SPI_INFO */
void flash_get_jedec_id(uint8_t *dest)
{
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Read manufacturer and device ID */
flash_execute_cmd(CMD_READ_ID, MASK_CMD_RD_3BYTE);
/* Enable tri-state */
TRISTATE_FLASH(1);
dest[0] = NPCX_UMA_DB0;
dest[1] = NPCX_UMA_DB1;
dest[2] = NPCX_UMA_DB2;
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
}
static void flash_uma_lock(int enable)
{
if (enable && !all_protected) {
/*
* Store SR1 / SR2 for later use since we're about to lock
* out all access (including read access) to these regs.
*/
saved_sr1 = flash_get_status1();
saved_sr2 = flash_get_status2();
}
all_protected = enable;
UPDATE_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK, enable);
}
static int flash_set_status_for_prot(int reg1, int reg2)
{
/*
* Writing SR regs will fail if our UMA lock is enabled. If WP
* is deasserted then remove the lock and allow the write.
*/
if (all_protected) {
#ifdef NPCX_INT_FLASH_SUPPORT
if (is_int_flash_protected())
return EC_ERROR_ACCESS_DENIED;
#endif
if (flash_get_protect() & EC_FLASH_PROTECT_GPIO_ASSERTED)
return EC_ERROR_ACCESS_DENIED;
flash_uma_lock(0);
}
/*
* If WP# is active and ec doesn't protect the status registers of
* internal spi-flash, protect it now before setting them.
*/
#ifdef NPCX_INT_FLASH_SUPPORT
flash_protect_int_flash(!gpio_get_level(GPIO_WP_L));
#endif
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Enable write */
flash_write_enable();
NPCX_UMA_DB0 = reg1;
NPCX_UMA_DB1 = reg2;
/* Write status register 1/2 */
flash_execute_cmd(CMD_WRITE_STATUS_REG, MASK_CMD_WR_2BYTE);
/* Enable tri-state */
TRISTATE_FLASH(1);
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
spi_flash_reg_to_protect(reg1, reg2,
&addr_prot_start, &addr_prot_length);
return EC_SUCCESS;
}
static int flash_check_prot_range(unsigned int offset, unsigned int bytes)
{
/* Invalid value */
if (offset + bytes > CONFIG_FLASH_SIZE)
return EC_ERROR_INVAL;
/* Check if ranges overlap */
if (MAX(addr_prot_start, offset) < MIN(addr_prot_start +
addr_prot_length, offset + bytes))
return EC_ERROR_ACCESS_DENIED;
return EC_SUCCESS;
}
static int flash_check_prot_reg(unsigned int offset, unsigned int bytes)
{
unsigned int start;
unsigned int len;
uint8_t sr1, sr2;
int rv = EC_SUCCESS;
/*
* If WP# is active and ec doesn't protect the status registers of
* internal spi-flash, protect it now.
*/
#ifdef NPCX_INT_FLASH_SUPPORT
flash_protect_int_flash(!gpio_get_level(GPIO_WP_L));
#endif
sr1 = flash_get_status1();
sr2 = flash_get_status2();
/* Invalid value */
if (offset + bytes > CONFIG_FLASH_SIZE)
return EC_ERROR_INVAL;
/* Compute current protect range */
rv = spi_flash_reg_to_protect(sr1, sr2, &start, &len);
if (rv)
return rv;
/* Check if ranges overlap */
if (MAX(start, offset) < MIN(start + len, offset + bytes))
return EC_ERROR_ACCESS_DENIED;
return EC_SUCCESS;
}
static int flash_write_prot_reg(unsigned int offset, unsigned int bytes,
int hw_protect)
{
int rv;
uint8_t sr1 = flash_get_status1();
uint8_t sr2 = flash_get_status2();
/* Invalid values */
if (offset + bytes > CONFIG_FLASH_SIZE)
return EC_ERROR_INVAL;
/* Compute desired protect range */
rv = spi_flash_protect_to_reg(offset, bytes, &sr1, &sr2);
if (rv)
return rv;
if (hw_protect)
sr1 |= SPI_FLASH_SR1_SRP0;
return flash_set_status_for_prot(sr1, sr2);
}
static void flash_burst_write(unsigned int dest_addr, unsigned int bytes,
const char *data)
{
unsigned int i;
/* Chip Select down */
flash_cs_level(0);
/* Set write address */
flash_set_address(dest_addr);
/* Start programming */
flash_execute_cmd(CMD_FLASH_PROGRAM, MASK_CMD_WR_ADR);
for (i = 0; i < bytes; i++) {
flash_execute_cmd(*data, MASK_CMD_WR_ONLY);
data++;
}
/* Chip Select up */
flash_cs_level(1);
}
static int flash_program_bytes(uint32_t offset, uint32_t bytes,
const uint8_t *data)
{
int write_size;
int rv;
while (bytes > 0) {
/* Write length can not go beyond the end of the flash page */
write_size = MIN(bytes, CONFIG_FLASH_WRITE_IDEAL_SIZE -
(offset & (CONFIG_FLASH_WRITE_IDEAL_SIZE - 1)));
/* Enable write */
rv = flash_write_enable();
if (rv)
return rv;
/* Burst UMA transaction */
flash_burst_write(offset, write_size, data);
/* Wait write completed */
rv = flash_wait_ready();
if (rv)
return rv;
data += write_size;
offset += write_size;
bytes -= write_size;
}
return rv;
}
/*****************************************************************************/
int flash_physical_read(int offset, int size, char *data)
{
int dest_addr = offset;
uint32_t idx;
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Chip Select down. */
flash_cs_level(0);
/* Set read address */
flash_set_address(dest_addr);
/* Start fast read - 1110 1001 - EXEC, WR, CMD, ADDR */
flash_execute_cmd(CMD_FAST_READ, MASK_CMD_ADR_WR);
/* Burst read transaction */
for (idx = 0; idx < size; idx++) {
/* 1101 0101 - EXEC, RD, NO CMD, NO ADDR, 4 bytes */
NPCX_UMA_CTS = MASK_RD_1BYTE;
/* wait for UMA to complete */
while (IS_BIT_SET(NPCX_UMA_CTS, EXEC_DONE))
;
/* Get read transaction results*/
data[idx] = NPCX_UMA_DB0;
}
/* Chip Select up */
flash_cs_level(1);
/* Enable tri-state */
TRISTATE_FLASH(1);
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
return EC_SUCCESS;
}
int flash_physical_write(int offset, int size, const char *data)
{
int dest_addr = offset;
int write_len;
int rv;
/* Fail if offset, size, and data aren't at least word-aligned */
if ((offset | size
| (uint32_t)(uintptr_t)data) & (CONFIG_FLASH_WRITE_SIZE - 1))
return EC_ERROR_INVAL;
/* check protection */
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
while (size > 0) {
/* First write multiples of 256, then (size % 256) last */
write_len = ((size % CONFIG_FLASH_WRITE_IDEAL_SIZE) == size) ?
size : CONFIG_FLASH_WRITE_IDEAL_SIZE;
/* check protection */
if (flash_check_prot_range(dest_addr, write_len)) {
rv = EC_ERROR_ACCESS_DENIED;
break;
}
rv = flash_program_bytes(dest_addr, write_len, data);
if (rv)
break;
data += write_len;
dest_addr += write_len;
size -= write_len;
}
/* Enable tri-state */
TRISTATE_FLASH(1);
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
return rv;
}
int flash_physical_erase(int offset, int size)
{
int rv = EC_SUCCESS;
/* check protection */
if (all_protected)
return EC_ERROR_ACCESS_DENIED;
/* Lock physical flash operations */
flash_lock_mapped_storage(1);
/* Disable tri-state */
TRISTATE_FLASH(0);
/* Alignment has been checked in upper layer */
for (; size > 0; size -= CONFIG_FLASH_ERASE_SIZE,
offset += CONFIG_FLASH_ERASE_SIZE) {
/* check protection */
if (flash_check_prot_range(offset, CONFIG_FLASH_ERASE_SIZE)) {
rv = EC_ERROR_ACCESS_DENIED;
break;
}
/*
* 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();
/* Enable write */
rv = flash_write_enable();
if (rv)
break;
/* Set erase address */
flash_set_address(offset);
/* Start erase */
flash_execute_cmd(NPCX_ERASE_COMMAND, MASK_CMD_ADR);
/* Wait erase completed */
rv = flash_wait_ready();
if (rv)
break;
}
/* Enable tri-state */
TRISTATE_FLASH(1);
/* Unlock physical flash operations */
flash_lock_mapped_storage(0);
return rv;
}
int flash_physical_get_protect(int bank)
{
uint32_t addr = bank * CONFIG_FLASH_BANK_SIZE;
return flash_check_prot_reg(addr, CONFIG_FLASH_BANK_SIZE);
}
uint32_t flash_physical_get_protect_flags(void)
{
uint32_t flags = 0;
/* Check if WP region is protected in status register */
if (flash_check_prot_reg(WP_BANK_OFFSET*CONFIG_FLASH_BANK_SIZE,
WP_BANK_COUNT*CONFIG_FLASH_BANK_SIZE))
flags |= EC_FLASH_PROTECT_RO_AT_BOOT;
/*
* TODO: If status register protects a range, but SRP0 is not set,
* flags should indicate EC_FLASH_PROTECT_ERROR_INCONSISTENT.
*/
if (flag_prot_inconsistent)
flags |= EC_FLASH_PROTECT_ERROR_INCONSISTENT;
/* Read all-protected state from our shadow copy */
if (all_protected)
flags |= EC_FLASH_PROTECT_ALL_NOW;
return flags;
}
int flash_physical_protect_now(int all)
{
if (all) {
/*
* Set UMA_LOCK bit for locking all UMA transaction.
* But we still can read directly from flash mapping address
*/
flash_uma_lock(1);
} else {
/* TODO: Implement RO "now" protection */
}
return EC_SUCCESS;
}
int flash_physical_protect_at_boot(uint32_t new_flags)
{
int ret;
if ((new_flags & (EC_FLASH_PROTECT_RO_AT_BOOT |
EC_FLASH_PROTECT_ALL_AT_BOOT)) == 0) {
/* Clear protection bits in status register */
return flash_set_status_for_prot(0, 0);
}
ret = flash_write_prot_reg(CONFIG_WP_STORAGE_OFF,
CONFIG_WP_STORAGE_SIZE,
1);
/*
* Set UMA_LOCK bit for locking all UMA transaction.
* But we still can read directly from flash mapping address
*/
if (new_flags & EC_FLASH_PROTECT_ALL_AT_BOOT)
flash_uma_lock(1);
return ret;
}
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)
{
/*
* Protect status registers of internal spi-flash if WP# is active
* during ec initialization.
*/
#ifdef NPCX_INT_FLASH_SUPPORT
flash_protect_int_flash(!gpio_get_level(GPIO_WP_L));
#endif
#if !defined(NPCX_INT_FLASH_SUPPORT)
/* Enable FIU interface */
flash_pinmux(1);
#endif
#if defined(CONFIG_EXTERNAL_STORAGE) && !defined(NPCX_INT_FLASH_SUPPORT)
/* Disable tristate all the time */
CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS);
#endif
/* Initialize UMA to unlocked */
flash_uma_lock(0);
return EC_SUCCESS;
}
void flash_lock_mapped_storage(int lock)
{
if (lock)
mutex_lock(&flash_lock);
else
mutex_unlock(&flash_lock);
}
/*****************************************************************************/
/* Host commands */
#if defined(CONFIG_HOSTCMD_FLASH_SPI_INFO) && !defined(BOARD_NPCX_EVB)
/* NPCX EVB uses implementation from spi_flash.c */
static enum ec_status flash_command_spi_info(struct host_cmd_handler_args *args)
{
struct ec_response_flash_spi_info *r = args->response;
flash_get_jedec_id(r->jedec);
r->reserved0 = 0;
flash_get_mfr_dev_id(r->mfr_dev_id);
r->sr1 = flash_get_status1();
r->sr2 = flash_get_status2();
args->response_size = sizeof(*r);
return EC_RES_SUCCESS;
}
DECLARE_HOST_COMMAND(EC_CMD_FLASH_SPI_INFO,
flash_command_spi_info,
EC_VER_MASK(0));
#endif
#ifdef CONFIG_CMD_FLASH_TRISTATE
#ifdef NPCX_INT_FLASH_SUPPORT
#error "Flash tristate is not relevant when internal flash is used."
#endif
static void flash_tristate(int enable)
{
/* Enable/Disable FIU pins to tri-state */
UPDATE_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS, enable);
}
static int flash_spi_sel_lock(int enable)
{
/*
* F_SPI_QUAD, F_SPI_CS1_1/2, F_SPI_TRIS become read-only
* if this bit is set
*/
UPDATE_BIT(NPCX_DEV_CTL4, NPCX_DEV_CTL4_F_SPI_SLLK, enable);
return IS_BIT_SET(NPCX_DEV_CTL4, NPCX_DEV_CTL4_F_SPI_SLLK);
}
/*****************************************************************************/
/* Console commands */
static int command_flash_spi_sel_lock(int argc, char **argv)
{
int ena;
if (argc > 1) {
if (!parse_bool(argv[1], &ena))
return EC_ERROR_PARAM1;
ena = flash_spi_sel_lock(ena);
ccprintf("Enabled: %d\n", ena);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flash_spi_lock, command_flash_spi_sel_lock,
"[on | off]",
"Lock spi flash interface selection");
static int command_flash_tristate(int argc, char **argv)
{
int ena;
if (argc > 1) {
if (!parse_bool(argv[1], &ena))
return EC_ERROR_PARAM1;
flash_tristate(ena);
ccprintf("Enabled: %d\n", ena);
}
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flash_tristate, command_flash_tristate,
"[on | off]",
"Tristate spi flash pins");
#endif /* CONFIG_CMD_FLASH_TRISTATE */
static int command_flash_chip(int argc, char **argv)
{
uint8_t jedec_id[3];
ccprintf("Status 1: 0x%02x, Status 2: 0x%02x\n", flash_get_status1(),
flash_get_status2());
flash_get_jedec_id(jedec_id);
ccprintf("Manufacturer: 0x%02x, DID: 0x%02x%02x\n", jedec_id[0],
jedec_id[1], jedec_id[2]);
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(flashchip, command_flash_chip,
NULL,
"Print flash chip info");