diff --git a/flash.h b/flash.h index b18d698d8..5cb7040e9 100644 --- a/flash.h +++ b/flash.h @@ -120,6 +120,8 @@ enum write_granularity { #define FEATURE_OTP (1 << 8) #define FEATURE_QPI (1 << 9) #define FEATURE_4BA_SUPPORT (1 << 10) +#define FEATURE_4BA_EXT_ADDR (1 << 11) /**< Regular 3-byte operations can be used by writing the most + significant address byte into an extended address register. */ enum test_state { OK = 0, @@ -236,6 +238,12 @@ struct flashrom_flashctx { bool verify_after_write; bool verify_whole_chip; } flags; + /* We cache the state of the extended address register (highest byte + of a 4BA for 3BA instructions) and the state of the 4BA mode here. + If possible, we enter 4BA mode early. If that fails, we make use + of the extended address register. */ + int address_high_byte; + bool in_4ba_mode; }; /* Timing used in probe routines. ZERO is -2 to differentiate between an unset diff --git a/flashrom.c b/flashrom.c index 12d739014..8849f6396 100644 --- a/flashrom.c +++ b/flashrom.c @@ -2215,6 +2215,9 @@ int prepare_flash_access(struct flashctx *const flash, if (flash->chip->unlock) flash->chip->unlock(flash); + flash->address_high_byte = -1; + flash->in_4ba_mode = false; + /* Enable/disable 4-byte addressing mode if flash chip supports it */ if ((flash->chip->feature_bits & FEATURE_4BA_SUPPORT) && flash->chip->four_bytes_addr_funcs.set_4ba) { diff --git a/spi25.c b/spi25.c index 84de75e2f..6940394c7 100644 --- a/spi25.c +++ b/spi25.c @@ -363,14 +363,37 @@ static int spi_simple_write_cmd(struct flashctx *const flash, const uint8_t op, return result ? result : status; } +static int spi_set_extended_address(struct flashctx *const flash, const uint8_t addr_high) +{ + if (flash->address_high_byte != addr_high && + spi_write_extended_address_register(flash, addr_high)) + return -1; + flash->address_high_byte = addr_high; + return 0; +} + static int spi_prepare_address(struct flashctx *const flash, uint8_t cmd_buf[], const unsigned int addr) { - /* TODO: extend for 4BA */ - cmd_buf[1] = (addr >> 16) & 0xff; - cmd_buf[2] = (addr >> 8) & 0xff; - cmd_buf[3] = (addr >> 0) & 0xff; - return 3; + if (flash->in_4ba_mode) { + cmd_buf[1] = (addr >> 24) & 0xff; + cmd_buf[2] = (addr >> 16) & 0xff; + cmd_buf[3] = (addr >> 8) & 0xff; + cmd_buf[4] = (addr >> 0) & 0xff; + return 4; + } else { + if (flash->chip->feature_bits & FEATURE_4BA_EXT_ADDR) { + if (spi_set_extended_address(flash, addr >> 24)) + return -1; + } else { + if (addr >> 24) + return -1; + } + cmd_buf[1] = (addr >> 16) & 0xff; + cmd_buf[2] = (addr >> 8) & 0xff; + cmd_buf[3] = (addr >> 0) & 0xff; + return 3; + } } /** diff --git a/spi4ba.c b/spi4ba.c index a44e0674d..902f07309 100644 --- a/spi4ba.c +++ b/spi4ba.c @@ -39,12 +39,17 @@ /* Enter 4-bytes addressing mode (without sending WREN before) */ int spi_enter_4ba_b7(struct flashctx *flash) { + int result; const unsigned char cmd[JEDEC_ENTER_4_BYTE_ADDR_MODE_OUTSIZE] = { JEDEC_ENTER_4_BYTE_ADDR_MODE }; msg_trace("-> %s\n", __func__); /* Switch to 4-bytes addressing mode */ - return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + result = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (!result) + flash->in_4ba_mode = true; + + return result; } /* Enter 4-bytes addressing mode with sending WREN before */ @@ -75,18 +80,25 @@ int spi_enter_4ba_b7_we(struct flashctx *flash) result = spi_send_multicommand(flash, cmds); if (result) msg_cerr("%s failed during command execution\n", __func__); + else + flash->in_4ba_mode = true; return result; } /* Exit 4-bytes addressing mode (without sending WREN before) */ int spi_exit_4ba_e9(struct flashctx *flash) { + int result; const unsigned char cmd[JEDEC_EXIT_4_BYTE_ADDR_MODE_OUTSIZE] = { JEDEC_EXIT_4_BYTE_ADDR_MODE }; msg_trace("-> %s\n", __func__); /* Switch to 3-bytes addressing mode */ - return spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + result = spi_send_command(flash, sizeof(cmd), 0, cmd, NULL); + if (!result) + flash->in_4ba_mode = false; + + return result; } /* Exit 4-bytes addressing mode with sending WREN before */ @@ -115,9 +127,10 @@ int spi_exit_4ba_e9_we(struct flashctx *flash) /* Switch to 3-bytes addressing mode */ result = spi_send_multicommand(flash, cmds); - if (result) { + if (result) msg_cerr("%s failed during command execution\n", __func__); - } + else + flash->in_4ba_mode = false; return result; } diff --git a/spi4ba.h b/spi4ba.h index 8a0179243..a0316bc7c 100644 --- a/spi4ba.h +++ b/spi4ba.h @@ -114,5 +114,6 @@ int spi_block_erase_21_4ba_direct(struct flashctx *flash, unsigned int addr, uns int spi_block_erase_5c_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_dc_4ba_direct(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_write_extended_address_register(struct flashctx *flash, uint8_t regdata); #endif /* __SPI_4BA_H__ */