mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 02:20:48 +00:00
Now that HOOK_INIT hooks are called from a task switching context, most calls to task_start_called() should no longer be needed. This commit removes them. BRANCH=None BUG=chrome-os-partner:27226 TEST=make -j buildall tests TEST=Flash EC image onto samus and verify EC boot, AP boot, keyboard, lid, and tap-for-battery all functional. TEST=Flash EC image onto samus_pd and verify charging still works. TEST=Flash EC image onto ryu(P3) and verify that EC boot. TEST=Added ASSERT(task_start_called()) to the places where I removed task_start_called(). Booted samus, samus_pd, cyan, and ryu with AC inserted and verified that no ASSERT's were hit upon boot. Change-Id: Ic12c61862e85ca3a0a295beedbb4eeee6d5e515b Signed-off-by: Aseda Aboagye <aaboagye@google.com> Reviewed-on: https://chromium-review.googlesource.com/285635 Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Shawn N <shawnn@chromium.org> Commit-Queue: Aseda Aboagye <aaboagye@chromium.org> Trybot-Ready: Aseda Aboagye <aaboagye@chromium.org>
776 lines
18 KiB
C
776 lines
18 KiB
C
/* Copyright (c) 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 "registers.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"
|
|
|
|
int all_protected; /* Has all-flash protection been requested? */
|
|
int addr_prot_start;
|
|
int addr_prot_length;
|
|
uint8_t flag_prot_inconsistent;
|
|
|
|
#define FLASH_ABORT_TIMEOUT 10000
|
|
|
|
#ifdef CONFIG_CODERAM_ARCH
|
|
#define TRISTATE_FLASH(x)
|
|
#else
|
|
#define TRISTATE_FLASH(x) flash_tristate(x)
|
|
#endif
|
|
/*****************************************************************************/
|
|
/* flash internal functions */
|
|
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);
|
|
}
|
|
}
|
|
|
|
void flash_tristate(int enable)
|
|
{
|
|
/* Enable/Disable FIU pins to tri-state */
|
|
UPDATE_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS, enable);
|
|
}
|
|
|
|
void flash_execute_cmd(uint8_t code, uint8_t cts)
|
|
{
|
|
/* 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))
|
|
;
|
|
}
|
|
|
|
void flash_cs_level(int level)
|
|
{
|
|
/* Set chip select to high/low level */
|
|
UPDATE_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_SW_CS1, level);
|
|
}
|
|
|
|
void flash_wait_ready(void)
|
|
{
|
|
uint8_t mask = SPI_FLASH_SR1_BUSY;
|
|
uint16_t timeout = FLASH_ABORT_TIMEOUT;
|
|
|
|
/* Chip Select down. */
|
|
flash_cs_level(0);
|
|
/* Command for Read status register */
|
|
flash_execute_cmd(CMD_READ_STATUS_REG, MASK_CMD_ONLY);
|
|
while (--timeout) {
|
|
/* 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;
|
|
|
|
msleep(1);
|
|
}; /* Wait for Busy clear */
|
|
/* Chip Select high. */
|
|
flash_cs_level(1);
|
|
}
|
|
|
|
int flash_write_enable(void)
|
|
{
|
|
uint8_t mask = SPI_FLASH_SR1_WEL;
|
|
/* Write enable command */
|
|
flash_execute_cmd(CMD_WRITE_EN, MASK_CMD_ONLY);
|
|
/* Wait for flash is not busy */
|
|
flash_wait_ready();
|
|
|
|
if (NPCX_UMA_DB0 & mask)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
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];
|
|
}
|
|
|
|
uint8_t flash_get_status1(void)
|
|
{
|
|
/* 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);
|
|
return NPCX_UMA_DB0;
|
|
}
|
|
|
|
uint8_t flash_get_status2(void)
|
|
{
|
|
/* 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);
|
|
return NPCX_UMA_DB0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* flash protection functions */
|
|
/* Use a copy function of spi_flash.c in flash driver */
|
|
/**
|
|
* Computes block write protection range from registers
|
|
* Returns start == len == 0 for no protection
|
|
*
|
|
* @param sr1 Status register 1
|
|
* @param sr2 Status register 2
|
|
* @param start Output pointer for protection start offset
|
|
* @param len Output pointer for protection length
|
|
*
|
|
* @return EC_SUCCESS, or non-zero if any error.
|
|
*/
|
|
static int reg_to_protect(uint8_t sr1, uint8_t sr2, unsigned int *start,
|
|
unsigned int *len)
|
|
{
|
|
int blocks;
|
|
int size;
|
|
uint8_t cmp;
|
|
uint8_t sec;
|
|
uint8_t tb;
|
|
uint8_t bp;
|
|
|
|
/* Determine flags */
|
|
cmp = (sr2 & SPI_FLASH_SR2_CMP) ? 1 : 0;
|
|
sec = (sr1 & SPI_FLASH_SR1_SEC) ? 1 : 0;
|
|
tb = (sr1 & SPI_FLASH_SR1_TB) ? 1 : 0;
|
|
bp = (sr1 & (SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0))
|
|
>> 2;
|
|
|
|
/* Bad pointers or invalid data */
|
|
if (!start || !len || sr1 == -1 || sr2 == -1)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Not defined by datasheet */
|
|
if (sec && bp == 6)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Determine granularity (4kb sector or 64kb block) */
|
|
/* Computation using 2 * 1024 is correct */
|
|
size = sec ? (2 * 1024) : (64 * 1024);
|
|
|
|
/* Determine number of blocks */
|
|
/* Equivalent to pow(2, bp) with pow(2, 0) = 0 */
|
|
blocks = bp ? (1 << bp) : 0;
|
|
/* Datasheet specifies don't care for BP == 4, BP == 5 */
|
|
if (sec && bp == 5)
|
|
blocks = (1 << 4);
|
|
|
|
/* Determine number of bytes */
|
|
*len = size * blocks;
|
|
|
|
/* Determine bottom/top of memory to protect */
|
|
*start = tb ? 0 :
|
|
(CONFIG_FLASH_SIZE - *len) % CONFIG_FLASH_SIZE;
|
|
|
|
/* Reverse computations if complement set */
|
|
if (cmp) {
|
|
*start = (*start + *len) % CONFIG_FLASH_SIZE;
|
|
*len = CONFIG_FLASH_SIZE - *len;
|
|
}
|
|
|
|
/*
|
|
* If SRP0 is not set, flash is not protected because status register
|
|
* can be rewritten.
|
|
*/
|
|
if (!(sr1 & SPI_FLASH_SR1_SRP0)) {
|
|
/* Set protection inconsistent if len != 0*/
|
|
if (*len != 0)
|
|
flag_prot_inconsistent = 1;
|
|
*start = *len = 0;
|
|
return EC_SUCCESS;
|
|
}
|
|
/* Flag for checking protection inconsistent */
|
|
flag_prot_inconsistent = 0;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* Computes block write protection registers from range
|
|
*
|
|
* @param start Desired protection start offset
|
|
* @param len Desired protection length
|
|
* @param sr1 Output pointer for status register 1
|
|
* @param sr2 Output pointer for status register 2
|
|
*
|
|
* @return EC_SUCCESS, or non-zero if any error.
|
|
*/
|
|
static int protect_to_reg(unsigned int start, unsigned int len,
|
|
uint8_t *sr1, uint8_t *sr2)
|
|
{
|
|
char cmp = 0;
|
|
char sec = 0;
|
|
char tb = 0;
|
|
char bp = 0;
|
|
int blocks;
|
|
int size;
|
|
|
|
/* Bad pointers */
|
|
if (!sr1 || !sr2 || *sr1 == -1 || *sr2 == -1)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Invalid data */
|
|
if ((start && !len) || start + len > CONFIG_FLASH_SIZE)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Set complement bit based on whether length is power of 2 */
|
|
if ((len & (len - 1)) != 0) {
|
|
cmp = 1;
|
|
start = (start + len) % CONFIG_FLASH_SIZE;
|
|
len = CONFIG_FLASH_SIZE - len;
|
|
}
|
|
|
|
/* Set bottom/top bit based on start address */
|
|
/* Do not set if len == 0 or len == CONFIG_FLASH_SIZE */
|
|
if (!start && (len % CONFIG_FLASH_SIZE))
|
|
tb = 1;
|
|
|
|
/* Set sector bit and determine block length based on protect length */
|
|
if (len == 0 || len >= 128 * 1024) {
|
|
sec = 0;
|
|
size = 64 * 1024;
|
|
} else if (len >= 4 * 1024 && len <= 32 * 1024) {
|
|
sec = 1;
|
|
size = 2 * 1024;
|
|
} else
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Determine number of blocks */
|
|
if (len % size != 0)
|
|
return EC_ERROR_INVAL;
|
|
blocks = len / size;
|
|
|
|
/* Determine bp = log2(blocks) with log2(0) = 0 */
|
|
bp = blocks ? (31 - __builtin_clz(blocks)) : 0;
|
|
|
|
/* Clear bits */
|
|
*sr1 &= ~(SPI_FLASH_SR1_SEC | SPI_FLASH_SR1_TB
|
|
| SPI_FLASH_SR1_BP2 | SPI_FLASH_SR1_BP1 | SPI_FLASH_SR1_BP0);
|
|
*sr2 &= ~SPI_FLASH_SR2_CMP;
|
|
|
|
/* Set bits */
|
|
*sr1 |= (sec ? SPI_FLASH_SR1_SEC : 0) | (tb ? SPI_FLASH_SR1_TB : 0)
|
|
| (bp << 2);
|
|
*sr2 |= (cmp ? SPI_FLASH_SR2_CMP : 0);
|
|
|
|
/* Set SRP0 so status register can't be changed */
|
|
*sr1 |= SPI_FLASH_SR1_SRP0;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_set_status_for_prot(int reg1, int reg2)
|
|
{
|
|
/* 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);
|
|
|
|
reg_to_protect(reg1, reg2, &addr_prot_start, &addr_prot_length);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_check_prot_range(unsigned int offset, unsigned int bytes)
|
|
{
|
|
/* Invalid value */
|
|
if (offset + bytes > CONFIG_FLASH_PHYSICAL_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;
|
|
}
|
|
|
|
int flash_check_prot_reg(unsigned int offset, unsigned int bytes)
|
|
{
|
|
unsigned int start;
|
|
unsigned int len;
|
|
uint8_t sr1 = 0, sr2 = 0;
|
|
int rv = EC_SUCCESS;
|
|
|
|
sr1 = flash_get_status1();
|
|
sr2 = flash_get_status2();
|
|
|
|
/* Invalid value */
|
|
if (offset + bytes > CONFIG_FLASH_PHYSICAL_SIZE)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* Compute current protect range */
|
|
rv = 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;
|
|
|
|
}
|
|
|
|
int flash_write_prot_reg(unsigned int offset, unsigned int bytes)
|
|
{
|
|
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 = protect_to_reg(offset, bytes, &sr1, &sr2);
|
|
if (rv)
|
|
return rv;
|
|
|
|
return flash_set_status_for_prot(sr1, sr2);
|
|
}
|
|
|
|
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 erase address */
|
|
flash_set_address(dest_addr);
|
|
/* Start write */
|
|
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);
|
|
}
|
|
|
|
int flash_uma_lock(int enable)
|
|
{
|
|
UPDATE_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK, enable);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* Physical layer APIs */
|
|
|
|
int flash_physical_read(int offset, int size, char *data)
|
|
{
|
|
int dest_addr = offset;
|
|
uint32_t idx;
|
|
|
|
/* 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);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_physical_read_image_size(int offset, int size)
|
|
{
|
|
int dest_addr = offset;
|
|
uint8_t temp;
|
|
uint32_t idx;
|
|
uint32_t image_size = 0;
|
|
|
|
/* 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))
|
|
;
|
|
/* Find eof of image */
|
|
temp = NPCX_UMA_DB0;
|
|
if (temp == 0xea)
|
|
image_size = idx;
|
|
}
|
|
|
|
/* Chip Select up */
|
|
flash_cs_level(1);
|
|
/* Enable tri-state */
|
|
TRISTATE_FLASH(1);
|
|
return image_size;
|
|
}
|
|
|
|
int flash_physical_is_erased(uint32_t offset, int size)
|
|
{
|
|
int dest_addr = offset;
|
|
uint32_t idx;
|
|
uint8_t temp;
|
|
|
|
/* 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 */
|
|
temp = NPCX_UMA_DB0;
|
|
if (temp != 0xFF)
|
|
break;
|
|
}
|
|
|
|
/* Chip Select up */
|
|
flash_cs_level(1);
|
|
|
|
if (idx == size)
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
int flash_physical_write(int offset, int size, const char *data)
|
|
{
|
|
int dest_addr = offset;
|
|
const int sz_page = CONFIG_FLASH_WRITE_IDEAL_SIZE;
|
|
|
|
/* 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;
|
|
|
|
/* Disable tri-state */
|
|
TRISTATE_FLASH(0);
|
|
|
|
/* Write the data per CONFIG_FLASH_WRITE_IDEAL_SIZE bytes */
|
|
for (; size >= sz_page; size -= sz_page) {
|
|
|
|
/* check protection */
|
|
if (flash_check_prot_range(dest_addr, sz_page))
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
/* Enable write */
|
|
flash_write_enable();
|
|
/* Burst UMA transaction */
|
|
flash_burst_write(dest_addr, sz_page, data);
|
|
/* Wait write completed */
|
|
flash_wait_ready();
|
|
|
|
data += sz_page;
|
|
dest_addr += sz_page;
|
|
}
|
|
|
|
/* Handle final partial page, if any */
|
|
if (size != 0) {
|
|
/* check protection */
|
|
if (flash_check_prot_range(dest_addr, size))
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
/* Enable write */
|
|
flash_write_enable();
|
|
/* Burst UMA transaction */
|
|
flash_burst_write(dest_addr, size, data);
|
|
/* Wait write completed */
|
|
flash_wait_ready();
|
|
}
|
|
|
|
/* Enable tri-state */
|
|
TRISTATE_FLASH(1);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_physical_erase(int offset, int size)
|
|
{
|
|
/* check protection */
|
|
if (all_protected)
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
/* 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) {
|
|
|
|
/* Do nothing if already erased */
|
|
if (flash_is_erased(offset, CONFIG_FLASH_ERASE_SIZE))
|
|
continue;
|
|
|
|
/* check protection */
|
|
if (flash_check_prot_range(offset, CONFIG_FLASH_ERASE_SIZE))
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
/*
|
|
* 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 */
|
|
flash_write_enable();
|
|
/* Set erase address */
|
|
flash_set_address(offset);
|
|
/* Start erase */
|
|
flash_execute_cmd(CMD_SECTOR_ERASE, MASK_CMD_ADR);
|
|
|
|
/* Wait erase completed */
|
|
flash_wait_ready();
|
|
}
|
|
|
|
/* Enable tri-state */
|
|
TRISTATE_FLASH(1);
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
int flash_physical_get_protect(int bank)
|
|
{
|
|
uint32_t addr = bank * CONFIG_FLASH_BANK_SIZE;
|
|
/* All UMA transaction is locked means all banks are protected */
|
|
if (IS_BIT_SET(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK))
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
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) {
|
|
all_protected = 1;
|
|
/*
|
|
* Set UMA_LOCK bit for locking all UMA transaction.
|
|
* But we still can read directly from flash mapping address
|
|
*/
|
|
flash_uma_lock(1);
|
|
} else {
|
|
all_protected = 0;
|
|
/* Unlocking all UMA transaction */
|
|
flash_uma_lock(0);
|
|
}
|
|
/* TODO: if all, disable SPI interface */
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
|
|
int flash_physical_protect_at_boot(enum flash_wp_range range)
|
|
{
|
|
switch (range) {
|
|
case FLASH_WP_NONE:
|
|
/* Unlock UMA transactions */
|
|
if (IS_BIT_SET(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK))
|
|
CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK);
|
|
/* Clear protection bits in status register */
|
|
return flash_set_status_for_prot(0, 0);
|
|
case FLASH_WP_RO:
|
|
/* Unlock UMA transactions */
|
|
if (IS_BIT_SET(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK))
|
|
CLEAR_BIT(NPCX_UMA_ECTS, NPCX_UMA_ECTS_UMA_LOCK);
|
|
/* Protect read-only */
|
|
return flash_write_prot_reg(
|
|
WP_BANK_OFFSET*CONFIG_FLASH_BANK_SIZE,
|
|
WP_BANK_COUNT*CONFIG_FLASH_BANK_SIZE);
|
|
case FLASH_WP_ALL:
|
|
/* Protect all */
|
|
/*
|
|
* Set UMA_LOCK bit for locking all UMA transaction.
|
|
* But we still can read directly from flash mapping address
|
|
*/
|
|
return flash_uma_lock(1);
|
|
default:
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
/* Enable FIU interface */
|
|
flash_pinmux(1);
|
|
|
|
#ifdef CONFIG_CODERAM_ARCH
|
|
/* Disable tristate all the time */
|
|
CLEAR_BIT(NPCX_DEVCNT, NPCX_DEVCNT_F_SPI_TRIS);
|
|
#endif
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* 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_sel_lock, command_flash_spi_sel_lock,
|
|
"[0 | 1]",
|
|
"Lock spi flash interface selection",
|
|
NULL);
|
|
|
|
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,
|
|
"[0 | 1]",
|
|
"Tristate spi flash pins",
|
|
NULL);
|
|
|