diff options
-rw-r--r-- | chip/g/flash.c | 113 |
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 */ |