summaryrefslogtreecommitdiff
path: root/chip/lm4/flash.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-03-20 16:57:39 -0700
committerRandall Spangler <rspangler@chromium.org>2012-04-02 10:57:03 -0700
commitb70183a9bd93cb2ffb987434cca72ccac4f505cf (patch)
tree240f170362279631284ede0165dfbecf0246d63f /chip/lm4/flash.c
parent36572d45faeac979ee3587bf9ae3bcc8e2fe14d8 (diff)
downloadchrome-ec-b70183a9bd93cb2ffb987434cca72ccac4f505cf.tar.gz
Implement persistent flash write protect settings
This uses the last bank of flash to hold persistent settings, and looks at the write protect GPIO to decide whether to protect the chip at boot (chrome-os-partner:7453). For ease of debugging, I've temporarily hacked this so flash uses the RECOVERYn signal (dut-control goog_rec_mode:on) to enable WP instead of the write protect signal; this works around chrome-os-partner:8580. Also note that if you write protect any blocks even temporarily, you'll need to do a power-on reset to clear them before you can reprogram the flash. See chrome-os-partner:8632. At the EC console, "hibernate 1" will do that, or you can just yank the power. This also fixes a bug in the flash write and erase commands, where they weren't properly detecting failure if you attempted to modify a protected block (missed an interrupt reason...) New "flashwp" console commands work. LPC commands need reworking. Signed-off-by: Randall Spangler <rspangler@chromium.org> BUG=chrome-os-partner:8448 TEST=manual Change-Id: I49c38cc25c793094ae3331a4586fda0761b4bac6
Diffstat (limited to 'chip/lm4/flash.c')
-rw-r--r--chip/lm4/flash.c206
1 files changed, 29 insertions, 177 deletions
diff --git a/chip/lm4/flash.c b/chip/lm4/flash.c
index bc1505e791..e05b62089b 100644
--- a/chip/lm4/flash.c
+++ b/chip/lm4/flash.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 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.
*/
@@ -6,24 +6,21 @@
/* Flash memory module for Chrome EC */
#include "flash.h"
-#include "gpio.h"
#include "uart.h"
#include "registers.h"
#include "util.h"
+#define FLASH_WRITE_BYTES 4
+#define FLASH_FWB_WORDS 32
+#define FLASH_FWB_BYTES (FLASH_FWB_WORDS * 4)
+#define FLASH_ERASE_BYTES 1024
+#define FLASH_PROTECT_BYTES 2048
+
#define BANK_SHIFT 5 /* bank registers have 32bits each, 2^32 */
#define BANK_MASK ((1 << BANK_SHIFT) - 1) /* 5 bits */
#define F_BANK(b) ((b) >> BANK_SHIFT)
#define F_BIT(b) (1 << ((b) & BANK_MASK))
-static int usable_flash_size;
-
-
-int flash_get_size(void)
-{
- return usable_flash_size;
-}
-
int flash_get_write_block_size(void)
{
@@ -39,16 +36,19 @@ int flash_get_erase_block_size(void)
int flash_get_protect_block_size(void)
{
+ BUILD_ASSERT(FLASH_PROTECT_BYTES == CONFIG_FLASH_BANK_SIZE);
return FLASH_PROTECT_BYTES;
}
-int flash_read(int offset, int size, char *data)
+int flash_physical_size(void)
{
- if (size < 0 || offset > usable_flash_size ||
- offset + size > usable_flash_size)
- return EC_ERROR_UNKNOWN; /* Invalid range */
+ return (LM4_FLASH_FSIZE + 1) * FLASH_PROTECT_BYTES;
+}
+
+int flash_physical_read(int offset, int size, char *data)
+{
/* Just read the flash from its memory window. */
/* TODO: (crosbug.com/p/7473) is this affected by data cache?
* That is, if we read a block, then alter it, then read it
@@ -58,8 +58,8 @@ int flash_read(int offset, int size, char *data)
}
-/* Performs a write-buffer operation. Buffer (FWB) and address (FMA)
- * must be pre-loaded. */
+/* Perform a write-buffer operation. Buffer (FWB) and address (FMA) must be
+ * pre-loaded. */
static int write_buffer(void)
{
if (!LM4_FLASH_FWBVAL)
@@ -72,32 +72,25 @@ static int write_buffer(void)
LM4_FLASH_FMC2 = 0xa4420001;
/* Wait for write to complete */
+ /* TODO: timeout */
while (LM4_FLASH_FMC2 & 0x01) {}
/* Check for error conditions - program failed, erase needed,
* voltage error. */
- if (LM4_FLASH_FCRIS & 0x2600)
+ if (LM4_FLASH_FCRIS & 0x2e01)
return EC_ERROR_UNKNOWN;
return EC_SUCCESS;
}
-int flash_write(int offset, int size, const char *data)
+int flash_physical_write(int offset, int size, const char *data)
{
const uint32_t *data32 = (const uint32_t *)data;
int rv;
int i;
- if (size < 0 || offset > usable_flash_size ||
- offset + size > usable_flash_size ||
- (offset | size) & (FLASH_WRITE_BYTES - 1))
- return EC_ERROR_UNKNOWN; /* Invalid range */
-
- /* TODO (crosbug.com/p/7478) - safety check - don't allow writing to
- * the image we're running from */
-
- /* Get initial page and write buffer index */
+ /* Get initial write buffer index and page */
LM4_FLASH_FMA = offset & ~(FLASH_FWB_BYTES - 1);
i = (offset >> 2) & (FLASH_FWB_WORDS - 1);
@@ -125,16 +118,8 @@ int flash_write(int offset, int size, const char *data)
}
-int flash_erase(int offset, int size)
+int flash_physical_erase(int offset, int size)
{
- if (size < 0 || offset > usable_flash_size ||
- offset + size > usable_flash_size ||
- (offset | size) & (FLASH_ERASE_BYTES - 1))
- return EC_ERROR_UNKNOWN; /* Invalid range */
-
- /* TODO (crosbug.com/p/7478) - safety check - don't allow erasing the
- * image we're running from */
-
LM4_FLASH_FCMISC = LM4_FLASH_FCRIS; /* Clear previous error status */
LM4_FLASH_FMA = offset;
@@ -143,10 +128,12 @@ int flash_erase(int offset, int size)
LM4_FLASH_FMC = 0xa4420002;
/* Wait for erase to complete */
+ /* TODO: timeout */
while (LM4_FLASH_FMC & 0x02) {}
- /* Check for error conditions - erase failed, voltage error */
- if (LM4_FLASH_FCRIS & 0x0a00)
+ /* Check for error conditions - erase failed, voltage error,
+ * protection error */
+ if (LM4_FLASH_FCRIS & 0x0a01)
return EC_ERROR_UNKNOWN;
LM4_FLASH_FMA += FLASH_ERASE_BYTES;
@@ -155,149 +142,14 @@ int flash_erase(int offset, int size)
return EC_SUCCESS;
}
-/* Get write protect status of single flash block
- * return value:
- * 0 - WP
- * non-zero - writable
- */
-static uint32_t get_block_wp(int block)
-{
- return LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block);
-}
-
-static void set_block_wp(int block)
-{
- LM4_FLASH_FMPPE[F_BANK(block)] &= ~F_BIT(block);
-}
-
-static int find_first_wp_block(void)
-{
- int block;
- for (block = 0; block < LM4_FLASH_FSIZE; block++)
- if (get_block_wp(block) == 0)
- return block;
- return -1;
-}
-static int find_last_wp_block(void)
+int flash_physical_get_protect(int block)
{
- int block;
- for (block = LM4_FLASH_FSIZE - 1; block >= 0; block--)
- if (get_block_wp(block) == 0)
- return block;
- return -1;
-}
-
-static int get_wp_range(uint32_t *start, uint32_t *nblock)
-{
- int start_blk, end_blk;
-
- start_blk = find_first_wp_block();
-
- if (start_blk < 0) {
- /* Flash is not write protected */
- *start = 0;
- *nblock = 0;
- return EC_SUCCESS;
- }
-
- /* TODO: Sanity check the shadow value? */
-
- end_blk = find_last_wp_block();
- *nblock = (uint32_t)(end_blk - start_blk + 1);
- *start = (uint32_t)start_blk;
- return EC_SUCCESS;
+ return (LM4_FLASH_FMPPE[F_BANK(block)] & F_BIT(block)) ? 0 : 1;
}
-static int set_wp_range(int start, int nblock)
+void flash_physical_set_protect(int block)
{
- int end_blk, block;
-
- if (nblock == 0)
- return EC_SUCCESS;
-
- end_blk = (start + nblock - 1);
-
- for (block = start; block <= end_blk; block++)
- set_block_wp(block);
-
- return EC_SUCCESS;
-}
-
-int flash_get_write_protect_range(uint32_t *offset, uint32_t *size)
-{
- uint32_t start, nblock;
- int rv;
-
- rv = get_wp_range(&start, &nblock);
- if (rv)
- return rv;
-
- *size = nblock * FLASH_PROTECT_BYTES;
- *offset = start * FLASH_PROTECT_BYTES;
- return EC_SUCCESS;
-}
-
-int flash_set_write_protect_range(uint32_t offset, uint32_t size)
-{
- int start, nblock;
- int rv;
-
- if ((offset + size) > (LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES))
- return EC_ERROR_UNKNOWN; /* Invalid range */
-
- rv = flash_get_write_protect_status();
-
- if (rv & EC_FLASH_WP_RANGE_LOCKED) {
- if (size == 0) {
- /* TODO: Clear shadow if system WP is asserted */
- /* TODO: Reboot EC */
- return EC_SUCCESS;
- }
-
- return EC_ERROR_UNKNOWN; /* Range locked */
- }
-
- start = offset / FLASH_PROTECT_BYTES;
- nblock = ((offset + size - 1) / FLASH_PROTECT_BYTES) - start + 1;
- rv = set_wp_range(start, nblock);
- if (rv)
- return rv;
-
- return EC_SUCCESS;
-}
-
-
-int flash_get_write_protect_status(void)
-{
- uint32_t start, nblock;
- int rv;
-
- rv = get_wp_range(&start, &nblock);
- if (rv)
- return rv;
-
- rv = 0;
- if (nblock)
- rv |= EC_FLASH_WP_RANGE_LOCKED;
- /* TODO: get WP gpio*/
-
- return rv;
-}
-
-
-int flash_pre_init(void)
-{
- /* Calculate usable flash size. Reserve one protection block
- * at the top to hold the write protect range. FSIZE already
- * returns one less than the number of protection pages. */
- usable_flash_size = LM4_FLASH_FSIZE * FLASH_PROTECT_BYTES;
-
- /* TODO (crosbug.com/p/7453) - check WP# GPIO. If it's set and the
- * flash protect range is set, write the flash protection registers.
- * Probably cleaner to do this in vboot, since we're going to need to
- * use the same last block of flash to hold the firmware rollback
- * counters. */
- return EC_SUCCESS;
+ LM4_FLASH_FMPPE[F_BANK(block)] &= ~F_BIT(block);
}