summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNadim Taha <ntaha@google.com>2016-02-03 21:57:30 -0800
committerNadim Taha <ntaha@chromium.org>2016-02-04 20:56:10 +0000
commit1b9e6b2375ec2a2d2dc95968aa78d1c64faa1d2a (patch)
treecbcfc1d8d6598a8f29c7b5a61d019c7316f75be8
parent00fbe2a0aa0fca3ec2840e262876610a1822c0d7 (diff)
downloadchrome-ec-1b9e6b2375ec2a2d2dc95968aa78d1c64faa1d2a.tar.gz
Cr50: Modified the flash driver to retry operations as appropriate.
The max retry counts are based on the TSMC specification. This is a necessary change given that we're using their smart program/erase algorithms. BRANCH=none BUG=chrome-os-partner:45366 TEST=Tested RW updates. Change-Id: I18ca09e54ce13f2cf75dac32fb2457d5963ca040 Signed-off-by: Nadim Taha <ntaha@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/325535 Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r--chip/g/flash.c113
1 files changed, 75 insertions, 38 deletions
diff --git a/chip/g/flash.c b/chip/g/flash.c
index 04074cc3b5..f27ebb20d8 100644
--- a/chip/g/flash.c
+++ b/chip/g/flash.c
@@ -42,6 +42,7 @@
#include "flash.h"
#include "registers.h"
#include "timer.h"
+#include "watchdog.h"
int flash_pre_init(void)
{
@@ -101,18 +102,26 @@ static int do_flash_op(enum flash_op op, int byte_offset, int words)
{
volatile uint32_t *fsh_pe_control;
uint32_t opcode, tmp, errors;
- int i;
- int timedelay = 100; /* TODO(crosbug.com/p/45366): how long? */
+ int retry_count, max_attempts, extra_prog_pulse, i;
+ int timedelay_us = 100;
+
+ /* Make sure the smart program/erase algorithms are enabled. */
+ if (!GREAD(FLASH, FSH_TIMING_PROG_SMART_ALGO_ON) ||
+ !GREAD(FLASH, FSH_TIMING_ERASE_SMART_ALGO_ON)) {
+ return EC_ERROR_UNIMPLEMENTED;
+ }
/* Error status is self-clearing. Read it until it does (we hope). */
for (i = 0; i < 50; i++) {
tmp = GREAD(FLASH, FSH_ERROR);
if (!tmp)
break;
- usleep(timedelay);
+ usleep(timedelay_us);
}
- /* TODO: Is it even possible that we can't clear the error status?
- * What should/can we do about that? */
+ /* If we can't clear the error status register then something is wrong.
+ */
+ if (tmp)
+ return EC_ERROR_UNKNOWN;
/* We have two flash banks. Adjust offset and registers accordingly. */
if (byte_offset >= CONFIG_FLASH_SIZE / 2) {
@@ -127,10 +136,14 @@ static int do_flash_op(enum flash_op op, int byte_offset, int words)
case OP_ERASE_BLOCK:
opcode = 0x31415927;
words = 0; /* don't care, really */
+ /* This number is based on the TSMC spec Nme=Terase/Tsme */
+ max_attempts = 45;
break;
case OP_WRITE_BLOCK:
opcode = 0x27182818;
words--; /* count register is zero-based */
+ /* This number is based on the TSMC spec Nmp=Tprog/Tsmp */
+ max_attempts = 9;
break;
}
@@ -143,41 +156,65 @@ static int do_flash_op(enum flash_op op, int byte_offset, int words)
GWRITE_FIELD(FLASH, FSH_TRANS, MAINB, 0); /* NOT the info bank */
GWRITE_FIELD(FLASH, FSH_TRANS, SIZE, words);
- /* Kick it off */
- GWRITE(FLASH, FSH_PE_EN, 0xb11924e1);
- *fsh_pe_control = opcode;
-
- /* Wait for completion */
- for (i = 0; i < 50; i++) {
- tmp = *fsh_pe_control;
- if (!tmp)
- break;
- usleep(timedelay);
- }
-
- /* Timed out waiting for control register to clear */
- if (tmp)
- return EC_ERROR_UNKNOWN;
-
- /* Check error status */
- errors = GREAD(FLASH, FSH_ERROR);
-
- /* Error status is self-clearing. Read it until it does (we hope). */
- for (i = 0; i < 50; i++) {
- tmp = GREAD(FLASH, FSH_ERROR);
- if (!tmp)
- break;
- usleep(timedelay);
+ /* TODO: Make sure this function isn't getting called "too often" in
+ * between erases.
+ */
+ extra_prog_pulse = 0;
+ for (retry_count = 0; retry_count < max_attempts; retry_count++) {
+ /* Kick it off */
+ GWRITE(FLASH, FSH_PE_EN, 0xb11924e1);
+ *fsh_pe_control = opcode;
+
+ /* Wait for completion. 150ms should be enough
+ * (crosbug.com/p/45366).
+ */
+ for (i = 0; i < 1500; i++) {
+ tmp = *fsh_pe_control;
+ if (!tmp)
+ break;
+ usleep(timedelay_us);
+ }
+
+ /* Timed out waiting for control register to clear */
+ if (tmp)
+ return EC_ERROR_UNKNOWN;
+
+ /* Check error status */
+ errors = GREAD(FLASH, FSH_ERROR);
+
+ /* Error status is self-clearing. Read it until it does
+ * (we hope).
+ */
+ for (i = 0; i < 50; i++) {
+ tmp = GREAD(FLASH, FSH_ERROR);
+ if (!tmp)
+ break;
+ usleep(timedelay_us);
+ }
+ /* If we can't clear the error status register then something
+ * is wrong.
+ */
+ if (tmp)
+ return EC_ERROR_UNKNOWN;
+
+ /* The operation was successful. */
+ if (!errors) {
+ /* From the spec:
+ * "In addition, one more program pulse is needed after
+ * program verification is passed."
+ */
+ if (op == OP_WRITE_BLOCK && !extra_prog_pulse) {
+ extra_prog_pulse = 1;
+ max_attempts++;
+ continue;
+ }
+ return EC_SUCCESS;
+ }
+ /* If there were errors after completion retry. */
+ watchdog_reload();
}
- /* If there were errors after completion, or if we can't clear the
- * error status register (is that likely?) then something is wrong. */
- if (errors || tmp)
- return EC_ERROR_UNKNOWN;
-
- /* The operation was successful. */
- /* TODO: Should we read it back to be sure? */
- return EC_SUCCESS;
+ return EC_ERROR_UNKNOWN;
}
/* Write up to CONFIG_FLASH_WRITE_IDEAL_SIZE bytes at once */