diff options
Diffstat (limited to 'spi25_statusreg.c')
-rw-r--r-- | spi25_statusreg.c | 581 |
1 files changed, 462 insertions, 119 deletions
diff --git a/spi25_statusreg.c b/spi25_statusreg.c index a0b0fcf5e..bb56ee1a8 100644 --- a/spi25_statusreg.c +++ b/spi25_statusreg.c @@ -17,29 +17,146 @@ * GNU General Public License for more details. */ +#include <stdlib.h> + #include "flash.h" #include "chipdrivers.h" +#include "programmer.h" #include "spi.h" /* === Generic functions === */ -static int spi_write_status_register_flag(const struct flashctx *flash, int status, const unsigned char enable_opcode) + +/* + * Writing SR2 or higher with an extended WRSR command requires + * writing all lower SRx along with it, so just read the lower + * SRx and write them back. + */ +static int spi_prepare_wrsr_ext( + uint8_t write_cmd[4], size_t *const write_cmd_len, + const struct flashctx *const flash, + const enum flash_reg reg, const uint8_t value) { - int result; - int i = 0; + enum flash_reg reg_it; + size_t i = 0; + + write_cmd[i++] = JEDEC_WRSR; + + for (reg_it = STATUS1; reg_it < reg; ++reg_it) { + uint8_t sr; + + if (spi_read_register(flash, reg_it, &sr)) { + msg_cerr("Writing SR%d failed: failed to read SR%d for writeback.\n", + reg - STATUS1 + 1, reg_it - STATUS1 + 1); + return 1; + } + write_cmd[i++] = sr; + } + + write_cmd[i++] = value; + *write_cmd_len = i; + + return 0; +} + +int spi_write_register(const struct flashctx *flash, enum flash_reg reg, uint8_t value) +{ + int feature_bits = flash->chip->feature_bits; + + uint8_t write_cmd[4]; + size_t write_cmd_len = 0; + /* - * WRSR requires either EWSR or WREN depending on chip type. - * The code below relies on the fact hat EWSR and WREN have the same - * INSIZE and OUTSIZE. + * Create SPI write command sequence based on the destination register + * and the chip's supported command set. */ + switch (reg) { + case STATUS1: + write_cmd[0] = JEDEC_WRSR; + write_cmd[1] = value; + write_cmd_len = JEDEC_WRSR_OUTSIZE; + break; + case STATUS2: + if (feature_bits & FEATURE_WRSR2) { + write_cmd[0] = JEDEC_WRSR2; + write_cmd[1] = value; + write_cmd_len = JEDEC_WRSR2_OUTSIZE; + break; + } + if (feature_bits & FEATURE_WRSR_EXT2) { + if (spi_prepare_wrsr_ext(write_cmd, &write_cmd_len, flash, reg, value)) + return 1; + break; + } + msg_cerr("Cannot write SR2: unsupported by chip\n"); + return 1; + case STATUS3: + if (feature_bits & FEATURE_WRSR3) { + write_cmd[0] = JEDEC_WRSR3; + write_cmd[1] = value; + write_cmd_len = JEDEC_WRSR3_OUTSIZE; + break; + } + if ((feature_bits & FEATURE_WRSR_EXT3) == FEATURE_WRSR_EXT3) { + if (spi_prepare_wrsr_ext(write_cmd, &write_cmd_len, flash, reg, value)) + return 1; + break; + } + msg_cerr("Cannot write SR3: unsupported by chip\n"); + return 1; + case SECURITY: + /* + * Security register doesn't have a normal write operation. Instead, + * there are separate commands that set individual OTP bits. + */ + msg_cerr("Cannot write SECURITY: unsupported by design\n"); + return 1; + case CONFIG: + /* + * This one is read via a separate command, but written as if it's SR2 + * in FEATURE_WRSR_EXT2 case of WRSR command. + */ + if (feature_bits & FEATURE_CFGR) { + write_cmd[0] = JEDEC_WRSR; + if (spi_read_register(flash, STATUS1, &write_cmd[1])) { + msg_cerr("Writing CONFIG failed: failed to read SR1 for writeback.\n"); + return 1; + } + write_cmd[2] = value; + write_cmd_len = 3; + break; + } + msg_cerr("Cannot write CONFIG: unsupported by chip\n"); + return 1; + default: + msg_cerr("Cannot write register: unknown register\n"); + return 1; + } + + if (!spi_probe_opcode(flash, write_cmd[0])) { + msg_pdbg("%s: write to register %d not supported by programmer, ignoring.\n", __func__, reg); + return SPI_INVALID_OPCODE; + } + + uint8_t enable_cmd; + if (feature_bits & FEATURE_WRSR_WREN) { + enable_cmd = JEDEC_WREN; + } else if (feature_bits & FEATURE_WRSR_EWSR) { + enable_cmd = JEDEC_EWSR; + } else { + msg_cdbg("Missing status register write definition, assuming " + "EWSR is needed\n"); + enable_cmd = JEDEC_EWSR; + } + struct spi_command cmds[] = { { .writecnt = JEDEC_WREN_OUTSIZE, - .writearr = (const unsigned char[]){ enable_opcode }, + .writearr = &enable_cmd, .readcnt = 0, .readarr = NULL, }, { - .writecnt = JEDEC_WRSR_OUTSIZE, - .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status }, + .writecnt = write_cmd_len, + .writearr = write_cmd, .readcnt = 0, .readarr = NULL, }, { @@ -49,69 +166,113 @@ static int spi_write_status_register_flag(const struct flashctx *flash, int stat .readarr = NULL, }}; - result = spi_send_multicommand(flash, cmds); + int result = spi_send_multicommand(flash, cmds); if (result) { msg_cerr("%s failed during command execution\n", __func__); - /* No point in waiting for the command to complete if execution - * failed. - */ return result; } - /* WRSR performs a self-timed erase before the changes take effect. + + /* + * WRSR performs a self-timed erase before the changes take effect. * This may take 50-85 ms in most cases, and some chips apparently * allow running RDSR only once. Therefore pick an initial delay of * 100 ms, then wait in 10 ms steps until a total of 5 s have elapsed. + * + * Newer chips with multiple status registers (SR2 etc.) are unlikely + * to have problems with multiple RDSR commands, so only wait for the + * initial 100 ms if the register we wrote to was SR1. */ - programmer_delay(100 * 1000); - while (spi_read_status_register(flash) & SPI_SR_WIP) { - if (++i > 490) { - msg_cerr("Error: WIP bit after WRSR never cleared\n"); - return TIMEOUT_ERROR; - } - programmer_delay(10 * 1000); + int delay_ms = 5000; + if (reg == STATUS1) { + programmer_delay(flash, 100 * 1000); + delay_ms -= 100; } - return 0; + + for (; delay_ms > 0; delay_ms -= 10) { + uint8_t status; + result = spi_read_register(flash, STATUS1, &status); + if (result) + return result; + if ((status & SPI_SR_WIP) == 0) + return 0; + programmer_delay(flash, 10 * 1000); + } + + + msg_cerr("Error: WIP bit after WRSR never cleared\n"); + return TIMEOUT_ERROR; } -int spi_write_status_register(const struct flashctx *flash, int status) +int spi_read_register(const struct flashctx *flash, enum flash_reg reg, uint8_t *value) { int feature_bits = flash->chip->feature_bits; - int ret = 1; + uint8_t read_cmd; - if (!(feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) { - msg_cdbg("Missing status register write definition, assuming " - "EWSR is needed\n"); - feature_bits |= FEATURE_WRSR_EWSR; + switch (reg) { + case STATUS1: + read_cmd = JEDEC_RDSR; + break; + case STATUS2: + if (feature_bits & (FEATURE_WRSR_EXT2 | FEATURE_WRSR2)) { + read_cmd = JEDEC_RDSR2; + break; + } + msg_cerr("Cannot read SR2: unsupported by chip\n"); + return 1; + case STATUS3: + if ((feature_bits & FEATURE_WRSR_EXT3) == FEATURE_WRSR_EXT3 + || (feature_bits & FEATURE_WRSR3)) { + read_cmd = JEDEC_RDSR3; + break; + } + msg_cerr("Cannot read SR3: unsupported by chip\n"); + return 1; + case SECURITY: + if (feature_bits & FEATURE_SCUR) { + read_cmd = JEDEC_RDSCUR; + break; + } + msg_cerr("Cannot read SECURITY: unsupported by chip\n"); + return 1; + case CONFIG: + if (feature_bits & FEATURE_CFGR) { + read_cmd = JEDEC_RDCR; + break; + } + msg_cerr("Cannot read CONFIG: unsupported by chip\n"); + return 1; + default: + msg_cerr("Cannot read register: unknown register\n"); + return 1; + } + + if (!spi_probe_opcode(flash, read_cmd)) { + msg_pdbg("%s: read from register %d not supported by programmer.\n", __func__, reg); + return SPI_INVALID_OPCODE; } - if (feature_bits & FEATURE_WRSR_WREN) - ret = spi_write_status_register_flag(flash, status, JEDEC_WREN); - if (ret && (feature_bits & FEATURE_WRSR_EWSR)) - ret = spi_write_status_register_flag(flash, status, JEDEC_EWSR); - return ret; -} -uint8_t spi_read_status_register(const struct flashctx *flash) -{ - static const unsigned char cmd[JEDEC_RDSR_OUTSIZE] = { JEDEC_RDSR }; /* FIXME: No workarounds for driver/hardware bugs in generic code. */ - unsigned char readarr[2]; /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */ - int ret; + /* JEDEC_RDSR_INSIZE=1 but wbsio needs 2 */ + uint8_t readarr[2]; - /* Read Status Register */ - ret = spi_send_command(flash, sizeof(cmd), sizeof(readarr), cmd, readarr); + int ret = spi_send_command(flash, sizeof(read_cmd), sizeof(readarr), &read_cmd, readarr); if (ret) { - msg_cerr("RDSR failed!\n"); - /* FIXME: We should propagate the error. */ - return 0; + msg_cerr("Register read failed!\n"); + return ret; } - return readarr[0]; + *value = readarr[0]; + msg_cspew("%s: read_cmd 0x%02x returned 0x%02x\n", __func__, read_cmd, readarr[0]); + return 0; } -static int spi_restore_status(struct flashctx *flash, uint8_t status) +static int spi_restore_status(struct flashctx *flash, void *data) { + uint8_t status = *(uint8_t *)data; + free(data); + msg_cdbg("restoring chip status (0x%02x)\n", status); - return spi_write_status_register(flash, status); + return spi_write_register(flash, STATUS1, status); } /* A generic block protection disable. @@ -139,14 +300,23 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m uint8_t status; int result; - status = spi_read_status_register(flash); + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + if ((status & bp_mask) == 0) { msg_cdbg2("Block protection is disabled.\n"); return 0; } /* Restore status register content upon exit in finalize_flash_access(). */ - register_chip_restore(spi_restore_status, flash, status); + uint8_t *data = calloc(sizeof(uint8_t), 1); + if (!data) { + msg_cerr("Out of memory!\n"); + return 1; + } + *data = status; + register_chip_restore(spi_restore_status, flash, data); msg_cdbg("Some block protection in effect, disabling... "); if ((status & lock_mask) != 0) { @@ -156,12 +326,16 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m return 1; } /* All bits except the register lock bit (often called SPRL, SRWD, WPEN) are readonly. */ - result = spi_write_status_register(flash, status & ~lock_mask); + result = spi_write_register(flash, STATUS1, status & ~lock_mask); if (result) { - msg_cerr("spi_write_status_register failed.\n"); + msg_cerr("Could not write status register 1.\n"); return result; } - status = spi_read_status_register(flash); + + ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + if ((status & lock_mask) != 0) { msg_cerr("Unsetting lock bit(s) failed.\n"); return 1; @@ -169,16 +343,21 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m msg_cdbg("done.\n"); } /* Global unprotect. Make sure to mask the register lock bit as well. */ - result = spi_write_status_register(flash, status & ~(bp_mask | lock_mask) & unprotect_mask); + result = spi_write_register(flash, STATUS1, status & ~(bp_mask | lock_mask) & unprotect_mask); if (result) { - msg_cerr("spi_write_status_register failed.\n"); + msg_cerr("Could not write status register 1.\n"); return result; } - status = spi_read_status_register(flash); + + ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + if ((status & bp_mask) != 0) { msg_cerr("Block protection could not be disabled!\n"); - if (flash->chip->printlock) - flash->chip->printlock(flash); + printlockfunc_t *printlock = lookup_printlock_func_ptr(flash); + if (printlock) + printlock(flash); return 1; } msg_cdbg("disabled.\n"); @@ -186,12 +365,12 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m } /* A common block protection disable that tries to unset the status register bits masked by 0x3C. */ -int spi_disable_blockprotect(struct flashctx *flash) +static int spi_disable_blockprotect(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x3C, 0, 0, 0xFF); } -int spi_disable_blockprotect_sst26_global_unprotect(struct flashctx *flash) +static int spi_disable_blockprotect_sst26_global_unprotect(struct flashctx *flash) { int result = spi_write_enable(flash); if (result) @@ -206,7 +385,7 @@ int spi_disable_blockprotect_sst26_global_unprotect(struct flashctx *flash) /* A common block protection disable that tries to unset the status register bits masked by 0x0C (BP0-1) and * protected/locked by bit #7. Useful when bits 4-5 may be non-0). */ -int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash) +static int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF); } @@ -214,21 +393,21 @@ int spi_disable_blockprotect_bp1_srwd(struct flashctx *flash) /* A common block protection disable that tries to unset the status register bits masked by 0x1C (BP0-2) and * protected/locked by bit #7. Useful when bit #5 is neither a protection bit nor reserved (and hence possibly * non-0). */ -int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash) +static int spi_disable_blockprotect_bp2_srwd(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0, 0xFF); } /* A common block protection disable that tries to unset the status register bits masked by 0x3C (BP0-3) and * protected/locked by bit #7. */ -int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash) +static int spi_disable_blockprotect_bp3_srwd(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x3C, 1 << 7, 0, 0xFF); } /* A common block protection disable that tries to unset the status register bits masked by 0x7C (BP0-4) and * protected/locked by bit #7. */ -int spi_disable_blockprotect_bp4_srwd(struct flashctx *flash) +static int spi_disable_blockprotect_bp4_srwd(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF); } @@ -293,17 +472,23 @@ void spi_prettyprint_status_register_bit(uint8_t status, int bit) msg_cdbg("Chip status register: Bit %i is %sset\n", bit, (status & (1 << bit)) ? "" : "not "); } -int spi_prettyprint_status_register_plain(struct flashctx *flash) +static int spi_prettyprint_status_register_plain(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); return 0; } /* Print the plain hex value and the welwip bits only. */ -int spi_prettyprint_status_register_default_welwip(struct flashctx *flash) +static int spi_prettyprint_status_register_default_welwip(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_welwip(status); @@ -314,9 +499,12 @@ int spi_prettyprint_status_register_default_welwip(struct flashctx *flash) * AMIC A25L series * and MX MX25L512 */ -int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash) +static int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -332,9 +520,12 @@ int spi_prettyprint_status_register_bp1_srwd(struct flashctx *flash) * AMIC A25L series * PMC Pm25LD series */ -int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash) +static int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -349,9 +540,12 @@ int spi_prettyprint_status_register_bp2_srwd(struct flashctx *flash) * ST M25P series * MX MX25L series */ -int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash) +static int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -361,9 +555,12 @@ int spi_prettyprint_status_register_bp3_srwd(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash) +static int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -372,9 +569,12 @@ int spi_prettyprint_status_register_bp4_srwd(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash) +static int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_bpl(status); @@ -385,9 +585,12 @@ int spi_prettyprint_status_register_bp2_bpl(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash) +static int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_bpl(status); @@ -407,9 +610,12 @@ int spi_prettyprint_status_register_bp2_tb_bpl(struct flashctx *flash) * by the second status register. */ -int spi_prettyprint_status_register_amic_a25l032(struct flashctx *flash) +static int spi_prettyprint_status_register_amic_a25l032(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -463,9 +669,13 @@ static void spi_prettyprint_status_register_atmel_at25_swp(uint8_t status) } } -int spi_prettyprint_status_register_at25df(struct flashctx *flash) +static int spi_prettyprint_status_register_at25df(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_srpl(status); @@ -476,7 +686,7 @@ int spi_prettyprint_status_register_at25df(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25df_sec(struct flashctx *flash) +static int spi_prettyprint_status_register_at25df_sec(struct flashctx *flash) { /* FIXME: We should check the security lockdown. */ msg_cdbg("Ignoring security lockdown (if present)\n"); @@ -485,11 +695,13 @@ int spi_prettyprint_status_register_at25df_sec(struct flashctx *flash) } /* used for AT25F512, AT25F1024(A), AT25F2048 */ -int spi_prettyprint_status_register_at25f(struct flashctx *flash) +static int spi_prettyprint_status_register_at25f(struct flashctx *flash) { uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; - status = spi_read_status_register(flash); spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_wpen(status); @@ -501,11 +713,13 @@ int spi_prettyprint_status_register_at25f(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25f512a(struct flashctx *flash) +static int spi_prettyprint_status_register_at25f512a(struct flashctx *flash) { uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; - status = spi_read_status_register(flash); spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_wpen(status); @@ -518,9 +732,12 @@ int spi_prettyprint_status_register_at25f512a(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25f512b(struct flashctx *flash) +static int spi_prettyprint_status_register_at25f512b(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_srpl(status); @@ -532,11 +749,14 @@ int spi_prettyprint_status_register_at25f512b(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25f4096(struct flashctx *flash) +static int spi_prettyprint_status_register_at25f4096(struct flashctx *flash) { uint8_t status; - status = spi_read_status_register(flash); + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; + spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_wpen(status); @@ -547,9 +767,12 @@ int spi_prettyprint_status_register_at25f4096(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25fs010(struct flashctx *flash) +static int spi_prettyprint_status_register_at25fs010(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_wpen(status); @@ -567,9 +790,12 @@ int spi_prettyprint_status_register_at25fs010(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at25fs040(struct flashctx *flash) +static int spi_prettyprint_status_register_at25fs040(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_wpen(status); @@ -579,9 +805,12 @@ int spi_prettyprint_status_register_at25fs040(struct flashctx *flash) return 0; } -int spi_prettyprint_status_register_at26df081a(struct flashctx *flash) +static int spi_prettyprint_status_register_at26df081a(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_atmel_at25_srpl(status); @@ -598,48 +827,51 @@ int spi_prettyprint_status_register_at26df081a(struct flashctx *flash) * sectors at once by writing 0 not only the protection bits (2 and 3) but also completely unrelated bits (4 and * 5) which normally are not touched. * Affected are all known Atmel chips matched by AT2[56]D[FLQ]..1A? but the AT26DF041. */ -int spi_disable_blockprotect_at2x_global_unprotect(struct flashctx *flash) +static int spi_disable_blockprotect_at2x_global_unprotect(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 1 << 4, 0x00); } -int spi_disable_blockprotect_at2x_global_unprotect_sec(struct flashctx *flash) +static int spi_disable_blockprotect_at2x_global_unprotect_sec(struct flashctx *flash) { /* FIXME: We should check the security lockdown. */ msg_cinfo("Ignoring security lockdown (if present)\n"); return spi_disable_blockprotect_at2x_global_unprotect(flash); } -int spi_disable_blockprotect_at25f(struct flashctx *flash) +static int spi_disable_blockprotect_at25f(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF); } -int spi_disable_blockprotect_at25f512a(struct flashctx *flash) +static int spi_disable_blockprotect_at25f512a(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 0, 0xFF); } -int spi_disable_blockprotect_at25f512b(struct flashctx *flash) +static int spi_disable_blockprotect_at25f512b(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 1 << 4, 0xFF); } -int spi_disable_blockprotect_at25fs010(struct flashctx *flash) +static int spi_disable_blockprotect_at25fs010(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x6C, 1 << 7, 0, 0xFF); } -int spi_disable_blockprotect_at25fs040(struct flashctx *flash) +static int spi_disable_blockprotect_at25fs040(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF); } /* === Eon === */ -int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash) +static int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -651,14 +883,17 @@ int spi_prettyprint_status_register_en25s_wp(struct flashctx *flash) /* === Intel/Numonyx/Micron - Spansion === */ -int spi_disable_blockprotect_n25q(struct flashctx *flash) +static int spi_disable_blockprotect_n25q(struct flashctx *flash) { return spi_disable_blockprotect_generic(flash, 0x5C, 1 << 7, 0, 0xFF); } -int spi_prettyprint_status_register_n25q(struct flashctx *flash) +static int spi_prettyprint_status_register_n25q(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -675,15 +910,59 @@ int spi_prettyprint_status_register_n25q(struct flashctx *flash) /* Used by Intel/Numonyx S33 and Spansion S25FL-S chips */ /* TODO: Clear P_FAIL and E_FAIL with Clear SR Fail Flags Command (30h) here? */ -int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash) +static int spi_disable_blockprotect_bp2_ep_srwd(struct flashctx *flash) { return spi_disable_blockprotect_bp2_srwd(flash); } +blockprotect_func_t *lookup_blockprotect_func_ptr(const struct flashchip *const chip) +{ + switch (chip->unlock) { + case SPI_DISABLE_BLOCKPROTECT: return spi_disable_blockprotect; + case SPI_DISABLE_BLOCKPROTECT_BP2_EP_SRWD: return spi_disable_blockprotect_bp2_ep_srwd; + case SPI_DISABLE_BLOCKPROTECT_BP1_SRWD: return spi_disable_blockprotect_bp1_srwd; + case SPI_DISABLE_BLOCKPROTECT_BP2_SRWD: return spi_disable_blockprotect_bp2_srwd; + case SPI_DISABLE_BLOCKPROTECT_BP3_SRWD: return spi_disable_blockprotect_bp3_srwd; + case SPI_DISABLE_BLOCKPROTECT_BP4_SRWD: return spi_disable_blockprotect_bp4_srwd; + case SPI_DISABLE_BLOCKPROTECT_AT45DB: return spi_disable_blockprotect_at45db; /* at45db.c */ + case SPI_DISABLE_BLOCKPROTECT_AT25F: return spi_disable_blockprotect_at25f; + case SPI_DISABLE_BLOCKPROTECT_AT25FS010: return spi_disable_blockprotect_at25fs010; + case SPI_DISABLE_BLOCKPROTECT_AT25FS040: return spi_disable_blockprotect_at25fs040; + case SPI_DISABLE_BLOCKPROTECT_AT25F512A: return spi_disable_blockprotect_at25f512a; + case SPI_DISABLE_BLOCKPROTECT_AT25F512B: return spi_disable_blockprotect_at25f512b; + case SPI_DISABLE_BLOCKPROTECT_AT2X_GLOBAL_UNPROTECT: return spi_disable_blockprotect_at2x_global_unprotect; + case SPI_DISABLE_BLOCKPROTECT_AT2X_GLOBAL_UNPROTECT_SEC: return spi_disable_blockprotect_at2x_global_unprotect_sec; + case SPI_DISABLE_BLOCKPROTECT_SST26_GLOBAL_UNPROTECT: return spi_disable_blockprotect_sst26_global_unprotect; + case SPI_DISABLE_BLOCKPROTECT_N25Q: return spi_disable_blockprotect_n25q; + /* fallthough to lookup_jedec_blockprotect_func_ptr() */ + case UNLOCK_REGSPACE2_BLOCK_ERASER_0: + case UNLOCK_REGSPACE2_BLOCK_ERASER_1: + case UNLOCK_REGSPACE2_UNIFORM_32K: + case UNLOCK_REGSPACE2_UNIFORM_64K: + return lookup_jedec_blockprotect_func_ptr(chip); + /* fallthough to lookup_82802ab_blockprotect_func_ptr() */ + case UNLOCK_28F004S5: + case UNLOCK_LH28F008BJT: + return lookup_82802ab_blockprotect_func_ptr(chip); + case UNLOCK_SST_FWHUB: return unlock_sst_fwhub; /* sst_fwhub.c */ + case UNPROTECT_28SF040: return unprotect_28sf040; /* sst28sf040.c */ + /* default: non-total function, 0 indicates no unlock function set. + * We explicitly do not want a default catch-all case in the switch + * to ensure unhandled enum's are compiler warnings. + */ + case NO_BLOCKPROTECT_FUNC: return NULL; + }; + + return NULL; +} + /* Used by Intel/Numonyx S33 and Spansion S25FL-S chips */ -int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash) +static int spi_prettyprint_status_register_bp2_ep_srwd(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_hex(status); spi_prettyprint_status_register_srwd(status); @@ -709,14 +988,17 @@ static void spi_prettyprint_status_register_sst25_common(uint8_t status) spi_prettyprint_status_register_welwip(status); } -int spi_prettyprint_status_register_sst25(struct flashctx *flash) +static int spi_prettyprint_status_register_sst25(struct flashctx *flash) { - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_sst25_common(status); return 0; } -int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash) +static int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash) { static const char *const bpt[] = { "none", @@ -727,13 +1009,16 @@ int spi_prettyprint_status_register_sst25vf016(struct flashctx *flash) "100000H-1FFFFFH", "all", "all" }; - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_sst25_common(status); msg_cdbg("Resulting block protection : %s\n", bpt[(status & 0x1c) >> 2]); return 0; } -int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash) +static int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash) { static const char *const bpt[] = { "none", @@ -742,8 +1027,66 @@ int spi_prettyprint_status_register_sst25vf040b(struct flashctx *flash) "0x40000-0x7ffff", "all blocks", "all blocks", "all blocks", "all blocks" }; - uint8_t status = spi_read_status_register(flash); + uint8_t status; + int ret = spi_read_register(flash, STATUS1, &status); + if (ret) + return ret; spi_prettyprint_status_register_sst25_common(status); msg_cdbg("Resulting block protection : %s\n", bpt[(status & 0x1c) >> 2]); return 0; } + +printlockfunc_t *lookup_printlock_func_ptr(struct flashctx *flash) +{ + switch (flash->chip->printlock) { + case PRINTLOCK_AT49F: return &printlock_at49f; + case PRINTLOCK_REGSPACE2_BLOCK_ERASER_0: return &printlock_regspace2_block_eraser_0; + case PRINTLOCK_REGSPACE2_BLOCK_ERASER_1: return &printlock_regspace2_block_eraser_1; + case PRINTLOCK_SST_FWHUB: return &printlock_sst_fwhub; + case PRINTLOCK_W39F010: return &printlock_w39f010; + case PRINTLOCK_W39L010: return &printlock_w39l010; + case PRINTLOCK_W39L020: return &printlock_w39l020; + case PRINTLOCK_W39L040: return &printlock_w39l040; + case PRINTLOCK_W39V040A: return &printlock_w39v040a; + case PRINTLOCK_W39V040B: return &printlock_w39v040b; + case PRINTLOCK_W39V040C: return &printlock_w39v040c; + case PRINTLOCK_W39V040FA: return &printlock_w39v040fa; + case PRINTLOCK_W39V040FB: return &printlock_w39v040fb; + case PRINTLOCK_W39V040FC: return &printlock_w39v040fc; + case PRINTLOCK_W39V080A: return &printlock_w39v080a; + case PRINTLOCK_W39V080FA: return &printlock_w39v080fa; + case PRINTLOCK_W39V080FA_DUAL: return &printlock_w39v080fa_dual; + case SPI_PRETTYPRINT_STATUS_REGISTER_AMIC_A25L032: return &spi_prettyprint_status_register_amic_a25l032; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25DF: return &spi_prettyprint_status_register_at25df; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25DF_SEC: return &spi_prettyprint_status_register_at25df_sec; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25F: return &spi_prettyprint_status_register_at25f; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25F4096: return &spi_prettyprint_status_register_at25f4096; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25F512A: return &spi_prettyprint_status_register_at25f512a; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25F512B: return &spi_prettyprint_status_register_at25f512b; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25FS010: return &spi_prettyprint_status_register_at25fs010; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT25FS040: return &spi_prettyprint_status_register_at25fs040; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT26DF081A: return &spi_prettyprint_status_register_at26df081a; + case SPI_PRETTYPRINT_STATUS_REGISTER_AT45DB: return &spi_prettyprint_status_register_at45db; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP1_SRWD: return &spi_prettyprint_status_register_bp1_srwd; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP2_BPL: return &spi_prettyprint_status_register_bp2_bpl; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP2_EP_SRWD: return &spi_prettyprint_status_register_bp2_ep_srwd; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP2_SRWD: return &spi_prettyprint_status_register_bp2_srwd; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP2_TB_BPL: return &spi_prettyprint_status_register_bp2_tb_bpl; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP3_SRWD: return &spi_prettyprint_status_register_bp3_srwd; + case SPI_PRETTYPRINT_STATUS_REGISTER_BP4_SRWD: return &spi_prettyprint_status_register_bp4_srwd; + case SPI_PRETTYPRINT_STATUS_REGISTER_DEFAULT_WELWIP: return &spi_prettyprint_status_register_default_welwip; + case SPI_PRETTYPRINT_STATUS_REGISTER_EN25S_WP: return &spi_prettyprint_status_register_en25s_wp; + case SPI_PRETTYPRINT_STATUS_REGISTER_N25Q: return &spi_prettyprint_status_register_n25q; + case SPI_PRETTYPRINT_STATUS_REGISTER_PLAIN: return &spi_prettyprint_status_register_plain; + case SPI_PRETTYPRINT_STATUS_REGISTER_SST25: return &spi_prettyprint_status_register_sst25; + case SPI_PRETTYPRINT_STATUS_REGISTER_SST25VF016: return &spi_prettyprint_status_register_sst25vf016; + case SPI_PRETTYPRINT_STATUS_REGISTER_SST25VF040B: return &spi_prettyprint_status_register_sst25vf040b; + /* default: non-total function, 0 indicates no unlock function set. + * We explicitly do not want a default catch-all case in the switch + * to ensure unhandled enum's are compiler warnings. + */ + case NO_PRINTLOCK_FUNC: return NULL; + }; + + return NULL; +} |