818 lines
18 KiB
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");
|