summaryrefslogtreecommitdiffstats
path: root/spi25_statusreg.c
diff options
context:
space:
mode:
authorsibradzic <5964548+sibradzic@users.noreply.github.com>2020-03-25 19:40:46 +0900
committerEdward O'Callaghan <quasisec@chromium.org>2020-04-22 06:06:36 +0000
commita3519561bd0fb44153bb376322b799000657576f (patch)
tree6d2f3c9e5130718423592ab0afbbffa29f86bad6 /spi25_statusreg.c
parenta25c13cdb601f9d43b0f8edad96f9489efcb4b37 (diff)
downloadflashrom-a3519561bd0fb44153bb376322b799000657576f.tar.gz
flashrom-a3519561bd0fb44153bb376322b799000657576f.tar.bz2
flashrom-a3519561bd0fb44153bb376322b799000657576f.zip
flashchips: port S25FS(128S) chip from chromiumos
This may seem too big just to support yet another flash chip, but in reality it brings support for whole new family of S25FS Spansion/Cypress flash chips. These chips require handling of some special status registers for erasing or writing, with very specific timing checks in place. For example, WIP status bit will remain being set to 1 if erase or programming errors occur, and in that case chip 'software reset' has to be performed otherwise the chip will remain unresponsive to all further commands. Also, special CR3NV register (Configuration Register 3 Nonvolatile) status bits needs to be read and set by using RDAR (ReaD Any Register) and WRAR (WRite Any Register) OP commands, and these states are needed to determine which type of reset feature is enabled at the time (legacy or S25FS type) in the first place, determine whether Uniform or Hybrid sector architecture is used at the time, or set programming buffer address wrap point (256 or 512 bytes). Furthermore, S25FS chip status register has to be restored to its original state (hence that ugly CHIP_RESTORE_CALLBACK) following erasing or writing, failing to do so may result in host being unable to access data on the chip at all. Finally, although this brings support for the whole family of chips, I only have one such chip to do the actual testing, S25FS128S (Small Sectors), which I had fully tested on ch341a and FT4232H programmers, with confirmed working probe, read, erase and write. Full summary of changes are here: flashchips: add new flashchip sctructure property: .reset add chip definitions: S25FS128S Large Sectors S25FS128S Small Sectors flash: add macro (chip_restore_func_data call-back): CHIP_RESTORE_CALLBACK flashrom: add struct: chip_restore_func_data add call-back function: register_chip_restore spi: add OP codes: CMD_RDAR, CMD_WRAR, CMD_WRAR_LEN, CMD_RSTEN, CMD_RST add register bit function definitions: CR3NV_ADDR, CR3NV_20H_NV add timers: T_W, T_RPH, T_SE spi25: refactor (based on chromiumos implementation) function: spi_poll_wip port these functions from chromiumos: probe_spi_big_spansion s25fs_software_reset s25f_legacy_software_reset s25fs_block_erase_d8 spi25_statusreg: port these functions from chromiumos: spi_restore_status s25fs_read_cr s25fs_write_cr s25fs_restore_cr3nv Most of the ported functions are originally from s25f.c found at https://chromium.googlesource.com/chromiumos/third_party/flashrom with exception of spi_restore_status which is defined in spi25_statusreg.c. The rest of macros and OP codes are defined in same files as in this commit. Change-Id: If659290874a4b9db6e71256bdef382d31b288e72 Signed-off-by: Samir Ibradzic <sibradzic@gmail.com> Reviewed-on: https://review.coreboot.org/c/flashrom/+/39822 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Edward O'Callaghan <quasisec@chromium.org>
Diffstat (limited to 'spi25_statusreg.c')
-rw-r--r--spi25_statusreg.c86
1 files changed, 86 insertions, 0 deletions
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index 8cd5a286c..aa574d5a8 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -108,6 +108,89 @@ uint8_t spi_read_status_register(struct flashctx *flash)
return readarr[0];
}
+static int spi_restore_status(struct flashctx *flash, uint8_t status)
+{
+ msg_cdbg("restoring chip status (0x%02x)\n", status);
+ return spi_write_status_register(flash, status);
+}
+
+/* 'Read Any Register' used on Spansion/Cypress S25FS chips */
+int s25fs_read_cr(struct flashctx *const flash, uint32_t addr)
+{
+ int result;
+ uint8_t cfg;
+ /* By default, 8 dummy cycles are necessary for variable-latency
+ commands such as RDAR (see CR2NV[3:0]). */
+ unsigned char read_cr_cmd[] = {
+ CMD_RDAR,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ };
+
+ result = spi_send_command(flash, sizeof(read_cr_cmd), 1, read_cr_cmd, &cfg);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return -1;
+ }
+
+ return cfg;
+}
+
+/* 'Write Any Register' used on Spansion/Cypress S25FS chips */
+int s25fs_write_cr(struct flashctx *const flash,
+ uint32_t addr, uint8_t data)
+{
+ int result;
+ struct spi_command cmds[] = {
+ {
+ .writecnt = JEDEC_WREN_OUTSIZE,
+ .writearr = (const unsigned char[]){ JEDEC_WREN },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = CMD_WRAR_LEN,
+ .writearr = (const unsigned char[]){
+ CMD_WRAR,
+ (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff,
+ (addr & 0xff),
+ data
+ },
+ .readcnt = 0,
+ .readarr = NULL,
+ }, {
+ .writecnt = 0,
+ .writearr = NULL,
+ .readcnt = 0,
+ .readarr = NULL,
+ }};
+
+ result = spi_send_multicommand(flash, cmds);
+ if (result) {
+ msg_cerr("%s failed during command execution at address 0x%x\n",
+ __func__, addr);
+ return -1;
+ }
+
+ programmer_delay(T_W);
+ return spi_poll_wip(flash, 1000 * 10);
+}
+
+/* Used on Spansion/Cypress S25FS chips */
+int s25fs_restore_cr3nv(struct flashctx *const flash, uint8_t cfg)
+{
+ int ret = 0;
+
+ msg_cdbg("Restoring CR3NV value to 0x%02x\n", cfg);
+ ret |= s25fs_write_cr(flash, CR3NV_ADDR, cfg);
+ ret |= s25fs_software_reset(flash);
+ return ret;
+}
+
/* A generic block protection disable.
* Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise.
* Tests if the register bits are locked with the lock_mask (lock_mask).
@@ -139,6 +222,9 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m
return 0;
}
+ /* restore status register content upon exit */
+ register_chip_restore(spi_restore_status, flash, status);
+
msg_cdbg("Some block protection in effect, disabling... ");
if ((status & lock_mask) != 0) {
msg_cdbg("\n\tNeed to disable the register lock first... ");